import { JSONValue, Serializable } from "./serializable.ts";import { ERROR_FAILED_TO_RESOLVE_POLYMORPHIC_CLASS, ERROR_MISSING_STATIC_OR_VALUE_ON_POLYMORPHIC_SWITCH,} from "./error_messages.ts";
export function PolymorphicResolver( target: unknown, propertyKey: string | symbol,): void { registerPolymorphicResolver( target, (target as Record<typeof propertyKey, () => Serializable>)[ propertyKey as string ], );}
export type ResolverFunction = ( input: string | JSONValue | Object,) => Serializable;
const POLYMORPHIC_RESOLVER_MAP = new Map<unknown, ResolverFunction>();
function registerPolymorphicResolver( classPrototype: unknown, resolver: ResolverFunction,): void { POLYMORPHIC_RESOLVER_MAP.set(classPrototype, resolver);}
export function PolymorphicSwitch( initializerFunction: InitializerFunction, value?: unknown,): PropertyDecorator { const hasValue = arguments.hasOwnProperty("1"); return function _PolymorphicSwitch( target: Function | Object, propertyKey: string | symbol, ) { if ( !Object.prototype.hasOwnProperty.call(target, propertyKey) && !hasValue ) { throw new Error(ERROR_MISSING_STATIC_OR_VALUE_ON_POLYMORPHIC_SWITCH); }
let targetConstructor = target;
if (typeof target !== "function") { targetConstructor = target.constructor; }
const parentPrototype = Object.getPrototypeOf(targetConstructor);
const propertyValue = (target as Record<typeof propertyKey, unknown>)[ (propertyKey as string) ] || value;
registerPolymorphicSwitch( parentPrototype, propertyKey, propertyValue, initializerFunction, ); };}
export type InitializerFunction = () => Serializable;const POLYMORPHIC_SWITCH_MAP = new Map< unknown, Map<string | symbol, Map<unknown, InitializerFunction>>>();
function registerPolymorphicSwitch( parentPrototype: unknown, propertyKey: string | symbol, propertyValue: unknown, initializerFunction: InitializerFunction,): void { let classMap = POLYMORPHIC_SWITCH_MAP.get(parentPrototype); if (!classMap) { POLYMORPHIC_SWITCH_MAP.set(parentPrototype, new Map()); classMap = POLYMORPHIC_SWITCH_MAP.get(parentPrototype); }
let propertyKeyMap = classMap?.get(propertyKey); if (!propertyKeyMap) { classMap?.set(propertyKey, new Map()); propertyKeyMap = classMap?.get(propertyKey); }
propertyKeyMap?.set(propertyValue, initializerFunction);}
export function polymorphicClassFromJSON<T extends Serializable>( classPrototype: Object & { prototype: T }, input: string | JSONValue | Object,): T { return (resolvePolymorphicClass(classPrototype, input)).fromJSON(input);}
function resolvePolymorphicClass<T extends Serializable>( classPrototype: Object & { prototype: T }, input: string | JSONValue | Object,): T { const classResolver = POLYMORPHIC_RESOLVER_MAP.get(classPrototype); if (classResolver) { return classResolver(input) as T; }
const resolvedClass = resolveSwitchMap(classPrototype, input);
if (resolvedClass) { return resolvedClass as T; }
throw new Error(ERROR_FAILED_TO_RESOLVE_POLYMORPHIC_CLASS);}
function resolveSwitchMap( classPrototype: unknown, input: string | JSONValue | Object,): Serializable | null { const classMap = POLYMORPHIC_SWITCH_MAP.get(classPrototype);
if (!classMap) { throw new Error(ERROR_FAILED_TO_RESOLVE_POLYMORPHIC_CLASS); }
const inputObject = typeof input === "string" ? JSON.parse(input) : input;
for (const [propertyKey, valueMap] of classMap.entries()) { const value = inputObject[propertyKey]; const initializer = valueMap.get(value); if (initializer) { return initializer(); } }
return null;}