"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 util_crypto_1 = require("@polkadot/util-crypto");
const Compact_1 = __importDefault(require("./Compact"));
const Raw_1 = __importDefault(require("./Raw"));
const utils_1 = require("./utils");
class BTreeMap extends Map {
    constructor(registry, keyType, valType, rawValue) {
        const KeyClass = utils_1.typeToConstructor(registry, keyType);
        const ValClass = utils_1.typeToConstructor(registry, valType);
        super(BTreeMap.decodeBTreeMap(registry, KeyClass, ValClass, rawValue));
        this.registry = registry;
        this._KeyClass = KeyClass;
        this._ValClass = ValClass;
    }
    /**
     * Decode input to pass into constructor.
     *
     * @param KeyClass - Type of the map key
     * @param ValClass - Type of the map value
     * @param value - Value to decode, one of:
     * - null
     * - undefined
     * - hex
     * - Uint8Array
     * - Map<any, any>, where both key and value types are either
     *   constructors or decodeable values for their types.
     * @param jsonMap
     * @internal
     */
    static decodeBTreeMap(registry, KeyClass, ValClass, value) {
        if (!value) {
            return new Map();
        }
        else if (util_1.isHex(value)) {
            return BTreeMap.decodeBTreeMap(registry, KeyClass, ValClass, util_1.hexToU8a(value));
        }
        else if (util_1.isU8a(value)) {
            return BTreeMap.decodeBTreeMapFromU8a(registry, KeyClass, ValClass, util_1.u8aToU8a(value));
        }
        else if (value instanceof Map) {
            return BTreeMap.decodeBTreeMapFromMap(registry, KeyClass, ValClass, value);
        }
        throw new Error('BTreeMap: cannot decode type');
    }
    /** @internal */
    static decodeBTreeMapFromU8a(registry, KeyClass, ValClass, u8a) {
        const output = new Map();
        const [offset, length] = Compact_1.default.decodeU8a(u8a);
        const types = [];
        for (let i = 0; i < length.toNumber(); i++) {
            types.push(KeyClass, ValClass);
        }
        const values = utils_1.decodeU8a(registry, u8a.subarray(offset), types);
        for (let i = 0; i < values.length; i += 2) {
            output.set(values[i], values[i + 1]);
        }
        return output;
    }
    /** @internal */
    static decodeBTreeMapFromMap(registry, KeyClass, ValClass, value) {
        const output = new Map();
        value.forEach((v, k) => {
            let key, val;
            try {
                key = (k instanceof KeyClass) ? k : new KeyClass(registry, k);
                val = (v instanceof ValClass) ? v : new ValClass(registry, v);
            }
            catch (error) {
                console.error('Failed to decode BTreeMap key or value:', error.message);
                throw error;
            }
            output.set(key, val);
        });
        return output;
    }
    static with(keyType, valType) {
        return class extends BTreeMap {
            constructor(registry, value) {
                super(registry, keyType, valType, value);
            }
        };
    }
    /**
     * @description The length of the value when encoded as a Uint8Array
     */
    get encodedLength() {
        let len = Compact_1.default.encodeU8a(this.size).length;
        this.forEach((v, k) => {
            len += v.encodedLength + k.encodedLength;
        });
        return len;
    }
    /**
     * @description Returns a hash of the value
     */
    get hash() {
        return new Raw_1.default(this.registry, util_crypto_1.blake2AsU8a(this.toU8a(), 256));
    }
    /**
     * @description Checks if the value is an empty value
     */
    get isEmpty() {
        return this.size === 0;
    }
    /**
     * @description Compares the value of the input to see if there is a match
     */
    eq(other) {
        return utils_1.compareMap(this, other);
    }
    /**
     * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation
     */
    toHex() {
        return util_1.u8aToHex(this.toU8a());
    }
    /**
     * @description Converts the Object to JSON, typically used for RPC transfers
     */
    toJSON() {
        const json = {};
        this.forEach((v, k) => {
            json[k.toString()] = v.toJSON();
        });
        return json;
    }
    /**
     * @description Returns the base runtime type name for this instance
     */
    toRawType() {
        return `BTreeMap<${new this._KeyClass(this.registry).toRawType()},${new this._ValClass(this.registry).toRawType()}>`;
    }
    /**
     * @description Returns the string representation of the value
     */
    toString() {
        return JSON.stringify(this.toJSON());
    }
    /**
     * @description Encodes the value as a Uint8Array as per the SCALE specifications
     * @param isBare true when the value has none of the type-specific prefixes (internal)
     */
    toU8a(isBare) {
        const encoded = new Array();
        if (!isBare) {
            encoded.push(Compact_1.default.encodeU8a(this.size));
        }
        this.forEach((v, k) => {
            encoded.push(k.toU8a(isBare), v.toU8a(isBare));
        });
        return util_1.u8aConcat(...encoded);
    }
}
exports.default = BTreeMap;
