"use strict";
// Copyright 2017-2020 @polkadot/metadata 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 create_1 = require("../../codec/create");
const Struct_1 = __importDefault(require("../../codec/Struct"));
const U8aFixed_1 = __importDefault(require("../../codec/U8aFixed"));
/**
 * @name CallIndex
 * @description
 * A wrapper around the `[sectionIndex, methodIndex]` value that uniquely identifies a method
 */
class CallIndex extends U8aFixed_1.default {
    constructor(registry, value) {
        super(registry, value, 16);
    }
}
exports.CallIndex = CallIndex;
/**
 * @name Call
 * @description
 * Extrinsic function descriptor, as defined in
 * {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
 */
class Call extends Struct_1.default {
    constructor(registry, value, meta) {
        const decoded = Call.decodeCall(registry, value, meta);
        super(registry, {
            callIndex: CallIndex,
            args: Struct_1.default.with(decoded.argsDef)
        }, decoded);
        this._meta = decoded.meta;
    }
    /**
     * Decode input to pass into constructor.
     *
     * @param value - Value to decode, one of:
     * - hex
     * - Uint8Array
     * - {@see DecodeMethodInput}
     * @param _meta - Metadata to use, so that `injectMethods` lookup is not
     * necessary.
     * @internal
     */
    static decodeCall(registry, value = new Uint8Array(), _meta) {
        if (util_1.isHex(value) || util_1.isU8a(value)) {
            return Call.decodeCallViaU8a(registry, util_1.u8aToU8a(value), _meta);
        }
        else if (util_1.isObject(value) && value.callIndex && value.args) {
            return Call.decodeCallViaObject(registry, value, _meta);
        }
        throw new Error(`Call: Cannot decode value '${value}' of type ${typeof value}`);
    }
    /** @internal */
    static decodeCallViaObject(registry, value, _meta) {
        // we only pass args/methodsIndex out
        const { args, callIndex } = value;
        // Get the correct lookupIndex
        const lookupIndex = callIndex instanceof CallIndex
            ? callIndex.toU8a()
            : callIndex;
        // Find metadata with callIndex
        const meta = _meta || registry.findMetaCall(lookupIndex).meta;
        return {
            args,
            argsDef: Call.getArgsDef(registry, meta),
            meta,
            callIndex
        };
    }
    /** @internal */
    static decodeCallViaU8a(registry, value, _meta) {
        // We need 2 bytes for the callIndex
        const callIndex = new Uint8Array(2);
        callIndex.set(value.subarray(0, 2), 0);
        // Find metadata with callIndex
        const meta = _meta || registry.findMetaCall(callIndex).meta;
        return {
            args: value.subarray(2),
            argsDef: Call.getArgsDef(registry, meta),
            callIndex,
            meta
        };
    }
    // If the extrinsic function has an argument of type `Origin`, we ignore it
    static filterOrigin(meta) {
        // FIXME should be `arg.type !== Origin`, but doesn't work...
        return meta
            ? meta.args.filter(({ type }) => type.toString() !== 'Origin')
            : [];
    }
    /**
     * Get a mapping of `argument name -> argument type` for the function, from
     * its metadata.
     *
     * @param meta - The function metadata used to get the definition.
     * @internal
     */
    static getArgsDef(registry, meta) {
        return Call.filterOrigin(meta).reduce((result, { name, type }) => {
            const Type = create_1.getTypeClass(registry, create_1.getTypeDef(type.toString()));
            result[name.toString()] = Type;
            return result;
        }, {});
    }
    /**
     * @description The arguments for the function call
     */
    get args() {
        // FIXME This should return a Struct instead of an Array
        return [...this.get('args').values()];
    }
    /**
     * @description The argument definitions
     */
    get argsDef() {
        return Call.getArgsDef(this.registry, this.meta);
    }
    /**
     * @description The encoded `[sectionIndex, methodIndex]` identifier
     */
    get callIndex() {
        return this.get('callIndex').toU8a();
    }
    /**
     * @description The encoded data
     */
    get data() {
        return this.get('args').toU8a();
    }
    /**
     * @description `true` if the `Origin` type is on the method (extrinsic method)
     */
    get hasOrigin() {
        const firstArg = this.meta.args[0];
        return !!firstArg && firstArg.type.toString() === 'Origin';
    }
    /**
     * @description The [[FunctionMetadata]]
     */
    get meta() {
        return this._meta;
    }
    /**
     * @description Returns the name of the method
     */
    get methodName() {
        return this.registry.findMetaCall(this.callIndex).method;
    }
    /**
     * @description Returns the module containing the method
     */
    get sectionName() {
        return this.registry.findMetaCall(this.callIndex).section;
    }
    /**
     * @description Returns the base runtime type name for this instance
     */
    toRawType() {
        return 'Call';
    }
}
exports.default = Call;
