import { Model } from "../model";
import * as kinds from "../core/kinds";
import { isString } from "../core/util/types";
import { to_object } from "../core/util/object";
export function decode_def(def, deserializer) {
    function kind_of(ref) {
        if (isString(ref)) {
            switch (ref) {
                case "Any": return kinds.Any;
                case "Unknown": return kinds.Unknown;
                case "Bool": return kinds.Bool;
                case "Float": return kinds.Float;
                case "Int": return kinds.Int;
                case "Bytes": return kinds.Bytes;
                case "Str": return kinds.Str;
                case "Null": return kinds.Null;
            }
        }
        else {
            switch (ref[0]) {
                case "Regex": {
                    const [, regex, flags] = ref;
                    return kinds.Regex(new RegExp(regex, flags));
                }
                case "Nullable": {
                    const [, sub_ref] = ref;
                    return kinds.Nullable(kind_of(sub_ref));
                }
                case "Or": {
                    const [, sub_ref, ...sub_refs] = ref;
                    return kinds.Or(kind_of(sub_ref), ...sub_refs.map(kind_of));
                }
                case "Tuple": {
                    const [, sub_ref, ...sub_refs] = ref;
                    return kinds.Tuple(kind_of(sub_ref), ...sub_refs.map(kind_of));
                }
                case "List": {
                    const [, sub_ref] = ref;
                    return kinds.List(kind_of(sub_ref));
                }
                case "Struct": {
                    const [, ...entry_refs] = ref;
                    const entries = entry_refs.map(([key, val_ref]) => [key, kind_of(val_ref)]);
                    return kinds.Struct(to_object(entries));
                }
                case "Dict": {
                    const [, val_ref] = ref;
                    return kinds.Dict(kind_of(val_ref));
                }
                case "Mapping": {
                    const [, key_ref, val_ref] = ref;
                    return kinds.Mapping(kind_of(key_ref), kind_of(val_ref));
                }
                case "Enum": {
                    const [, ...items] = ref;
                    return kinds.Enum(...items);
                }
                case "Ref": {
                    const [, model_ref] = ref;
                    const model = deserializer.resolver.get(model_ref.id);
                    if (model != null) {
                        return kinds.Ref(model);
                    }
                    else {
                        throw new Error(`${model_ref.id} wasn't defined before referencing it`);
                    }
                }
                case "AnyRef": {
                    return kinds.AnyRef();
                }
            }
        }
    }
    const base = (() => {
        const name = def.extends?.id ?? "Model";
        if (name == "Model") {
            // TODO: support base classes in general
            return Model;
        }
        const base = deserializer.resolver.get(name);
        if (base != null) {
            return base;
        }
        else {
            throw new Error(`base model ${name} of ${def.name} is not defined`);
        }
    })();
    const model = class extends base {
        static __qualified__ = def.name;
    };
    function decode(value) {
        if (value === undefined) {
            return value;
        }
        else {
            return deserializer.decode(value);
        }
    }
    for (const prop of def.properties ?? []) {
        const kind = kind_of(prop.kind);
        model.define({ [prop.name]: [kind, decode(prop.default)] });
    }
    for (const prop of def.overrides ?? []) {
        model.override({ [prop.name]: decode(prop.default) });
    }
    deserializer.resolver.register(model);
    return model;
}
//# sourceMappingURL=defs.js.map