"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 util_1 = require("@polkadot/util");
const Bytes_1 = __importDefault(require("./Bytes"));
// we unwrap the type here, turning into an output usable for createType
/** @internal */
function unwrapStorageType(type) {
    if (type.isPlain) {
        return type.asPlain.toString();
    }
    else if (type.isDoubleMap) {
        return type.asDoubleMap.value.toString();
    }
    const map = type.asMap;
    if (map.linked.isTrue) {
        return `(${map.value.toString()}, Linkage<${map.key.toString()}>)`;
    }
    return map.value.toString();
}
exports.unwrapStorageType = unwrapStorageType;
/**
 * @name StorageKey
 * @description
 * A representation of a storage key (typically hashed) in the system. It can be
 * constructed by passing in a raw key or a StorageEntry with (optional) arguments.
 */
class StorageKey extends Bytes_1.default {
    constructor(registry, value, override = {}) {
        const { key, method, section } = StorageKey.decodeStorageKey(value);
        super(registry, key);
        this._meta = StorageKey.getMeta(value);
        this._method = override.method || method;
        this._outputType = StorageKey.getType(value);
        this._section = override.section || section;
    }
    /** @internal */
    static decodeStorageKey(value) {
        if (value instanceof StorageKey) {
            return {
                key: value,
                method: value.method,
                section: value.section
            };
        }
        else if (!value || util_1.isString(value) || util_1.isU8a(value)) {
            // let Bytes handle these inputs
            return { key: value };
        }
        else if (util_1.isFunction(value)) {
            return {
                key: value(),
                method: value.method,
                section: value.section
            };
        }
        else if (Array.isArray(value)) {
            const [fn, ...arg] = value;
            util_1.assert(util_1.isFunction(fn), 'Expected function input for key construction');
            return {
                key: fn(...arg),
                method: fn.method,
                section: fn.section
            };
        }
        throw new Error(`Unable to convert input ${value} to StorageKey`);
    }
    static getMeta(value) {
        if (value instanceof StorageKey) {
            return value.meta;
        }
        else if (util_1.isFunction(value)) {
            return value.meta;
        }
        else if (Array.isArray(value)) {
            const [fn] = value;
            return fn.meta;
        }
        return undefined;
    }
    static getType(value) {
        if (value instanceof StorageKey) {
            return value.outputType;
        }
        else if (util_1.isFunction(value)) {
            return unwrapStorageType(value.meta.type);
        }
        else if (Array.isArray(value)) {
            const [fn] = value;
            if (fn.meta) {
                return unwrapStorageType(fn.meta.type);
            }
        }
        return undefined;
    }
    /**
     * @description The metadata or `undefined` when not available
     */
    get meta() {
        return this._meta;
    }
    /**
     * @description The key method or `undefined` when not specified
     */
    get method() {
        return this._method;
    }
    /**
     * @description The output type, `null` when not available
     */
    get outputType() {
        return this._outputType;
    }
    /**
     * @description The key section or `undefined` when not specified
     */
    get section() {
        return this._section;
    }
    /**
     * @description Sets the meta for this key
     */
    setMeta(meta) {
        this._meta = meta;
        return this;
    }
    /**
     * @description Sets the output type for this storage key
     */
    setOutputType(outputType) {
        this._outputType = outputType;
        return this;
    }
}
exports.default = StorageKey;
