import { getParam } from "./engine_utils.js";

const debug = getParam("debugtypes");

declare type Type = new (...args: any[]) => any;

class _TypeStore {

    private _types: Map<string, Type> = new Map();
    private _reverseTypes: Map<Type, string> = new Map();
    private _lazyLoaders: Map<string, () => Promise<Type>> = new Map();

    constructor() {
        if (debug) console.warn("TypeStore: Created", this);
    }

    /**
     * add a type to the store
     */
    public add(key: string, type: Type) {
        if (debug) console.warn("ADD TYPE", key);
        const existing = this._types.get(key);
        if (!existing) {
            this._types.set(key, type);
            this._reverseTypes.set(type, key);
        }
        else {
            if (debug) {
                if (existing !== type) {
                    console.warn("Type name exists multiple times in your project and may lead to runtime errors:", key)
                }
            }
        }
    }

    /**
     * Register a lazy-loadable type. The loader is called on first use via {@link getAsync}.
     * Once resolved, the type is cached for synchronous access via {@link get}.
     */
    public addLazy(key: string, loader: () => Promise<Type>) {
        if (!this._types.has(key)) {
            this._lazyLoaders.set(key, loader);
        }
    }

    /**
     * @returns the type for the given key if registered
     */
    public get(key: string): Type | null {
        return this._types.get(key) || null;
    }

    /**
     * Async version of {@link get} that also resolves lazy-registered types.
     * After resolving, the type is cached for future synchronous access.
     */
    public async getAsync(key: string): Promise<Type | null> {
        const existing = this._types.get(key);
        if (existing) return existing;
        const loader = this._lazyLoaders.get(key);
        if (loader) {
            if (debug) console.warn("LAZY LOAD TYPE", key);
            const type = await loader();
            this.add(key, type);
            this._lazyLoaders.delete(key);
            return type;
        }
        return null;
    }

    /**
     * @returns the key/name for the given type if registered
     */
    public getKey(type: Type): string | null {
        return this._reverseTypes.get(type) || null;
    }
}

export const $BuiltInTypeFlag = Symbol("BuiltInType");
export const TypeStore = new _TypeStore();

/** 
 * add to a class declaration to automatically register it to the TypeStore (required for HMR right now)       
 * 
 * `@registerType`
 * 
 * `export class MyType extends Behaviour { ... }`
 */
export const registerType = function (constructor: Type) {
    if (!TypeStore.get(constructor.name))
        TypeStore.add(constructor.name, constructor);
}