"""Semicircular protractor SVG generator for AGS Math instruments.

generate(angle, size) -> str  (self-contained SVG)

  - Cream semicircle face with an ink outline.
  - Steel-blue degree ticks around the arc; minors every 10 deg,
    longer majors with numeric labels every 30 deg.
  - Gold horizontal baseline through the centre.
  - Crimson ray from the centre at the given `angle` (0-180, measured
    anticlockwise from the right-hand baseline, standard protractor).
  - D0ACAC rounded border frame; CSS-var style block.
"""
import math

STYLE = (
    "<style>svg{--crimson:#960000;--gold:#BF9237;--lime:#88C808;"
    "--steel:#4A6488;--cream:#FAF5EE;--ink:#2A2A2A;--border:#D0ACAC;}</style>"
)


def _pt(cx, cy, r, deg):
    """Point at `deg` measured anticlockwise from the +x (right) axis."""
    a = math.radians(deg)
    return cx + r * math.cos(a), cy - r * math.sin(a)


def _face(size):
    """Build the cream protractor face (frame, semicircle, ticks, baseline).

    Returns (parts, cx, cy, R, width, height) so callers can append rays
    before closing the </svg>.
    """
    width = size
    height = int(size * 0.62)
    cx = width / 2
    cy = height - 30          # centre sits on the baseline near the bottom
    R = min(width / 2 - 24, cy - 24)   # face radius

    parts = []
    parts.append(
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" '
        f'viewBox="0 0 {width} {height}">'
    )
    parts.append(STYLE)

    # Border frame
    parts.append(
        f'<rect x="3" y="3" width="{width-6}" height="{height-6}" rx="18" ry="18" '
        f'fill="none" stroke="var(--border)" stroke-width="4"/>'
    )

    # Cream semicircle face (flat side down)
    lx, ly = _pt(cx, cy, R, 0)      # right end of baseline
    rx, ry = _pt(cx, cy, R, 180)    # left end of baseline
    parts.append(
        f'<path d="M {rx:.2f} {ry:.2f} A {R:.2f} {R:.2f} 0 0 1 {lx:.2f} {ly:.2f} Z" '
        f'fill="var(--cream)" stroke="var(--ink)" stroke-width="3" '
        f'stroke-linejoin="round"/>'
    )

    # Steel-blue degree ticks (every 10 deg minors, every 30 deg majors+labels)
    for deg in range(0, 181, 10):
        is_major = (deg % 30 == 0)
        tlen = 16 if is_major else 9
        x1, y1 = _pt(cx, cy, R, deg)
        x2, y2 = _pt(cx, cy, R - tlen, deg)
        parts.append(
            f'<line x1="{x1:.2f}" y1="{y1:.2f}" x2="{x2:.2f}" y2="{y2:.2f}" '
            f'stroke="var(--steel)" stroke-width="{2.2 if is_major else 1.2}"/>'
        )
        if is_major:
            lxp, lyp = _pt(cx, cy, R - 30, deg)
            parts.append(
                f'<text x="{lxp:.2f}" y="{lyp:.2f}" font-family="sans-serif" '
                f'font-size="12" fill="var(--steel)" text-anchor="middle" '
                f'dominant-baseline="central">{deg}</text>'
            )

    # Gold horizontal baseline
    parts.append(
        f'<line x1="{rx:.2f}" y1="{cy:.2f}" x2="{lx:.2f}" y2="{cy:.2f}" '
        f'stroke="var(--gold)" stroke-width="3.5" stroke-linecap="round"/>'
    )

    return parts, cx, cy, R, width, height


def _ray(parts, cx, cy, R, angle, label=None):
    """Append a crimson ray from centre at `angle`, with optional rim label."""
    ex, ey = _pt(cx, cy, R - 4, angle)
    parts.append(
        f'<line x1="{cx:.2f}" y1="{cy:.2f}" x2="{ex:.2f}" y2="{ey:.2f}" '
        f'stroke="var(--crimson)" stroke-width="3.5" stroke-linecap="round"/>'
    )
    if label is not None and label != "":
        # Place the label just beyond the rim along the ray direction.
        lx, ly = _pt(cx, cy, R + 12, angle)
        parts.append(
            f'<text x="{lx:.2f}" y="{ly:.2f}" font-family="sans-serif" '
            f'font-size="14" font-weight="bold" fill="var(--crimson)" '
            f'text-anchor="middle" dominant-baseline="central">{label}</text>'
        )


def generate(angle=45, size=340):
    angle = max(0.0, min(180.0, float(angle)))

    parts, cx, cy, R, width, height = _face(size)

    # Crimson ray from centre at the given angle
    _ray(parts, cx, cy, R, angle)
    # Centre pivot dot
    parts.append(f'<circle cx="{cx:.2f}" cy="{cy:.2f}" r="5" fill="var(--crimson)"/>')

    parts.append('</svg>')
    return '\n'.join(parts)


def generate_rays(rays=None, size=340):
    """Draw the protractor once with multiple labelled crimson rays.

    rays : list of dicts {"angle": float (0-180), "label": str}. Up to 6.
    Backward-compatible companion to generate(angle=..).
    """
    rays = rays or []
    rays = rays[:6]

    parts, cx, cy, R, width, height = _face(size)

    for entry in rays:
        ang = max(0.0, min(180.0, float(entry.get("angle", 0))))
        _ray(parts, cx, cy, R, ang, entry.get("label"))

    # Centre pivot dot on top of the rays
    parts.append(f'<circle cx="{cx:.2f}" cy="{cy:.2f}" r="5" fill="var(--crimson)"/>')

    parts.append('</svg>')
    return '\n'.join(parts)


if __name__ == '__main__':
    import os
    out = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'samples'))
    os.makedirs(out, exist_ok=True)
    samples = [45, 90, 135]
    for i, ang in enumerate(samples, 1):
        p = os.path.join(out, f'protractor_{i}.svg')
        with open(p, 'w', encoding='utf-8') as f:
            f.write(generate(angle=ang))
        print('wrote', p)

    rays = [
        {"angle": 30, "label": "A"},
        {"angle": 75, "label": "B"},
        {"angle": 110, "label": "C"},
        {"angle": 145, "label": "D"},
        {"angle": 160, "label": "E"},
    ]
    p = os.path.join(out, 'protractor_rays_1.svg')
    with open(p, 'w', encoding='utf-8') as f:
        f.write(generate_rays(rays=rays))
    print('wrote', p)
