import Foundation

// MARK: - Duration

/// Represents a musical note duration.
/// baseValue uses standard notation: 1 = whole, 2 = half, 4 = quarter, 8 = eighth, 16 = sixteenth
/// A dotted note is 1.5x its base duration.
struct Duration: Equatable, Hashable {
    let baseValue: Int   // 1, 2, 4, 8, 16
    let isDotted: Bool

    /// Duration in beats, where 1 beat = 1 quarter note.
    /// whole = 4 beats, half = 2 beats, quarter = 1 beat, eighth = 0.5, sixteenth = 0.25
    var beats: Double {
        let base: Double
        switch baseValue {
        case 1:  base = 4.0    // whole
        case 2:  base = 2.0    // half
        case 4:  base = 1.0    // quarter
        case 8:  base = 0.5    // eighth
        case 16: base = 0.25   // sixteenth
        default: base = 1.0    // fallback to quarter
        }
        return isDotted ? base * 1.5 : base
    }

    /// Duration in seconds at a given tempo (BPM = quarter notes per minute)
    func seconds(atBPM bpm: Double) -> Double {
        return beats * (60.0 / bpm)
    }

    /// Whether this note gets a filled (black) notehead
    var isFilledNotehead: Bool {
        return baseValue >= 4  // quarter notes and shorter are filled
    }

    /// Whether this note has a stem
    var hasStem: Bool {
        return baseValue >= 2  // half notes and shorter have stems
    }

    /// Number of flags (for unbeamed notes)
    /// eighth = 1 flag, sixteenth = 2 flags
    var flagCount: Int {
        switch baseValue {
        case 8:  return 1
        case 16: return 2
        default: return 0
        }
    }

    /// Number of beams when beamed with adjacent notes
    var beamCount: Int {
        return flagCount
    }

    /// Display name for the duration
    var displayName: String {
        let base: String
        switch baseValue {
        case 1:  base = "whole"
        case 2:  base = "half"
        case 4:  base = "quarter"
        case 8:  base = "eighth"
        case 16: base = "sixteenth"
        default: base = "unknown"
        }
        return isDotted ? "dotted \(base)" : base
    }

    /// Parse from file format: "4" = quarter, "8." = dotted eighth
    static func parse(_ str: String) -> Duration? {
        let dotted = str.hasSuffix(".")
        let numStr = dotted ? String(str.dropLast()) : str
        guard let value = Int(numStr) else { return nil }
        guard [1, 2, 4, 8, 16].contains(value) else { return nil }
        return Duration(baseValue: value, isDotted: dotted)
    }
}

extension Duration: CustomStringConvertible {
    var description: String {
        return "\(baseValue)\(isDotted ? "." : "")"
    }
}

extension Duration: Comparable {
    static func < (lhs: Duration, rhs: Duration) -> Bool {
        return lhs.beats < rhs.beats
    }
}
