import Foundation
#if canImport(AppKit)
import AppKit
#elseif canImport(UIKit)
import UIKit
#endif

// MARK: - Layout Metrics

/// Computed layout information for the sheet music view.
/// Determines where each measure, staff system, and note should be positioned.

/// Represents the screen position of a single measure in the layout
struct MeasureFrame {
    let measureIndex: Int
    let systemIndex: Int     // Which staff system (line) this measure is on
    let rect: CGRect         // The frame rectangle in view coordinates
    let staffY: CGFloat      // Y position of the top staff line

    /// Center point of the measure
    var center: CGPoint {
        return CGPoint(x: rect.midX, y: rect.midY)
    }
}

/// Represents a system (one horizontal line of staves across the page)
struct StaffSystem {
    let systemIndex: Int
    let measures: [MeasureFrame]
    let rect: CGRect          // Bounding rect for the entire system
    let staffTopY: CGFloat    // Y of the top staff line
    let numberOfParts: Int    // How many parts are stacked

    /// Y position for a specific part's staff within this system
    func staffY(forPartIndex partIndex: Int) -> CGFloat {
        let partSpacing = AppConstants.Layout.staffHeight + 40  // space between part staves
        return staffTopY + CGFloat(partIndex) * partSpacing
    }
}

/// The complete layout for the entire sheet music
struct SheetMusicLayout {
    let systems: [StaffSystem]
    let measureFrames: [MeasureFrame]    // Flat list for quick lookup by measure index
    let totalSize: CGSize                // Total scrollable content size
    let measuresPerSystem: Int           // How many measures fit on each system

    /// Find the measure frame for a given measure index
    func frame(forMeasure index: Int) -> MeasureFrame? {
        return measureFrames.first { $0.measureIndex == index }
    }

    /// Hit test: which measure index is at a given point?
    func measureIndex(at point: CGPoint) -> Int? {
        for frame in measureFrames {
            if frame.rect.contains(point) {
                return frame.measureIndex
            }
        }
        return nil
    }
}

// MARK: - Layout Calculator

struct LayoutCalculator {

    /// Calculate the complete sheet music layout for a song within a given view width.
    static func calculateLayout(
        song: Song,
        viewWidth: CGFloat,
        showAllParts: Bool = true
    ) -> SheetMusicLayout {
        let L = AppConstants.Layout.self
        let totalMeasures = song.totalMeasures
        guard totalMeasures > 0 else {
            return SheetMusicLayout(systems: [], measureFrames: [], totalSize: .zero, measuresPerSystem: 0)
        }

        let numberOfParts = showAllParts ? song.parts.count : 1
        let partStaffHeight = L.staffHeight + 40  // staff + space between parts
        let systemContentHeight = CGFloat(numberOfParts) * partStaffHeight - 40 // subtract last gap

        // Available width for measures
        let availableWidth = viewWidth - L.leftMargin - L.rightMargin

        // Calculate how many measures fit per system
        let measuresPerSystem = max(1, Int(availableWidth / L.measureMinWidth))
        let measureWidth = availableWidth / CGFloat(measuresPerSystem)

        // Build systems
        var systems: [StaffSystem] = []
        var allFrames: [MeasureFrame] = []
        var systemIndex = 0
        var measureIndex = 0

        while measureIndex < totalMeasures {
            let measuresInThisSystem = min(measuresPerSystem, totalMeasures - measureIndex)
            let systemY = L.topMargin + CGFloat(systemIndex) * (systemContentHeight + L.systemSpacing)

            var systemMeasures: [MeasureFrame] = []
            for i in 0..<measuresInThisSystem {
                let x = L.leftMargin + CGFloat(i) * measureWidth
                let frame = MeasureFrame(
                    measureIndex: measureIndex + i,
                    systemIndex: systemIndex,
                    rect: CGRect(x: x, y: systemY, width: measureWidth, height: systemContentHeight),
                    staffY: systemY
                )
                systemMeasures.append(frame)
                allFrames.append(frame)
            }

            let systemRect = CGRect(
                x: 0,
                y: systemY,
                width: viewWidth,
                height: systemContentHeight
            )

            systems.append(StaffSystem(
                systemIndex: systemIndex,
                measures: systemMeasures,
                rect: systemRect,
                staffTopY: systemY,
                numberOfParts: numberOfParts
            ))

            measureIndex += measuresInThisSystem
            systemIndex += 1
        }

        // Total content height
        let totalHeight: CGFloat
        if let lastSystem = systems.last {
            totalHeight = lastSystem.rect.maxY + L.topMargin
        } else {
            totalHeight = L.topMargin * 2
        }

        return SheetMusicLayout(
            systems: systems,
            measureFrames: allFrames,
            totalSize: CGSize(width: viewWidth, height: totalHeight),
            measuresPerSystem: measuresPerSystem
        )
    }
}
