import { coreMachineDefinitions } from './data'; import { MACHINE_CATEGORIES, MACHINE_CATEGORY_LABELS, MACHINE_DIFFICULTIES, MACHINE_DIFFICULTY_LABELS, } from './schema'; import type { CatalogueCategoryFilter, CatalogueDifficultyFilter, MachineCategory, MachineDefinition, MachineDifficulty, MachineId, MachinePart, MachinePartViewState, } from './schema'; const isDefined = (value: T | undefined | null): value is T => value != null; export const defaultMachineId: MachineId = 'four-stroke-petrol-engine'; export const machineRegistry = Object.freeze([...coreMachineDefinitions]) as readonly MachineDefinition[]; export const MACHINE_REGISTRY = machineRegistry; export const machines = machineRegistry; export const catalogueMachines = machineRegistry; export const machineRegistryById = machineRegistry.reduce>((accumulator, machine) => { accumulator[machine.id] = machine; return accumulator; }, {}); export const machineRegistryBySlug = machineRegistry.reduce>((accumulator, machine) => { accumulator[machine.slug] = machine; return accumulator; }, {}); export const machineCategories = MACHINE_CATEGORIES.map((value) => ({ value, label: MACHINE_CATEGORY_LABELS[value], count: machineRegistry.filter((machine) => machine.category === value).length, })); export const categories = machineCategories; export const difficultyOptions = MACHINE_DIFFICULTIES.map((value) => ({ value, label: MACHINE_DIFFICULTY_LABELS[value], count: machineRegistry.filter((machine) => machine.difficulty === value).length, })); export const difficulties = difficultyOptions; export const featuredMachines = machineRegistry.filter((machine) => [ 'four-stroke-petrol-engine', 'planetary-gearbox', 'centrifugal-pump', 'geneva-drive', 'turbocharger', 'turbofan-engine', ].includes(machine.id), ); export const getAllMachines = (): readonly MachineDefinition[] => machineRegistry; export const getDefaultMachineId = (): MachineId => defaultMachineId; export const getMachineById = (id: string | null | undefined): MachineDefinition | undefined => { if (!id) { return undefined; } return machineRegistryById[id] ?? machineRegistryBySlug[id]; }; export const getMachineBySlug = (slug: string | null | undefined): MachineDefinition | undefined => { if (!slug) { return undefined; } return machineRegistryBySlug[slug] ?? machineRegistryById[slug]; }; export const requireMachineById = (id: string | null | undefined): MachineDefinition => { const machine = getMachineById(id); if (!machine) { throw new Error(`Unknown Mechanica machine id: ${id ?? ''}`); } return machine; }; export const getMachinesByCategory = (category: CatalogueCategoryFilter): readonly MachineDefinition[] => { if (category === 'all') { return machineRegistry; } return machineRegistry.filter((machine) => machine.category === category); }; export const getMachinesByDifficulty = (difficulty: CatalogueDifficultyFilter): readonly MachineDefinition[] => { if (difficulty === 'all') { return machineRegistry; } return machineRegistry.filter((machine) => machine.difficulty === difficulty); }; export const getMachineParts = (machineOrId: MachineDefinition | string | null | undefined): readonly MachinePart[] => { const machine = typeof machineOrId === 'string' ? getMachineById(machineOrId) : machineOrId; return machine?.parts ?? []; }; export const getPartById = ( machineOrId: MachineDefinition | string | null | undefined, partId: string | null | undefined, ): MachinePart | undefined => { if (!partId) { return undefined; } return getMachineParts(machineOrId).find((part) => part.id === partId); }; export const getDefaultPartViewState = ( machineOrId: MachineDefinition | string | null | undefined, ): Record => { const machine = typeof machineOrId === 'string' ? getMachineById(machineOrId) : machineOrId; if (!machine) { return {}; } return machine.parts.reduce>((accumulator, part) => { accumulator[part.id] = { partId: part.id, visible: part.defaultVisible, opacity: part.defaultOpacity, }; return accumulator; }, {}); }; export const getRelatedMachines = ( machineOrId: MachineDefinition | string | null | undefined, limit = 4, ): readonly MachineDefinition[] => { const machine = typeof machineOrId === 'string' ? getMachineById(machineOrId) : machineOrId; if (!machine) { return []; } const directMatches = machine.related.map((relatedId) => getMachineById(relatedId)).filter(isDefined); const fallbackMatches = machineRegistry.filter( (candidate) => candidate.id !== machine.id && candidate.category === machine.category && !directMatches.some((direct) => direct.id === candidate.id), ); return [...directMatches, ...fallbackMatches].slice(0, limit); }; export const getCatalogueKeywords = (machine: MachineDefinition): readonly string[] => [ machine.title, machine.subtitle, machine.description, machine.category, machine.difficulty, ...machine.tags, ...machine.keywords, ...machine.parts.flatMap((part) => [part.name, ...part.tags]), ]; export const getMachineCountsByCategory = (): Record => MACHINE_CATEGORIES.reduce>( (accumulator, category) => { accumulator[category] = machineRegistry.filter((machine) => machine.category === category).length; return accumulator; }, { engines: 0, gearboxes: 0, pumps: 0, mechanisms: 0, structural: 0, }, ); export const getMachineCountsByDifficulty = (): Record => MACHINE_DIFFICULTIES.reduce>( (accumulator, difficulty) => { accumulator[difficulty] = machineRegistry.filter((machine) => machine.difficulty === difficulty).length; return accumulator; }, { beginner: 0, intermediate: 0, advanced: 0, }, ); export default machineRegistry;