<script lang="ts">
    import * as race from '$lib/race';
    import * as accel from '$lib/racelib/accel';

    let rawPower = $state(1200);
    let rawGuts = $state(1200);
    let rawWit = $state(1200);
    let powerBonus = $state(0);
    let gutsBonus = $state(0);
    let witBonus = $state(0);
    let style = $state(race.RunningStyle.FrontRunner);
    let surfApt = $state(race.AptitudeLevel.A);
    let styleApt = $state(race.AptitudeLevel.A);
    let mood = $state(race.Mood.Normal);
    let isCareer = $state(false);

    let raceLen = $state(2000);
    let surface = $state(race.Surface.Turf);
    let conditions = $state(race.GroundConditions.Firm);
    let slope = $state(0);

    let rawSkills: accel.Skill[] = $state([]);
    let skillToAdd: accel.Skill = $state({accel: 0.2, dur: 1.2});
    const quick = [
        {name: "GW+TTL", skills: [{accel: 0.2, dur: 3}, {accel: 0.4, dur: 1.2}]},
        {name: "GW+EL", skills: [{accel: 0.2, dur: 3}, {accel: 0.2, dur: 1.2}]},
        {name: "Clear", skills: []},
    ];
    function addSkill() {
        rawSkills.push(skillToAdd);
        skillToAdd = {accel: 0.2, dur: 1.2};
    }
    function removeSkill(i: number) {
        rawSkills.splice(i, 1);
    }
    function setSkillList(l: accel.Skill[]) {
        rawSkills = l;
    }

    const phases = $derived(race.phases(raceLen));

    const basePower = $derived(race.baseStat(mood, rawPower));
    const adjPower = $derived(race.adjustedStat(race.Stat.Power, basePower, isCareer, surface, conditions));
    const power = $derived(race.finalStat(adjPower, powerBonus));
    const baseGuts = $derived(race.baseStat(mood, rawGuts));
    const adjGuts = $derived(race.adjustedStat(race.Stat.Guts, baseGuts, isCareer));
    const guts = $derived(race.finalStat(adjGuts, gutsBonus));
    const baseWit = $derived(race.baseStat(mood, rawWit));
    const adjWit = $derived(race.adjustedStat(race.Stat.Wit, baseWit, isCareer, styleApt));
    const wit = $derived(race.finalStat(adjWit, witBonus));

    const dashAccel = $derived(race.acceleration(power, style, surfApt, race.Phase.EarlyRace, race.AptitudeLevel.A, slope > 0, true));
    const postAccel = $derived(race.acceleration(power, style, surfApt, race.Phase.EarlyRace, race.AptitudeLevel.A, slope > 0, false));
    const minSpeed = $derived(race.minSpeed(raceLen, guts));
    const baseSpeed = $derived(race.baseSpeed(raceLen));
    const dashTarget = $derived(baseSpeed * 0.85);
    const hillMod: [number, number] = $derived.by(() => {
        if (slope > 0) {
            const m = race.uphillMod(power, slope);
            return [m, m];
        }
        if (slope < 0) {
            return [0, race.downhillAccelMod(slope)];
        }
        return [0, 0];
    })
    const earlyTarget = $derived(race.sectionSpeed(raceLen, baseWit, wit, style, race.Phase.EarlyRace));
    const speeds: [number, number] = $derived([earlyTarget[0] + hillMod[0], earlyTarget[1] + hillMod[1]]);
    const avgEarlyTarget = $derived((earlyTarget[0] + earlyTarget[1]) * 0.5);

    const startInstant: accel.Instant = {t: 0, x: 0, v: 3};
    function doSeggsToIt(targetSpeed: number, skills: accel.Skill[]) {
        const startDash = accel.up(startInstant, dashTarget, dashAccel, skills);
        const postStart = {...last(startDash.segments).end, v: minSpeed};
        const post = accel.up(postStart, targetSpeed, postAccel, startDash.skills);
        const r = [...startDash.segments, ...post.segments];
        return r;
    }
    function last<T>(l: T[]): T {
        return l[l.length - 1];
    }
    const skills = $derived(rawSkills.map((s) => ({...s, dur: race.skillDuration(s.dur, raceLen)})));
    const seggs = $derived([doSeggsToIt(speeds[0], skills), doSeggsToIt(speeds[1], skills)]);
    const unskilledSeggs = $derived([doSeggsToIt(speeds[0], []), doSeggsToIt(speeds[1], [])]);
    
    const distance = $derived([
        last(seggs[0]).end.x,
        last(seggs[1]).end.x,
    ]);
    const time = $derived([
        last(seggs[0]).end.t,
        last(seggs[1]).end.t,
    ]);
    const midTime = $derived([
        time[0] + (phases[race.Phase.EarlyRace] - distance[0])/speeds[1],
        time[1] + (phases[race.Phase.EarlyRace] - distance[1])/speeds[0],
    ]);
    function gainLengths(fast: accel.Instant, slow: accel.Instant): number {
        const m = fast.v * (slow.t - fast.t) + fast.x - slow.x;
        return m / race.HORSE_LENGTH;
    }
    const gain = $derived([
        gainLengths(last(seggs[0]).end, last(unskilledSeggs[0]).end),
        gainLengths(last(seggs[1]).end, last(unskilledSeggs[1]).end),
    ]);
</script>

<h1 class="text-4xl">Gate Calculator</h1>
<form class="mx-auto my-4 p-4 grid gap-8 grid-cols-1 max-w-4xl rounded-md shadow-sm md:grid-cols-5">
    <div class="grid rounded-md text-center shadow-md ring md:col-span-5 md:grid-cols-5">
        <div class="m-4">
            <label for="powerStat">Power</label>
            <input type="number" id="powerStat" bind:value={rawPower} class="w-full" />
        </div>
        <div class="m-4">
            <label for="gutsStat">Guts</label>
            <input type="number" id="gutsStat" bind:value={rawGuts} class="w-full" />
        </div>
        <div class="m-4">
            <label for="witStat">Wit</label>
            <input type="number" id="witStat" bind:value={rawWit} class="w-full" />
        </div>
        <div class="m-4">
            <label for="style">Style</label>
            <select id="style" required bind:value={style} class="w-full">
                {#each race.RUNNING_STYLES as [name, style]}
                    <option value={style}>{name}</option>
                {/each}
            </select>
        </div>
        <div class="m-4">
            <label for="mood">Mood</label>
            <select id="mood" required bind:value={mood} class="w-full">
                {#each race.MOODS as mood (mood)}
                    <option value={mood}>{race.Mood[mood]}</option>
                {/each}
            </select>
        </div>
        <div class="m-4">
            <label for="powerBonus">Power Greens</label>
            <input type="number" id="powerBonus" bind:value={powerBonus} class="w-full" />
        </div>
        <div class="m-4">
            <label for="gutsBonus">Guts Greens</label>
            <input type="number" id="gutsBonus" bind:value={gutsBonus} class="w-full" />
        </div>
        <div class="m-4">
            <label for="witBonus">Wit Greens</label>
            <input type="number" id="witBonus" bind:value={witBonus} class="w-full" />
        </div>
        <div class="m-4">
            <label for="surfaceApt">{race.Surface[surface]}</label>
            <select id="surfaceApt" required bind:value={surfApt} class="w-full">
                {#each race.APTITUDE_LEVELS as apt (apt)}
                    <option value={apt}>{race.AptitudeLevel[apt]}</option>
                {/each}
            </select>
        </div>
        <div class="m-4">
            <label for="styleApt">Style</label>
            <select id="styleApt" required bind:value={styleApt} class="w-full">
                {#each race.APTITUDE_LEVELS as apt (apt)}
                    <option value={apt}>{race.AptitudeLevel[apt]}</option>
                {/each}
            </select>
        </div>
    </div>
    <div class="mb-auto grid grid-cols-6 max-w-4xl rounded-md text-center shadow-md ring md:col-span-3">
        <input class="m-4 w-full col-span-4 justify-self-center-safe" type="range" id="raceLen" min="1000" max="3600" step="100" bind:value={raceLen} />
        <span class="mb-4 my-auto pl-2 col-span-2 text-sm">{raceLen}m</span>
        <select id="surface" bind:value={surface} class="m-4 col-span-2">
            <option value={race.Surface.Turf}>Turf</option>
            <option value={race.Surface.Dirt}>Dirt</option>
        </select>
        <select id="conditions" bind:value={conditions} class="m-4 col-span-2">
            {#each race.GROUND_CONDITONS as cond (cond)}
                <option value={cond}>{race.GroundConditions[cond]}</option>
            {/each}
        </select>
        <select id="slope" bind:value={slope} class="m-4 col-span-2">
            {#each [2, 1.5, 1, 0, -1, -1.5, -2] as per (per)}
                <option value={per}>{per > 0 ? `Uphill +${per}` : (per < 0 ? `Downhill ${per}` : 'Flat')}</option>
            {/each}
        </select>
    </div>
    <div class="mb-auto w-full flex flex-col max-w-4xl rounded-md text-center shadow-md ring md:col-span-2">
        <div class="mx-4 mt-2 flex flex-row gap-2">
            <span class="w-24 flex-1">Duration</span>
            <span class="w-24 flex-1">Accel</span>
            <span class="flex-none w-8"><!-- filler to align other columns --></span>
        </div>
        {#each rawSkills as skill, i (i)}
            <div class="mx-4 mt-2 flex flex-row gap-2">
                <span class="my-auto w-24 flex-1">{skill.dur}s</span>
                <span class="my-auto w-24 flex-1">{skill.accel}</span>
                <button type="button" class="my-auto flex-none w-8 h-8 rounded-full text-2xl hover:cursor-pointer bg-mist-300 dark:bg-mist-900" onclick={() => removeSkill(i)}>&times;</button>
            </div>
        {/each}
        <div class="mx-4 mt-2 flex flex-row gap-2">
            <input type="number" class="my-auto w-24 flex-1" min="0.9" step="0.3" bind:value={skillToAdd.dur} />
            <input type="number" class="my-auto w-24 flex-1" min="0" step="0.05" bind:value={skillToAdd.accel} />
            <button type="button" class="my-auto flex-none w-8 h-8 rounded-full text-2xl hover:cursor-pointer bg-mist-300 dark:bg-mist-900" onclick={addSkill}>+</button>
        </div>
        <div class="flex flex-row mt-1">
            <span class="my-auto h-0 flex-1 border"></span>
            <span class="mx-2 flex-none">Quick Setup</span>
            <span class="my-auto h-0 flex-1 border"></span>
        </div>
        <div class="flex flex-row m-2 gap-2 place-content-center-safe">
            {#each quick as q (q.name)}
                <button type="button" class="p-2 rounded-md hover:cursor-pointer bg-mist-300 dark:bg-mist-900" onclick={() => setSkillList(q.skills)}>{q.name}</button>
            {/each}
        </div>
    </div>
</form>

{#snippet info(name: string, unit: string, val: number, val2?: number)}
    <div class="m-2 max-w-72 flex-1 flex-col rounded-md border p-2 text-center shadow-sm transition-shadow hover:shadow-md">
        <span class="block">{name}</span>
        {#if val2 == null}
            <span class="block text-xl">{val.toFixed(3)} {unit}</span>
        {:else}
            <span class="block text-xl">{val.toFixed(3)} &ndash; {val2.toFixed(3)} {unit}</span>
        {/if}
    </div>
{/snippet}

<div class="mx-auto flex w-full flex-col place-items-center md:flex-row md:justify-center">
    {@render info('Base Acceleration', 'm/s²', postAccel)}
    {@render info('Target Speed', 'm/s', speeds[0], speeds[1])}
</div>
<div class="mx-auto flex w-full flex-col place-items-center md:flex-row md:justify-center">
    {@render info('Accel Time', 's', time[0], time[1])}
    {@render info('Accel Distance', 'm', distance[0], distance[1])}
    {@render info('Time to Mid-Race', 's', midTime[0], midTime[1])}
</div>
<div class="mx-auto flex w-full flex-col place-items-center md:flex-row md:justify-center">
    {@render info('Skill Gain', 'L', gain[0], gain[1])}
</div>

{#snippet seggsTable(name: string, s: accel.Travel[])}
    <table class="table-fixed border">
        <caption class="text-xl">{name}</caption>
        <thead>
            <tr class="py-2">
                <th class="px-2" scope="col">Time</th>
                <th class="px-2" scope="col">Accel</th>
                <th class="px-2" scope="col">Distance</th>
                <th class="px-2" scope="col">Speed</th>
            </tr>
        </thead>
        <tbody>
            {#each s as {start, end, a} (start.t)}
                <tr class="even:bg-mist-300 dark:even:bg-mist-900">
                    <td class="px-4 text-center">{end.t.toFixed(3)}</td>
                    <td class="px-4 text-center">{a.toFixed(3)}</td>
                    <td class="px-4 text-center">{end.x.toFixed(3)}</td>
                    <td class="px-4 text-center">{end.v.toFixed(3)}</td>
                </tr>
            {/each}
        </tbody>
    </table>
{/snippet}
<div class="mx-auto mt-8 max-w-4xl grid grid-cols-1 md:grid-cols-2 gap-8">
    {@render seggsTable(slope < 0 ? 'Fastest Speed (Downhill)' : 'Fastest Speed', seggs[1])}
    {@render seggsTable('Slowest Speed', seggs[0])}
</div>
