"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Deserializer = void 0;
const enums_1 = require("../enums");
const errors_1 = require("../errors");
const identities_1 = require("../identities");
const utils_1 = require("../utils");
const types_1 = require("./types");
// Reference: https://github.com/ArkEcosystem/AIPs/blob/master/AIPS/aip-11.md
class Deserializer {
    static applyV1Compatibility(transaction) {
        transaction.secondSignature = transaction.secondSignature || transaction.signSignature;
        transaction.typeGroup = enums_1.TransactionTypeGroup.Core;
        if (transaction.type === enums_1.TransactionType.Vote && transaction.senderPublicKey) {
            transaction.recipientId = identities_1.Address.fromPublicKey(transaction.senderPublicKey, transaction.network);
        }
        else if (transaction.type === enums_1.TransactionType.MultiSignature &&
            transaction.asset &&
            transaction.asset.multiSignatureLegacy) {
            transaction.asset.multiSignatureLegacy.keysgroup = transaction.asset.multiSignatureLegacy.keysgroup.map((k) => (k.startsWith("+") ? k : `+${k}`));
        }
    }
    static deserialize(serialized, options = {}) {
        const data = {};
        const buff = this.getByteBuffer(serialized);
        this.deserializeCommon(data, buff);
        const instance = types_1.TransactionTypeFactory.create(data);
        this.deserializeVendorField(instance, buff);
        // Deserialize type specific parts
        instance.deserialize(buff);
        this.deserializeSignatures(data, buff);
        if (data.version) {
            if (options.acceptLegacyVersion ||
                options.disableVersionCheck ||
                utils_1.isSupportedTransactionVersion(data.version)) {
                if (data.version === 1) {
                    this.applyV1Compatibility(data);
                }
            }
            else {
                throw new errors_1.TransactionVersionError(data.version);
            }
        }
        instance.serialized = buff.getResult();
        return instance;
    }
    static deserializeCommon(transaction, buf) {
        // buf.skip(1); // Skip 0xFF marker
        buf.jump(1); // Skip 0xFF marker
        transaction.version = buf.readUInt8();
        transaction.network = buf.readUInt8();
        if (transaction.version === 1) {
            transaction.type = buf.readUInt8();
            transaction.timestamp = buf.readUInt32LE();
        }
        else {
            transaction.typeGroup = buf.readUInt32LE();
            transaction.type = buf.readUInt16LE();
            transaction.nonce = utils_1.BigNumber.make(buf.readBigUInt64LE());
        }
        transaction.senderPublicKey = buf.readBuffer(33).toString("hex");
        transaction.fee = utils_1.BigNumber.make(buf.readBigUInt64LE().toString());
        transaction.amount = utils_1.BigNumber.ZERO;
    }
    static deserializeVendorField(transaction, buf) {
        const vendorFieldLength = buf.readUInt8();
        if (vendorFieldLength > 0) {
            if (transaction.hasVendorField()) {
                const vendorFieldBuffer = buf.readBuffer(vendorFieldLength);
                transaction.data.vendorField = vendorFieldBuffer.toString("utf8");
            }
            else {
                buf.jump(vendorFieldLength);
            }
        }
    }
    static deserializeSignatures(transaction, buf) {
        if (transaction.version === 1) {
            this.deserializeECDSA(transaction, buf);
        }
        else {
            this.deserializeSchnorrOrECDSA(transaction, buf);
        }
    }
    static deserializeSchnorrOrECDSA(transaction, buf) {
        if (this.detectSchnorr(buf)) {
            this.deserializeSchnorr(transaction, buf);
        }
        else {
            this.deserializeECDSA(transaction, buf);
        }
    }
    static deserializeECDSA(transaction, buf) {
        const currentSignatureLength = () => {
            buf.jump(1);
            const length = buf.readUInt8();
            buf.jump(-2);
            return length + 2;
        };
        // Signature
        if (buf.getRemainderLength()) {
            const signatureLength = currentSignatureLength();
            transaction.signature = buf.readBuffer(signatureLength).toString("hex");
        }
        const beginningMultiSignature = () => {
            const marker = buf.readUInt8();
            buf.jump(-1);
            return marker === 255;
        };
        // Second Signature
        if (buf.getRemainderLength() && !beginningMultiSignature()) {
            const secondSignatureLength = currentSignatureLength();
            transaction.secondSignature = buf.readBuffer(secondSignatureLength).toString("hex");
        }
        // Multi Signatures
        if (buf.getRemainderLength() && beginningMultiSignature()) {
            buf.jump(1);
            const multiSignature = buf.readBuffer(buf.getRemainderLength()).toString("hex");
            transaction.signatures = [multiSignature];
        }
        if (buf.getRemainderLength()) {
            throw new errors_1.InvalidTransactionBytesError("signature buffer not exhausted");
        }
    }
    static deserializeSchnorr(transaction, buf) {
        const canReadNonMultiSignature = () => {
            return (buf.getRemainderLength() && (buf.getRemainderLength() % 64 === 0 || buf.getRemainderLength() % 65 !== 0));
        };
        if (canReadNonMultiSignature()) {
            transaction.signature = buf.readBuffer(64).toString("hex");
        }
        if (canReadNonMultiSignature()) {
            transaction.secondSignature = buf.readBuffer(64).toString("hex");
        }
        if (buf.getRemainderLength()) {
            if (buf.getRemainderLength() % 65 === 0) {
                transaction.signatures = [];
                const count = buf.getRemainderLength() / 65;
                const publicKeyIndexes = {};
                for (let i = 0; i < count; i++) {
                    const multiSignaturePart = buf.readBuffer(65).toString("hex");
                    const publicKeyIndex = parseInt(multiSignaturePart.slice(0, 2), 16);
                    if (!publicKeyIndexes[publicKeyIndex]) {
                        publicKeyIndexes[publicKeyIndex] = true;
                    }
                    else {
                        throw new errors_1.DuplicateParticipantInMultiSignatureError();
                    }
                    transaction.signatures.push(multiSignaturePart);
                }
            }
            else {
                throw new errors_1.InvalidTransactionBytesError("signature buffer not exhausted");
            }
        }
    }
    static detectSchnorr(buf) {
        const remaining = buf.getRemainderLength();
        // `signature` / `secondSignature`
        if (remaining === 64 || remaining === 128) {
            return true;
        }
        // `signatures` of a multi signature transaction (type != 4)
        if (remaining % 65 === 0) {
            return true;
        }
        // only possiblity left is a type 4 transaction with and without a `secondSignature`.
        if ((remaining - 64) % 65 === 0 || (remaining - 128) % 65 === 0) {
            return true;
        }
        return false;
    }
    static getByteBuffer(serialized) {
        if (!(serialized instanceof Buffer)) {
            serialized = Buffer.from(serialized, "hex");
        }
        return new utils_1.ByteBuffer(serialized);
    }
}
exports.Deserializer = Deserializer;
//# sourceMappingURL=deserializer.js.map