"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 __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const codecClasses = __importStar(require("../../codec"));
const create_1 = require("../../codec/create");
const types_1 = require("../../codec/types");
const primitiveClasses = __importStar(require("../../primitive"));
// Maps the types as found to the source location. This is used to generate the
// imports in the output file, dep-duped and sorted
/** @internal */
function setImports(allDefs, imports, types) {
    const { codecTypes, localTypes, ignoredTypes, primitiveTypes, typesTypes } = imports;
    types.forEach((type) => {
        if (ignoredTypes.includes(type)) {
            // do nothing
        }
        else if (['AnyNumber', 'CallFunction', 'Codec', 'IExtrinsic', 'ITuple'].includes(type)) {
            typesTypes[type] = true;
        }
        else if (codecClasses[type]) {
            codecTypes[type] = true;
        }
        else if (primitiveClasses[type] || type === 'Metadata') {
            primitiveTypes[type] = true;
        }
        else if (type.includes('<') || type.includes('(') || type.includes('[')) {
            // If the type is a bit special (tuple, fixed u8, nested type...), then we
            // need to parse it with `getTypeDef`.
            const typeDef = create_1.getTypeDef(type);
            setImports(allDefs, imports, [types_1.TypeDefInfo[typeDef.info]]);
            // TypeDef.sub is a `TypeDef | TypeDef[]`
            if (Array.isArray(typeDef.sub)) {
                typeDef.sub.forEach((subType) => setImports(allDefs, imports, [subType.type]));
            }
            else if (typeDef.sub) {
                // typeDef.sub is a TypeDef in this case
                setImports(allDefs, imports, [typeDef.sub.type]);
            }
        }
        else {
            // find this module inside the exports from the rest
            const [moduleName] = Object.entries(allDefs).find(([, { types }]) => Object.keys(types).includes(type)) || [null];
            if (moduleName) {
                localTypes[moduleName][type] = true;
            }
        }
    });
}
exports.setImports = setImports;
// Create an Imports object, can be prefilled with `ignoredTypes`
/** @internal */
function createImports(importDefinitions, { types } = { types: {} }) {
    const definitions = {};
    const typeToModule = {};
    const moduleToPackage = {};
    Object.entries(importDefinitions).forEach(([packagePath, packageDef]) => {
        Object.entries(packageDef).forEach(([name, moduleDef]) => {
            if (definitions[name]) {
                throw new Error(`Duplicated module: ${name}. Packages: ${packagePath}, ${moduleToPackage[name]}`);
            }
            definitions[name] = moduleDef;
            moduleToPackage[name] = packagePath;
            Object.keys(moduleDef.types).forEach((type) => {
                if (typeToModule[type]) {
                    throw new Error(`Duplicated type: ${type}. Modules: ${name}, ${typeToModule[type]}`);
                }
                typeToModule[type] = name;
            });
        });
    });
    return {
        codecTypes: {},
        localTypes: Object.keys(definitions).reduce((local, mod) => {
            local[mod] = {};
            return local;
        }, {}),
        ignoredTypes: Object.keys(types),
        primitiveTypes: {},
        typesTypes: {},
        definitions,
        typeToModule,
        moduleToPackage
    };
}
exports.createImports = createImports;
