"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidTypeError = exports.InvalidArrayError = exports.LengthMismatchError = exports.BytesSizeMismatchError = exports.ArrayLengthMismatchError = exports.ZeroDataError = exports.DataSizeTooSmallError = void 0;
exports.decode = decode;
exports.encode = encode;
exports.encodePacked = encodePacked;
exports.format = format;
exports.from = from;
const abitype = require("abitype");
const Address = require("./Address.js");
const Bytes = require("./Bytes.js");
const Errors = require("./Errors.js");
const Hex = require("./Hex.js");
const internal = require("./internal/abiParameters.js");
const Cursor = require("./internal/cursor.js");
const Solidity = require("./Solidity.js");
function decode(parameters, data, options = {}) {
    const { as = 'Array', checksumAddress = false } = options;
    const bytes = typeof data === 'string' ? Bytes.fromHex(data) : data;
    const cursor = Cursor.create(bytes);
    if (Bytes.size(bytes) === 0 && parameters.length > 0)
        throw new ZeroDataError();
    if (Bytes.size(bytes) && Bytes.size(bytes) < 32)
        throw new DataSizeTooSmallError({
            data: typeof data === 'string' ? data : Hex.fromBytes(data),
            parameters: parameters,
            size: Bytes.size(bytes),
        });
    let consumed = 0;
    const values = as === 'Array' ? [] : {};
    for (let i = 0; i < parameters.length; ++i) {
        const param = parameters[i];
        cursor.setPosition(consumed);
        const [data, consumed_] = internal.decodeParameter(cursor, param, {
            checksumAddress,
            staticPosition: 0,
        });
        consumed += consumed_;
        if (as === 'Array')
            values.push(data);
        else
            values[param.name ?? i] = data;
    }
    return values;
}
function encode(parameters, values, options) {
    const { checksumAddress = false } = options ?? {};
    if (parameters.length !== values.length)
        throw new LengthMismatchError({
            expectedLength: parameters.length,
            givenLength: values.length,
        });
    const preparedParameters = internal.prepareParameters({
        checksumAddress,
        parameters: parameters,
        values: values,
    });
    const data = internal.encode(preparedParameters);
    if (data.length === 0)
        return '0x';
    return data;
}
function encodePacked(types, values) {
    if (types.length !== values.length)
        throw new LengthMismatchError({
            expectedLength: types.length,
            givenLength: values.length,
        });
    const data = [];
    for (let i = 0; i < types.length; i++) {
        const type = types[i];
        const value = values[i];
        data.push(encodePacked.encode(type, value));
    }
    return Hex.concat(...data);
}
(function (encodePacked) {
    function encode(type, value, isArray = false) {
        if (type === 'address') {
            const address = value;
            Address.assert(address);
            return Hex.padLeft(address.toLowerCase(), isArray ? 32 : 0);
        }
        if (type === 'string')
            return Hex.fromString(value);
        if (type === 'bytes')
            return value;
        if (type === 'bool')
            return Hex.padLeft(Hex.fromBoolean(value), isArray ? 32 : 1);
        const intMatch = type.match(Solidity.integerRegex);
        if (intMatch) {
            const [_type, baseType, bits = '256'] = intMatch;
            const size = Number.parseInt(bits, 10) / 8;
            return Hex.fromNumber(value, {
                size: isArray ? 32 : size,
                signed: baseType === 'int',
            });
        }
        const bytesMatch = type.match(Solidity.bytesRegex);
        if (bytesMatch) {
            const [_type, size] = bytesMatch;
            if (Number.parseInt(size, 10) !== (value.length - 2) / 2)
                throw new BytesSizeMismatchError({
                    expectedSize: Number.parseInt(size, 10),
                    value: value,
                });
            return Hex.padRight(value, isArray ? 32 : 0);
        }
        const arrayMatch = type.match(Solidity.arrayRegex);
        if (arrayMatch && Array.isArray(value)) {
            const [_type, childType] = arrayMatch;
            const data = [];
            for (let i = 0; i < value.length; i++) {
                data.push(encode(childType, value[i], true));
            }
            if (data.length === 0)
                return '0x';
            return Hex.concat(...data);
        }
        throw new InvalidTypeError(type);
    }
    encodePacked.encode = encode;
})(encodePacked || (exports.encodePacked = encodePacked = {}));
function format(parameters) {
    return abitype.formatAbiParameters(parameters);
}
function from(parameters) {
    if (Array.isArray(parameters) && typeof parameters[0] === 'string')
        return abitype.parseAbiParameters(parameters);
    if (typeof parameters === 'string')
        return abitype.parseAbiParameters(parameters);
    return parameters;
}
class DataSizeTooSmallError extends Errors.BaseError {
    constructor({ data, parameters, size, }) {
        super(`Data size of ${size} bytes is too small for given parameters.`, {
            metaMessages: [
                `Params: (${abitype.formatAbiParameters(parameters)})`,
                `Data:   ${data} (${size} bytes)`,
            ],
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.DataSizeTooSmallError'
        });
    }
}
exports.DataSizeTooSmallError = DataSizeTooSmallError;
class ZeroDataError extends Errors.BaseError {
    constructor() {
        super('Cannot decode zero data ("0x") with ABI parameters.');
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.ZeroDataError'
        });
    }
}
exports.ZeroDataError = ZeroDataError;
class ArrayLengthMismatchError extends Errors.BaseError {
    constructor({ expectedLength, givenLength, type, }) {
        super(`Array length mismatch for type \`${type}\`. Expected: \`${expectedLength}\`. Given: \`${givenLength}\`.`);
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.ArrayLengthMismatchError'
        });
    }
}
exports.ArrayLengthMismatchError = ArrayLengthMismatchError;
class BytesSizeMismatchError extends Errors.BaseError {
    constructor({ expectedSize, value, }) {
        super(`Size of bytes "${value}" (bytes${Hex.size(value)}) does not match expected size (bytes${expectedSize}).`);
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.BytesSizeMismatchError'
        });
    }
}
exports.BytesSizeMismatchError = BytesSizeMismatchError;
class LengthMismatchError extends Errors.BaseError {
    constructor({ expectedLength, givenLength, }) {
        super([
            'ABI encoding parameters/values length mismatch.',
            `Expected length (parameters): ${expectedLength}`,
            `Given length (values): ${givenLength}`,
        ].join('\n'));
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.LengthMismatchError'
        });
    }
}
exports.LengthMismatchError = LengthMismatchError;
class InvalidArrayError extends Errors.BaseError {
    constructor(value) {
        super(`Value \`${value}\` is not a valid array.`);
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.InvalidArrayError'
        });
    }
}
exports.InvalidArrayError = InvalidArrayError;
class InvalidTypeError extends Errors.BaseError {
    constructor(type) {
        super(`Type \`${type}\` is not a valid ABI Type.`);
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'AbiParameters.InvalidTypeError'
        });
    }
}
exports.InvalidTypeError = InvalidTypeError;
//# sourceMappingURL=AbiParameters.js.map