"""Two-pan balance scale SVG generator for AGS Math instruments.

generate(left_value, right_value) -> str  (self-contained SVG)

Tilt formula:
  The beam tilts toward the heavier side. Tilt angle (deg) is proportional
  to the signed difference (left - right):

      tilt = clamp(K * (left - right), -MAX, +MAX)

  with K = 3.0 deg per unit and MAX = 15 deg. Positive tilt lowers the LEFT
  pan (heavier-left dips down). When left == right the beam is level (0 deg).

  Sign convention for SVG (y grows downward): the beam is rotated about the
  central fulcrum by `tilt` degrees. The left end goes DOWN when left heavier.
  In screen rotation terms, rotating by +tilt (clockwise) raises the left and
  lowers the right, so we apply a rotation of -tilt to dip the heavier side.

The pans hang from the beam ends and move up/down with the tilt, but the pan
faces stay horizontal (they are not rotated, only translated to the rotated
beam-end positions).
"""
import math

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

K_DEG_PER_UNIT = 3.0
MAX_TILT = 15.0


def _clamp(v, lo, hi):
    return max(lo, min(hi, v))


def generate(left_value=5, right_value=5, width=360, height=320):
    diff = float(left_value) - float(right_value)
    tilt = _clamp(K_DEG_PER_UNIT * diff, -MAX_TILT, MAX_TILT)

    cx = width / 2          # fulcrum x
    fy = 90                 # beam pivot y
    arm = 120               # half-beam length
    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"/>'
    )

    # Stand (ink): vertical column + base
    base_y = height - 30
    parts.append(
        f'<rect x="{cx-6}" y="{fy}" width="12" height="{base_y-fy}" '
        f'fill="var(--ink)"/>'
    )
    parts.append(
        f'<rect x="{cx-50}" y="{base_y}" width="100" height="14" rx="6" '
        f'fill="var(--ink)"/>'
    )

    # Beam end positions after rotating about (cx, fy) by `tilt` deg.
    # Positive tilt should dip the LEFT (heavier-left). Left end at angle 180,
    # right end at 0. Rotating clockwise by +tilt lowers the right; so to lower
    # the left when diff>0 we rotate by -tilt.
    a = math.radians(-tilt)
    # left end (relative -arm,0), right end (relative +arm,0)
    lx = cx + (-arm) * math.cos(a)
    ly = fy + (-arm) * math.sin(a)
    rx = cx + (arm) * math.cos(a)
    ry = fy + (arm) * math.sin(a)

    # Beam (steel-blue)
    parts.append(
        f'<line x1="{lx:.2f}" y1="{ly:.2f}" x2="{rx:.2f}" y2="{ry:.2f}" '
        f'stroke="var(--steel)" stroke-width="8" stroke-linecap="round"/>'
    )

    # Fulcrum triangle (ink) at pivot
    parts.append(
        f'<polygon points="{cx-14},{fy+18} {cx+14},{fy+18} {cx},{fy-6}" '
        f'fill="var(--ink)"/>'
    )

    # Pans hang from each beam end. Hanger cords (steel) then horizontal gold pan.
    pan_drop = 70           # cord length
    pan_w = 80
    for (ex, ey) in ((lx, ly), (rx, ry)):
        py = ey + pan_drop   # pan face y (stays horizontal)
        # two cords forming a V to the pan rim
        parts.append(
            f'<line x1="{ex:.2f}" y1="{ey:.2f}" x2="{ex-pan_w/2:.2f}" y2="{py:.2f}" '
            f'stroke="var(--steel)" stroke-width="2"/>'
        )
        parts.append(
            f'<line x1="{ex:.2f}" y1="{ey:.2f}" x2="{ex+pan_w/2:.2f}" y2="{py:.2f}" '
            f'stroke="var(--steel)" stroke-width="2"/>'
        )
        # gold pan: shallow bowl (horizontal)
        parts.append(
            f'<path d="M {ex-pan_w/2:.2f} {py:.2f} '
            f'Q {ex:.2f} {py+22:.2f} {ex+pan_w/2:.2f} {py:.2f} Z" '
            f'fill="var(--gold)" stroke="var(--ink)" stroke-width="1.5"/>'
        )
        parts.append(
            f'<ellipse cx="{ex:.2f}" cy="{py:.2f}" rx="{pan_w/2:.2f}" ry="5" '
            f'fill="var(--gold)" stroke="var(--ink)" stroke-width="1.5"/>'
        )

    # Value labels on each pan
    parts.append(
        f'<text x="{lx:.2f}" y="{ly+pan_drop+14:.2f}" font-family="sans-serif" '
        f'font-size="20" font-weight="bold" fill="var(--ink)" '
        f'text-anchor="middle">{left_value}</text>'
    )
    parts.append(
        f'<text x="{rx:.2f}" y="{ry+pan_drop+14:.2f}" font-family="sans-serif" '
        f'font-size="20" font-weight="bold" fill="var(--ink)" '
        f'text-anchor="middle">{right_value}</text>'
    )

    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 = [(5, 5), (8, 3), (2, 9)]
    for i, (l, r) in enumerate(samples, 1):
        p = os.path.join(out, f'balance_scale_{i}.svg')
        with open(p, 'w', encoding='utf-8') as f:
            f.write(generate(l, r))
        print('wrote', p)
