import {
	TableType,
} from "qrc:/js/lib/generated/enum";
import {
	isCadFeature,
	isCadThroughHole,
	isPoint2,
	isScrewThread,
} from "qrc:/js/lib/generated/typeguard";
import {
	front,
} from "./array_util";
import {
	tolerances,
} from "./constants";

import {
	getTable,
} from "./table_utils";
import {
	assert,
	isArray,
	isInstanceOf,
	isNumber,
} from "./utils";

export interface TappingCandidate {
	cadFeature: CadFeature;
	matchingScrewThreads: ScrewThread[];
}

export function isTappingCandidate(arg: unknown): arg is TappingCandidate {
	return isInstanceOf<TappingCandidate>(arg, {
		cadFeature: isCadFeature,
		matchingScrewThreads: (arg: unknown): arg is ScrewThread[] => isArray(arg, isScrewThread),
	});
}

export interface TappingSceneCandidate extends TappingCandidate {
	/**
	 * Center of the associated core hole in Scene / TwoDimRep coordinates
	 */
	center2: Point2;
}

export interface TappingSceneSelectionEntry {
	cadFeature: CadFeature;
	screwThread: ScrewThread;
	center2: Point2;
}

/**
 * Create tapping label scene item
 */
export function tappingLabel(diameter: number, center2d: Point2, text: string): SceneTextItem {
	return {
		pos: {
			// Position label top right of the core hole
			entries: [
				center2d.entries[0] + diameter * 0.5,
				center2d.entries[1] + diameter,
			],
		},
		text: text,
	};
}

/**
 * Create text items with names of the screw threads to add to a scene
 */
export function createSheetTappingSceneIndexItems(
	tappingCandidates: readonly Readonly<TappingSceneCandidate>[],
	screwThreadsInput?: readonly Readonly<ScrewThread>[],
): SceneTextItem[] {
	const screwThreads = screwThreadsInput ?? getTable(TableType.screwThread);
	return tappingCandidates.map((candidate, index): SceneTextItem => {
		const screwThread = screwThreads.find(st => st.identifier === front(candidate.matchingScrewThreads).identifier);
		assert(screwThread !== undefined, "Sheet tapping user data do not match table values.  Expecting valid screwThread.");
		const center = candidate.center2;
		return tappingLabel(screwThread.coreHoleDiameter, center, index.toFixed(0));
	});
}

/**
 * Create zero-based indices to add to a scene
 */
export function createSheetTappingSceneNameItems(entries: readonly Readonly<TappingSceneSelectionEntry>[]): SceneTextItem[] {
	const screwThreads = getTable(TableType.screwThread);
	return entries.map((candidate): SceneTextItem => {
		const screwThread = screwThreads.find(st => st.identifier === candidate.screwThread.identifier);
		assert(screwThread !== undefined, "Sheet tapping user data do not match table values.  Expecting valid screwThread.");
		const center2d = candidate.center2;
		return tappingLabel(screwThread.coreHoleDiameter, center2d, screwThread.name);
	});
}

export interface ThroughHole {
	cadThroughHole: CadThroughHole;
	center2: Point2;
	diameter: number;
}

export function isThroughHole(arg: unknown): arg is ThroughHole {
	return isInstanceOf<ThroughHole>(arg, {
		cadThroughHole: isCadThroughHole,
		center2: isPoint2,
		diameter: isNumber,
	});
}

/**
 * Compute TappingCandidate for a ThroughHole and a thickness
 */
export function tappingCandidate(
	info: ThroughHole,
	thickness: number,
	screwThreadsInput?: readonly Readonly<ScrewThread>[],
): TappingCandidate {
	const screwThreads = screwThreadsInput ?? getTable(TableType.screwThread);
	const matchingScrewThreads = screwThreads
		.filter(screwThread => (thickness + tolerances.thickness) > screwThread.minDepth)
		.filter(screwThread => Math.abs(screwThread.coreHoleDiameter - info.diameter) < screwThread.symmetricTolerance);
	return {
		cadFeature: {
			type: "throughHole",
			content: info.cadThroughHole,
		},
		matchingScrewThreads: matchingScrewThreads,
	};
}
