import Foundation
import UIKit

// MARK: - Note Renderer (iOS)

/// Renders individual notes on the staff: noteheads, stems, flags, dots, accidentals, rests.
/// UIKit port — identical drawing logic to macOS, with UIColor/UIFont instead of NSColor/NSFont.
struct NoteRenderer_iOS {
    let L = AppConstants.Layout.self

    // MARK: - Staff position calculation

    func noteY(for pitch: Pitch, staffTopY: CGFloat) -> CGFloat {
        let topLinePosition = 10  // F5 staff position from middle C
        let notePosition = pitch.staffPositionFromMiddleC
        let halfSpacing = L.staffLineSpacing / 2.0
        return staffTopY + CGFloat(topLinePosition - notePosition) * halfSpacing
    }

    // MARK: - Note drawing

    func drawNote(
        _ note: Note,
        at x: CGFloat,
        staffTopY: CGFloat,
        color: UIColor,
        in context: CGContext
    ) {
        if note.isRest {
            drawRest(note.duration, at: x, staffTopY: staffTopY, color: color, in: context)
            return
        }

        guard let pitch = note.pitch else { return }

        let y = noteY(for: pitch, staffTopY: staffTopY)

        drawAccidental(pitch.accidental, at: x - L.accidentalOffset, y: y, color: color, in: context)
        drawNotehead(filled: note.duration.isFilledNotehead, at: x, y: y, color: color, in: context)

        if note.duration.hasStem {
            let stemUp = pitch.staffPositionFromMiddleC < 7
            drawStem(at: x, y: y, stemUp: stemUp, color: color, in: context)

            if note.duration.flagCount > 0 {
                drawFlags(count: note.duration.flagCount, at: x, y: y, stemUp: stemUp, color: color, in: context)
            }
        }

        if note.duration.isDotted {
            drawDot(at: x + L.noteHeadWidth + L.dotOffset, y: y, color: color, in: context)
        }
    }

    // MARK: - Component drawing

    func drawNotehead(filled: Bool, at x: CGFloat, y: CGFloat, color: UIColor, in context: CGContext) {
        let rect = CGRect(
            x: x,
            y: y - L.noteHeadHeight / 2,
            width: L.noteHeadWidth,
            height: L.noteHeadHeight
        )

        context.saveGState()
        context.translateBy(x: rect.midX, y: rect.midY)
        context.rotate(by: -0.2)
        let centeredRect = CGRect(
            x: -L.noteHeadWidth / 2,
            y: -L.noteHeadHeight / 2,
            width: L.noteHeadWidth,
            height: L.noteHeadHeight
        )

        if filled {
            context.setFillColor(color.cgColor)
            context.fillEllipse(in: centeredRect)
        } else {
            context.setStrokeColor(color.cgColor)
            context.setLineWidth(1.5)
            context.strokeEllipse(in: centeredRect)
        }

        context.restoreGState()
    }

    func drawStem(at x: CGFloat, y: CGFloat, stemUp: Bool, color: UIColor, in context: CGContext) {
        context.setStrokeColor(color.cgColor)
        context.setLineWidth(L.stemWidth)

        let stemX = stemUp ? x + L.noteHeadWidth - 1 : x + 1
        let stemEndY = stemUp ? y - L.stemLength : y + L.stemLength

        context.move(to: CGPoint(x: stemX, y: y))
        context.addLine(to: CGPoint(x: stemX, y: stemEndY))
        context.strokePath()
    }

    func drawFlags(count: Int, at x: CGFloat, y: CGFloat, stemUp: Bool, color: UIColor, in context: CGContext) {
        context.setStrokeColor(color.cgColor)
        context.setLineWidth(1.5)

        let stemX = stemUp ? x + L.noteHeadWidth - 1 : x + 1
        let stemEndY = stemUp ? y - L.stemLength : y + L.stemLength

        for i in 0..<count {
            let flagY = stemUp ? stemEndY + CGFloat(i) * 8 : stemEndY - CGFloat(i) * 8
            let flagEndX = stemX + (stemUp ? 10 : -10)
            let flagEndY = flagY + (stemUp ? 12 : -12)

            context.move(to: CGPoint(x: stemX, y: flagY))
            context.addQuadCurve(
                to: CGPoint(x: flagEndX, y: flagEndY),
                control: CGPoint(x: stemX + (stemUp ? 12 : -12), y: flagY + (stemUp ? 4 : -4))
            )
            context.strokePath()
        }
    }

    func drawDot(at x: CGFloat, y: CGFloat, color: UIColor, in context: CGContext) {
        context.setFillColor(color.cgColor)
        context.fillEllipse(in: CGRect(x: x, y: y - 1.5, width: 3, height: 3))
    }

    func drawAccidental(_ accidental: Accidental, at x: CGFloat, y: CGFloat, color: UIColor, in context: CGContext) {
        guard accidental != .natural else { return }

        let font = UIFont.systemFont(ofSize: 14)
        let attrs: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: color
        ]

        let symbol: String
        switch accidental {
        case .sharp:       symbol = "\u{266F}"
        case .flat:        symbol = "\u{266D}"
        case .natural:     symbol = "\u{266E}"
        case .doubleSharp: symbol = "\u{1D12A}"
        case .doubleFlat:  symbol = "\u{1D12B}"
        }

        (symbol as NSString).draw(at: CGPoint(x: x, y: y - 8), withAttributes: attrs)
    }

    // MARK: - Rest drawing

    func drawRest(_ duration: Duration, at x: CGFloat, staffTopY: CGFloat, color: UIColor, in context: CGContext) {
        let font = UIFont.systemFont(ofSize: 20)
        let attrs: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: color
        ]

        let centerY = staffTopY + L.staffHeight / 2

        let symbol: String
        switch duration.baseValue {
        case 1:  symbol = "\u{1D13B}"  // whole rest
        case 2:  symbol = "\u{1D13C}"  // half rest
        case 4:  symbol = "\u{1D13D}"  // quarter rest
        case 8:  symbol = "\u{1D13E}"  // eighth rest
        case 16: symbol = "\u{1D13F}"  // sixteenth rest
        default: symbol = "\u{1D13D}"
        }

        (symbol as NSString).draw(at: CGPoint(x: x, y: centerY - 12), withAttributes: attrs)
    }
}
