//
//  MoonShadowShape.swift
//  Shared between MoonWatch and MoonWatchWidget.
//

import SwiftUI

/// Geometric terminator: half-circle along the dark limb stitched to a
/// half-ellipse whose horizontal axis = cos(2π·t)·R. Renders correctly at
/// new (full dark), quarter (straight terminator), gibbous, and full
/// (no shadow). Slight `oversizeFactor` lets a parent `Circle` clip keep
/// the limb sharp while a small `.blur` softens the terminator.
public struct MoonShadowShape: Shape {
    public var phaseFraction: Double
    public var oversizeFactor: CGFloat

    public init(phaseFraction: Double, oversizeFactor: CGFloat = 1.04) {
        self.phaseFraction = phaseFraction
        self.oversizeFactor = oversizeFactor
    }

    public var animatableData: Double {
        get { phaseFraction }
        set { phaseFraction = newValue }
    }

    public func path(in rect: CGRect) -> Path {
        let r = min(rect.width, rect.height) / 2
        let outer = r * oversizeFactor
        let cx = rect.midX
        let cy = rect.midY
        let raw = phaseFraction.truncatingRemainder(dividingBy: 1)
        let u = raw < 0 ? raw + 1 : raw
        let cosT = CGFloat(cos(2 * Double.pi * u))
        let waning = u > 0.5
        let bulge: CGFloat = waning ? -cosT * outer : cosT * outer
        let kappa: CGFloat = 0.5522847498

        var path = Path()
        let top = CGPoint(x: cx, y: cy - outer)
        path.move(to: top)
        path.addArc(
            center: CGPoint(x: cx, y: cy),
            radius: outer,
            startAngle: .degrees(270),
            endAngle: .degrees(90),
            clockwise: !waning
        )
        path.addCurve(
            to: CGPoint(x: cx + bulge, y: cy),
            control1: CGPoint(x: cx + bulge * kappa, y: cy + outer),
            control2: CGPoint(x: cx + bulge,         y: cy + outer * kappa)
        )
        path.addCurve(
            to: top,
            control1: CGPoint(x: cx + bulge,         y: cy - outer * kappa),
            control2: CGPoint(x: cx + bulge * kappa, y: cy - outer)
        )
        path.closeSubpath()
        return path
    }
}
