import Foundation

// MARK: - Measure

/// A single measure containing an ordered sequence of notes.
/// Measures are computed from a part's note list by splitting at measure boundaries
/// based on the time signature.
struct Measure: Equatable {
    let index: Int          // 0-based measure number within the song
    let notes: [Note]       // Notes in this measure, in order

    /// Total duration in beats (quarter-note beats)
    var totalBeats: Double {
        return notes.reduce(0.0) { $0 + $1.beats }
    }

    /// Whether this measure contains only rests
    var isAllRests: Bool {
        return notes.allSatisfy { $0.isRest }
    }

    /// Whether this measure is empty (no notes at all)
    var isEmpty: Bool {
        return notes.isEmpty
    }

    /// The highest pitched note in this measure (for stem direction decisions)
    var highestPitch: Pitch? {
        return notes.compactMap { $0.pitch }.max { $0.midiNumber < $1.midiNumber }
    }

    /// The lowest pitched note in this measure
    var lowestPitch: Pitch? {
        return notes.compactMap { $0.pitch }.min { $0.midiNumber < $1.midiNumber }
    }

    /// Duration of this measure in seconds at a given BPM
    func seconds(atBPM bpm: Double) -> Double {
        return totalBeats * (60.0 / bpm)
    }
}

extension Measure: CustomStringConvertible {
    var description: String {
        let noteStr = notes.map { $0.description }.joined(separator: " ")
        return "[\(index): \(noteStr)]"
    }
}
