"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.
Object.defineProperty(exports, "__esModule", { value: true });
const types_1 = require("../../codec/types");
const create_1 = require("../../codec/create");
const utils_1 = require("../../codec/utils");
const imports_1 = require("./imports");
exports.HEADER = '// Auto-generated via `yarn polkadot-types-from-defs`, do not edit\n/* eslint-disable @typescript-eslint/no-empty-interface */\n\n';
exports.FOOTER = '\n';
const TYPES_NON_PRIMITIVE = ['Metadata'];
// creates the import lines
/** @internal */
function createImportCode(header, imports, checks) {
    return [
        {
            file: '@polkadot/types/types',
            types: Object.keys(imports.typesTypes)
        },
        {
            file: '@polkadot/types/codec',
            types: Object
                .keys(imports.codecTypes)
                .filter((name) => name !== 'Tuple')
        },
        {
            file: '@polkadot/types/primitive',
            types: Object
                .keys(imports.primitiveTypes)
                .filter((name) => !TYPES_NON_PRIMITIVE.includes(name))
        },
        {
            file: '@polkadot/types',
            types: Object
                .keys(imports.primitiveTypes)
                .filter((name) => TYPES_NON_PRIMITIVE.includes(name))
        },
        ...checks
    ].reduce((result, { file, types }) => {
        return types.length
            ? `${result}import { ${types.sort().join(', ')} } from '${file}';\n`
            : result;
    }, header) + '\n';
}
exports.createImportCode = createImportCode;
// helper to generate a `export interface <Name> extends <Base> {<Body>}
/** @internal */
function exportInterface(name = '', base, body = '') {
    // * @description extends [[${base}]]
    const doc = `/** @name ${name} */\n`;
    return `${doc}export interface ${name} extends ${base} {${body.length ? '\n' : ''}${body}}`;
}
exports.exportInterface = exportInterface;
// helper to create an `export type <Name> = <Base>`
// but since we don't want type alias (TS doesn't preserve names) we use
// interface here.
/** @internal */
function exportType(name = '', base) {
    return exportInterface(name, base);
}
exports.exportType = exportType;
/**
 * Given the inner `K` & `V`, return a `BTreeMap<K, V>`  string
 */
/** @internal */
function formatBTreeMap(key, val) {
    return `BTreeMap<${key}, ${val}>`;
}
/**
 * Given the inner `V`, return a `BTreeSet<V>`  string
 */
/** @internal */
function formatBTreeSet(val) {
    return `BTreeSet<${val}>`;
}
/**
 * Given the inner `T`, return a `Compact<T>` string
 */
/** @internal */
function formatCompact(inner) {
    return utils_1.paramsNotation('Compact', inner);
}
/**
 * Given the inner `O` & `E`, return a `Result<O, E>`  string
 */
/** @internal */
function formatResult(innerOk, innerError) {
    return `Result<${innerOk}, ${innerError}>`;
}
/**
 * Given the inner `T`, return a `Option<T>` string
 */
/** @internal */
function formatOption(inner) {
    return utils_1.paramsNotation('Option', inner);
}
/**
 * Given the inners `T[]`, return a `ITuple<...T>` string
 */
/** @internal */
function formatTuple(inners) {
    return utils_1.paramsNotation('ITuple', `[${inners.join(', ')}]`);
}
/**
 * Given the inner `T`, return a `Vec<T>` string
 */
/** @internal */
function formatVec(inner) {
    return utils_1.paramsNotation('Vec', inner);
}
/**
 * Correctly format a given type
 */
/** @internal */
function formatType(definitions, type, imports) {
    let typeDef;
    if (typeof type === 'string') {
        typeDef = create_1.getTypeDef(type);
    }
    else {
        typeDef = type;
    }
    imports_1.setImports(definitions, imports, [typeDef.type]);
    switch (typeDef.info) {
        case types_1.TypeDefInfo.Compact: {
            imports_1.setImports(definitions, imports, ['Compact']);
            return formatCompact(formatType(definitions, typeDef.sub.type, imports));
        }
        case types_1.TypeDefInfo.Option: {
            imports_1.setImports(definitions, imports, ['Option']);
            return formatOption(formatType(definitions, typeDef.sub.type, imports));
        }
        case types_1.TypeDefInfo.Plain: {
            return typeDef.type;
        }
        case types_1.TypeDefInfo.Vec: {
            imports_1.setImports(definitions, imports, ['Vec']);
            return formatVec(formatType(definitions, typeDef.sub.type, imports));
        }
        case types_1.TypeDefInfo.Tuple: {
            imports_1.setImports(definitions, imports, ['ITuple']);
            // `(a,b)` gets transformed into `ITuple<[a, b]>`
            return formatTuple((typeDef.sub
                .map((sub) => formatType(definitions, sub.type, imports))));
        }
        case types_1.TypeDefInfo.VecFixed: {
            if (typeDef.ext.type === 'u8') {
                imports_1.setImports(definitions, imports, ['Raw']);
                // `[u8, 32]` gets transformed into Raw
                return 'Raw';
            }
            imports_1.setImports(definitions, imports, ['Vec']);
            return formatVec(formatType(definitions, typeDef.ext.type, imports));
        }
        case types_1.TypeDefInfo.BTreeMap: {
            imports_1.setImports(definitions, imports, ['BTreeMap']);
            const [keyDef, valDef] = typeDef.sub;
            return formatBTreeMap(formatType(definitions, keyDef.type, imports), formatType(definitions, valDef.type, imports));
        }
        case types_1.TypeDefInfo.BTreeSet: {
            imports_1.setImports(definitions, imports, ['BTreeSet']);
            const valDef = typeDef.sub;
            return formatBTreeSet(formatType(definitions, valDef.type, imports));
        }
        case types_1.TypeDefInfo.Result: {
            imports_1.setImports(definitions, imports, ['Result']);
            const [okDef, errorDef] = typeDef.sub;
            return formatResult(formatType(definitions, okDef.type, imports), formatType(definitions, errorDef.type, imports));
        }
        default: {
            throw new Error(`Cannot format ${type}.`);
        }
    }
}
exports.formatType = formatType;
/**
 * Indent a string with `n` spaces before.
 */
/** @internal */
function indent(n, char = ' ') {
    return function (str) {
        return `${char.repeat(n)}${str}`;
    };
}
exports.indent = indent;
