"""
MENTORR Calculator — Pure Python calculation functions for ChatGPT Code Interpreter.

Usage in ChatGPT:
    exec(open('/mnt/data/mentorr_calculator.py').read())
    result = fret_layout(25.5, 22)
    print(result)
"""

import math


# ---------------------------------------------------------------------------
# Fret Layout Calculator
# ---------------------------------------------------------------------------

def fret_layout(scale_length, num_frets=22):
    """Generate a fret slotting table.

    Args:
        scale_length: Scale length in inches (e.g. 25.5, 24.75, 25.4).
        num_frets: Number of frets (default 22).

    Returns:
        Formatted string table with fret positions from nut.
    """
    lines = [
        f"Fret Layout for {scale_length}\" Scale ({scale_length * 25.4:.1f} mm):",
        "",
        f"{'Fret':>4} | {'From Nut (in)':>14} | {'From Nut (mm)':>14} | {'Fret Spacing (in)':>18}",
        f"{'----':>4}-+-{'-' * 14}-+-{'-' * 14}-+-{'-' * 18}",
    ]

    prev_dist = 0.0
    for i in range(1, num_frets + 1):
        dist = scale_length * (1 - (1 / (2 ** (i / 12.0))))
        spacing = dist - prev_dist
        lines.append(
            f"{i:4d} | {dist:14.4f} | {dist * 25.4:14.2f} | {spacing:18.4f}"
        )
        prev_dist = dist

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# String Tension Calculator
# ---------------------------------------------------------------------------

# Gauge sets: gauge in inches for each string (high E to low E)
GAUGE_SETS = {
    "extra_light": ["0.010", "0.014", "0.023", "0.030", "0.039", "0.047"],
    "light":       ["0.012", "0.016", "0.024", "0.032", "0.042", "0.053"],
    "medium":      ["0.013", "0.017", "0.026", "0.036", "0.046", "0.056"],
    "heavy":       ["0.014", "0.018", "0.027", "0.039", "0.049", "0.059"],
}

# Unit weight in lbs/in for common gauges (from D'Addario published data)
UNIT_WEIGHTS = {
    "0.008": 0.00001418, "0.009": 0.00001794, "0.010": 0.00002215,
    "0.011": 0.00002680, "0.012": 0.00003196, "0.013": 0.00003744,
    "0.014": 0.00004352, "0.015": 0.00004984, "0.016": 0.00005677,
    "0.017": 0.00006402, "0.018": 0.00007177, "0.019": 0.00007997,
    "0.020": 0.00008861, "0.021": 0.00009771, "0.022": 0.00010722,
    "0.023": 0.00011522, "0.024": 0.00012500, "0.025": 0.00013526,
    "0.026": 0.00014628, "0.027": 0.00015916, "0.028": 0.00017011,
    "0.030": 0.00019460, "0.032": 0.00022200, "0.034": 0.00025020,
    "0.036": 0.00028060, "0.039": 0.00032878, "0.042": 0.00037700,
    "0.046": 0.00045580, "0.047": 0.00047600, "0.049": 0.00051900,
    "0.053": 0.00059500, "0.054": 0.00062300, "0.056": 0.00067200,
    "0.059": 0.00074500,
}

# Tuning definitions: note names for strings 1-6 (high E to low E)
TUNINGS = {
    "standard":  ["E4", "B3", "G3", "D3", "A2", "E2"],
    "drop_d":    ["E4", "B3", "G3", "D3", "A2", "D2"],
    "open_g":    ["D4", "B3", "G3", "D3", "G2", "D2"],
    "open_d":    ["D4", "A3", "F#3", "D3", "A2", "D2"],
    "dadgad":    ["D4", "A3", "G3", "D3", "A2", "D2"],
    "eb":        ["Eb4", "Bb3", "Gb3", "Db3", "Ab2", "Eb2"],
    "half_step_down": ["Eb4", "Bb3", "Gb3", "Db3", "Ab2", "Eb2"],
}

# Frequency table (Hz) for all notes we might need
NOTE_FREQUENCIES = {
    "C2": 65.41, "Db2": 69.30, "D2": 73.42, "Eb2": 77.78,
    "E2": 82.41, "F2": 87.31, "F#2": 92.50, "Gb2": 92.50,
    "G2": 98.00, "Ab2": 103.83, "A2": 110.00, "Bb2": 116.54,
    "B2": 123.47,
    "C3": 130.81, "Db3": 138.59, "D3": 146.83, "Eb3": 155.56,
    "E3": 164.81, "F3": 174.61, "F#3": 185.00, "Gb3": 185.00,
    "G3": 196.00, "Ab3": 207.65, "A3": 220.00, "Bb3": 233.08,
    "B3": 246.94,
    "C4": 261.63, "Db4": 277.18, "D4": 293.66, "Eb4": 311.13,
    "E4": 329.63, "F4": 349.23, "F#4": 369.99, "G4": 392.00,
}


def string_tension(scale_length, set_type="light", tuning="standard"):
    """Calculate per-string and total tension.

    Args:
        scale_length: Scale length in inches.
        set_type: One of 'extra_light', 'light', 'medium', 'heavy'.
        tuning: One of 'standard', 'drop_d', 'open_g', 'open_d', 'dadgad', 'eb'.

    Returns:
        Formatted string with per-string tension in lbs and total.
    """
    gauges = GAUGE_SETS.get(set_type)
    if gauges is None:
        return f"Unknown set_type '{set_type}'. Options: {', '.join(GAUGE_SETS.keys())}"

    notes = TUNINGS.get(tuning)
    if notes is None:
        return f"Unknown tuning '{tuning}'. Options: {', '.join(TUNINGS.keys())}"

    lines = [
        f"String Tension: {scale_length}\" scale, {set_type} gauge, {tuning} tuning",
        "",
        f"{'String':>6} | {'Gauge':>6} | {'Note':>5} | {'Freq (Hz)':>10} | {'Tension (lbs)':>14}",
        f"{'------':>6}-+-{'------':>6}-+-{'-----':>5}-+-{'-' * 10}-+-{'-' * 14}",
    ]

    total = 0.0
    for i in range(6):
        gauge = gauges[i]
        note = notes[i]
        freq = NOTE_FREQUENCIES.get(note, 0)
        uw = UNIT_WEIGHTS.get(gauge, 0)

        # T = (UW * (2 * L * F)^2) / 386.4
        # 386.4 = gravitational constant in in/s^2
        tension = (uw * (2 * scale_length * freq) ** 2) / 386.4
        total += tension

        lines.append(
            f"{i + 1:6d} | {gauge:>6} | {note:>5} | {freq:10.2f} | {tension:14.2f}"
        )

    lines.append("")
    lines.append(f"Total Tension: {total:.2f} lbs ({total * 4.44822:.1f} N)")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Saddle Placement (Compensation)
# ---------------------------------------------------------------------------

def saddle_placement(scale_length, string_type="steel"):
    """Calculate saddle compensation for proper intonation.

    Args:
        scale_length: Theoretical scale length in inches.
        string_type: 'steel' or 'nylon'.

    Returns:
        Formatted saddle position with treble and bass compensation.
    """
    if string_type.lower() == "nylon":
        comp_treble = 0.04
        comp_bass = 0.08
    else:
        comp_treble = 0.06
        comp_bass = 0.15

    loc_treble = scale_length + comp_treble
    loc_bass = scale_length + comp_bass

    lines = [
        f"Saddle Placement: {scale_length}\" {string_type.title()} Strings",
        "",
        f"  Theoretical scale length: {scale_length:.4f}\" ({scale_length * 25.4:.2f} mm)",
        f"  Treble compensation:      +{comp_treble:.4f}\"",
        f"  Bass compensation:        +{comp_bass:.4f}\"",
        "",
        f"  Treble saddle position:   {loc_treble:.4f}\" ({loc_treble * 25.4:.2f} mm) from nut",
        f"  Bass saddle position:     {loc_bass:.4f}\" ({loc_bass * 25.4:.2f} mm) from nut",
        f"  Saddle angle (slant):     {(comp_bass - comp_treble):.4f}\" ({(comp_bass - comp_treble) * 25.4:.2f} mm) bass-side back",
    ]

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Bridge Thickness / Neck Angle Geometry
# ---------------------------------------------------------------------------

def bridge_thickness(desired_action_12th=0.090, saddle_protrusion=0.125, fret_height=0.040):
    """Calculate target bridge thickness from action and geometry.

    The action at the 12th fret is half the total string-to-fretboard
    distance at the bridge (because the 12th fret is the midpoint).

    Args:
        desired_action_12th: Desired string height at 12th fret in inches (default 0.090 = 6/64).
        saddle_protrusion: How far saddle sticks above bridge in inches (default 0.125 = 1/8).
        fret_height: Fret crown height in inches (default 0.040).

    Returns:
        Formatted bridge thickness result.
    """
    height_above_frets = desired_action_12th * 2
    total_height = height_above_frets + fret_height
    thickness = total_height - saddle_protrusion

    lines = [
        f"Bridge Thickness Calculation:",
        "",
        f"  Desired action at 12th:    {desired_action_12th:.3f}\" ({desired_action_12th * 25.4:.2f} mm)",
        f"  Action projected to bridge: {height_above_frets:.3f}\" ({height_above_frets * 25.4:.2f} mm)  (2x 12th fret action)",
        f"  Fret height:               {fret_height:.3f}\" ({fret_height * 25.4:.2f} mm)",
        f"  Total height at bridge:    {total_height:.3f}\" ({total_height * 25.4:.2f} mm)",
        f"  Saddle protrusion:         {saddle_protrusion:.3f}\" ({saddle_protrusion * 25.4:.2f} mm)",
        "",
        f"  >>> Target Bridge Thickness: {thickness:.3f}\" ({thickness * 25.4:.2f} mm)",
    ]

    return "\n".join(lines)
