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

// MARK: - Part

/// A named musical part (e.g., "Alto", "Tenor") with its notes organized into measures.
struct Part: Equatable {
    let name: String            // "Soprano", "Alto", "Tenor", "Bass", etc.
    let notes: [Note]           // All notes in order (flat list, before measure splitting)
    let measures: [Measure]     // Notes split into measures by time signature
    let colorIndex: Int         // Index into the part color palette

    /// Whether this part has recorded audio associated with it
    var hasRecordedAudio: Bool = false

    /// Create a Part from a flat list of notes, splitting into measures based on time signature
    static func create(
        name: String,
        notes: [Note],
        timeSignature: TimeSignature,
        colorIndex: Int
    ) -> Part {
        let measures = Part.splitIntoMeasures(notes: notes, timeSignature: timeSignature)
        return Part(name: name, notes: notes, measures: measures, colorIndex: colorIndex)
    }

    /// Split a flat list of notes into measures based on the time signature
    private static func splitIntoMeasures(
        notes: [Note],
        timeSignature: TimeSignature
    ) -> [Measure] {
        let beatsPerMeasure = timeSignature.quarterNoteBeatsPerMeasure
        var measures: [Measure] = []
        var currentNotes: [Note] = []
        var currentBeats: Double = 0.0
        var measureIndex = 0

        for note in notes {
            currentNotes.append(note)
            currentBeats += note.beats

            // Check if we've filled a measure (with small tolerance for floating point)
            if currentBeats >= beatsPerMeasure - 0.001 {
                measures.append(Measure(index: measureIndex, notes: currentNotes))
                currentNotes = []
                currentBeats = 0.0
                measureIndex += 1
            }
        }

        // If there are leftover notes (incomplete final measure), add them
        if !currentNotes.isEmpty {
            measures.append(Measure(index: measureIndex, notes: currentNotes))
        }

        return measures
    }

    static func == (lhs: Part, rhs: Part) -> Bool {
        return lhs.name == rhs.name && lhs.notes == rhs.notes && lhs.colorIndex == rhs.colorIndex
    }
}

// MARK: - Part Colors

/// Color palette for parts. Each part gets a distinct color for rendering.
struct PartColors {
    #if canImport(AppKit)
    static let palette: [NSColor] = [
        NSColor.systemBlue,
        NSColor.systemRed,
        NSColor.systemGreen,
        NSColor.systemPurple,
        NSColor.systemOrange,
        NSColor.systemTeal,
        NSColor.systemPink,
        NSColor.systemBrown,
    ]

    static func color(forIndex index: Int) -> NSColor {
        return palette[index % palette.count]
    }
    #elseif canImport(UIKit)
    static let palette: [UIColor] = [
        UIColor.systemBlue,
        UIColor.systemRed,
        UIColor.systemGreen,
        UIColor.systemPurple,
        UIColor.systemOrange,
        UIColor.systemTeal,
        UIColor.systemPink,
        UIColor.brown,
    ]

    static func color(forIndex index: Int) -> UIColor {
        return palette[index % palette.count]
    }
    #endif

    /// Color as RGB tuple for cross-platform use
    static let paletteRGB: [(r: Double, g: Double, b: Double)] = [
        (0.0, 0.478, 1.0),    // blue
        (1.0, 0.231, 0.188),  // red
        (0.204, 0.780, 0.349),// green
        (0.686, 0.322, 0.871),// purple
        (1.0, 0.584, 0.0),    // orange
        (0.353, 0.784, 0.980),// teal
        (1.0, 0.176, 0.333),  // pink
        (0.635, 0.518, 0.369),// brown
    ]
}

extension Part: CustomStringConvertible {
    var description: String {
        return "\(name): \(measures.count) measures, \(notes.count) notes"
    }
}
