import * as math from 'mathjs';
import { Phase, type Stat, type Surface } from './race';

export interface Race {
	location: RaceLocation;
	length: number;
	surface: Surface;
	direction: Direction;
	spans: Span[];
	slopes: Slope[];
	thresholds: [] | [Stat] | [Stat, Stat];
}

export enum RaceLocation {
	Sapporo = 'Sapporo',
	Hakodate = 'Hakodate',
	Niigata = 'Niigata',
	Fukushima = 'Fukushima',
	Nakayama = 'Nakayama',
	Tokyo = 'Tokyo',
	Chukyo = 'Chukyo',
	Kyoto = 'Kyoto',
	Hanshin = 'Hanshin',
	Kokura = 'Kokura',
	Ooi = 'Ooi',
	Kawasaki = 'Kawasaki',
	Funabashi = 'Funabashi',
	Morioka = 'Morioka',
	Longchamp = 'Longchamp',
	SantaAnitaPark = 'Santa Anita Park',
	DelMar = 'Del Mar',
}

export enum Direction {
	Right,
	Left,
	Stretch,
}

export enum SpanType {
	FrontStraight,
	AcrossStraight,
	FalseStraight,
	Corner1,
	Corner2,
	Corner3,
	Corner4,
}

export interface Span {
	type: SpanType;
	span: [number, number];
}

export interface Slope {
	per: -2 | -1.5 | -1 | 1 | 1.5 | 2;
	span: [number, number];
}

function sortSlopes(slopes: Slope[]): Slope[] {
	return slopes.toSorted((a, b) => a.span[0] - b.span[0]);
}

/**
 * Get the distances into a race where each phase ends.
 * @param raceLen Length of the race in meters
 * @returns Quadruple of phase end points, including separate late and last spurt phases
 */
export function phases(raceLen: number): [number, number, number, number] {
	const sixth = raceLen / 6;
	const half = raceLen / 2;
	return [sixth, sixth + half, 2 * sixth + half, raceLen];
}

export interface SectionInfo {
	section: number;
	phase: Phase;
	endRate: number;
	posKeep: boolean;
	rush: boolean;
	spotStruggleEnter: boolean;
}

export const SECTIONS: ReadonlyArray<SectionInfo> = [
	{ section: 1, phase: Phase.EarlyRace, endRate: 1 / 24, posKeep: true, rush: false, spotStruggleEnter: false },
	{ section: 2, phase: Phase.EarlyRace, endRate: 2 / 24, posKeep: true, rush: true, spotStruggleEnter: true },
	{ section: 3, phase: Phase.EarlyRace, endRate: 3 / 24, posKeep: true, rush: true, spotStruggleEnter: true },
	{ section: 4, phase: Phase.EarlyRace, endRate: 4 / 24, posKeep: true, rush: true, spotStruggleEnter: true },
	{ section: 5, phase: Phase.MidRace, endRate: 5 / 24, posKeep: true, rush: true, spotStruggleEnter: true },
	{ section: 6, phase: Phase.MidRace, endRate: 6 / 24, posKeep: true, rush: true, spotStruggleEnter: false },
	{ section: 7, phase: Phase.MidRace, endRate: 7 / 24, posKeep: true, rush: true, spotStruggleEnter: false },
	{ section: 8, phase: Phase.MidRace, endRate: 8 / 24, posKeep: true, rush: true, spotStruggleEnter: false },
	{ section: 9, phase: Phase.MidRace, endRate: 9 / 24, posKeep: true, rush: true, spotStruggleEnter: false },
	{ section: 10, phase: Phase.MidRace, endRate: 10 / 24, posKeep: true, rush: false, spotStruggleEnter: false },
	{ section: 11, phase: Phase.MidRace, endRate: 11 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 12, phase: Phase.MidRace, endRate: 12 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 13, phase: Phase.MidRace, endRate: 13 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 14, phase: Phase.MidRace, endRate: 14 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 15, phase: Phase.MidRace, endRate: 15 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 16, phase: Phase.MidRace, endRate: 16 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 17, phase: Phase.LateRace, endRate: 17 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 18, phase: Phase.LateRace, endRate: 18 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 19, phase: Phase.LateRace, endRate: 19 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 20, phase: Phase.LateRace, endRate: 20 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 21, phase: Phase.LateRace, endRate: 21 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 22, phase: Phase.LateRace, endRate: 22 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 23, phase: Phase.LateRace, endRate: 23 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
	{ section: 24, phase: Phase.LateRace, endRate: 24 / 24, posKeep: false, rush: false, spotStruggleEnter: false },
];

export interface SectionSlopeSegment extends SectionInfo {
	startDist: number;
	endDist: number;
	slope: -2 | -1.5 | -1 | 0 | 1 | 1.5 | 2;
}

export function splitSectionSlopes(raceLen: number, slopes: Slope[]): SectionSlopeSegment[] {
	const ss = sortSlopes(slopes);
	return SECTIONS.flatMap<SectionSlopeSegment>((sec) => {
		const startDist = ((sec.section - 1) * raceLen) / 24;
		const endDist = (sec.section * raceLen) / 24;
		const s = ss.filter((sl) => sl.span[0] < endDist && sl.span[1] > startDist);
		if (s.length === 0) {
			return [{ ...sec, startDist, endDist, slope: 0 }];
		}
		const r: Array<SectionSlopeSegment> = [{ ...sec, startDist, endDist: math.min(s[0].span[0], endDist), slope: 0 }];
		for (const [i, sl] of s.entries()) {
			const slopeEnd = math.min(endDist, sl.span[1]);
			r.push(
				{ ...sec, startDist: math.max(startDist, sl.span[0]), endDist: slopeEnd, slope: sl.per },
				{ ...sec, startDist: slopeEnd, endDist: math.min(s[i + 1]?.span[0] ?? endDist, endDist), slope: 0 },
			);
		}
		return r.filter((sl) => sl.startDist < sl.endDist);
	});
}
