"use strict";
// Copyright 2017-2020 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const types_1 = require("./types");
const util_1 = require("@polkadot/util");
const BTreeMap_1 = __importDefault(require("../BTreeMap"));
const BTreeSet_1 = __importDefault(require("../BTreeSet"));
const Compact_1 = __importDefault(require("../Compact"));
const Enum_1 = __importDefault(require("../Enum"));
const Option_1 = __importDefault(require("../Option"));
const Result_1 = __importDefault(require("../Result"));
const Set_1 = __importDefault(require("../Set"));
const Struct_1 = __importDefault(require("../Struct"));
const Tuple_1 = __importDefault(require("../Tuple"));
const U8aFixed_1 = __importDefault(require("../U8aFixed"));
const Vec_1 = __importDefault(require("../Vec"));
const VecFixed_1 = __importDefault(require("../VecFixed"));
const createClass_1 = require("./createClass");
function getSubDefArray(value) {
    util_1.assert(value.sub && Array.isArray(value.sub), `Expected subtype as TypeDef[] in ${JSON.stringify(value)}`);
    return value.sub;
}
function getSubDef(value) {
    util_1.assert(value.sub && !Array.isArray(value.sub), `Expected subtype as TypeDef in ${JSON.stringify(value)}`);
    return value.sub;
}
function getSubType(value) {
    return getSubDef(value).type;
}
// create a maps of type string constructors from the input
function getTypeClassMap(value) {
    const result = {};
    return getSubDefArray(value).reduce((result, sub) => {
        result[sub.name] = sub.type;
        return result;
    }, result);
}
// create an array of type string constructors from the input
function getTypeClassArray(value) {
    return getSubDefArray(value).map(({ type }) => type);
}
const infoMapping = {
    [types_1.TypeDefInfo.BTreeMap]: (registry, value) => {
        const [keyType, valueType] = getTypeClassArray(value);
        // eslint-disable-next-line @typescript-eslint/no-use-before-define
        return BTreeMap_1.default.with(keyType, valueType);
    },
    [types_1.TypeDefInfo.BTreeSet]: (registry, value) => BTreeSet_1.default.with(getSubType(value)),
    [types_1.TypeDefInfo.Compact]: (registry, value) => Compact_1.default.with(getSubType(value)),
    [types_1.TypeDefInfo.Enum]: (registry, value) => Enum_1.default.with(getTypeClassMap(value)),
    // We have circular deps between Linkage & Struct
    [types_1.TypeDefInfo.Linkage]: (registry, value) => {
        const type = `Option<${getSubType(value)}>`;
        const Clazz = Struct_1.default.with({ previous: type, next: type });
        createClass_1.ClassOf.prototype.toRawType = function () {
            return `Linkage<${this.next.toRawType(true)}>`;
        };
        return Clazz;
    },
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    [types_1.TypeDefInfo.Null]: (registry, _) => createClass_1.ClassOf(registry, 'Null'),
    [types_1.TypeDefInfo.Option]: (registry, value) => Option_1.default.with(getSubType(value)),
    [types_1.TypeDefInfo.Plain]: (registry, value) => registry.getOrThrow(value.type, `Unable to find plain type for ${JSON.stringify(value)}`),
    [types_1.TypeDefInfo.Result]: (registry, value) => {
        const [Ok, Error] = getTypeClassArray(value);
        // eslint-disable-next-line @typescript-eslint/no-use-before-define
        return Result_1.default.with({ Ok, Error });
    },
    [types_1.TypeDefInfo.Set]: (registry, value) => {
        const result = {};
        return Set_1.default.with(getSubDefArray(value).reduce((result, { name, index }) => {
            result[name] = index;
            return result;
        }, result));
    },
    [types_1.TypeDefInfo.Struct]: (registry, value) => Struct_1.default.with(getTypeClassMap(value)),
    [types_1.TypeDefInfo.Tuple]: (registry, value) => Tuple_1.default.with(getTypeClassArray(value)),
    [types_1.TypeDefInfo.Vec]: (registry, value) => {
        const subType = getSubType(value);
        return (subType === 'u8'
            ? createClass_1.ClassOf(registry, 'Bytes')
            : Vec_1.default.with(subType));
    },
    [types_1.TypeDefInfo.VecFixed]: (registry, value) => {
        util_1.assert(value.ext, 'Expected length & type information for fixed vector');
        const ext = value.ext;
        return (ext.type === 'u8'
            ? U8aFixed_1.default.with((ext.length * 8))
            : VecFixed_1.default.with(ext.type, ext.length));
    }
};
// Returns the type Class for construction
function getTypeClass(registry, value) {
    const Type = registry.get(value.type);
    if (Type) {
        return Type;
    }
    const getFn = infoMapping[value.info];
    if (!getFn) {
        throw new Error(`Unable to determine type from ${JSON.stringify(value)}`);
    }
    return getFn(registry, value);
}
exports.getTypeClass = getTypeClass;
