"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.concatArrays = exports.hasher = exports.calculateAlgoTxid = exports.mpDecode = exports.mpEncode = exports.algo_check_expect_empty = exports.algo_check_expect_block_out_of_range = exports.algo_ensure_data = exports.txIdToHexNo0x = exports.txIdToHex = exports.bufAddToCBufAdd = exports.base64ToText = exports.hexToBase64 = exports.base64ToHex = exports.hexToBase32 = exports.base32ToHex = exports.bytesToHex = exports.INVALIDADDRESERROR = void 0;
const msgpack = __importStar(require("algo-msgpack-with-bigint"));
const algosdk_1 = __importDefault(require("algosdk"));
const utils_1 = require("./utils");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const base32 = require("base32.js");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sha512_256 = require("js-sha512").sha512_256;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////// Errors //////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const INVALIDADDRESERROR = (address) => {
    return new Error(`Invalid address: ${address}`);
};
exports.INVALIDADDRESERROR = INVALIDADDRESERROR;
// export const INVALIDPUBKEYPAIRERROR = (algoKeyPair: IAlgoHexAddress) => {
//    return new Error(`Invalid keypair: ${algoKeyPair.publicKey} -  ${algoKeyPair.checksum}`);
// };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////// Code ////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////
// Bytes <-> Hex //
///////////////////
// prefix helper
function bufferEntryToHexString(bytePair) {
    let toAdd = bytePair.toString(16);
    if (toAdd.length === 1) {
        toAdd = "0" + toAdd;
    }
    return toAdd;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
    // should be: buffer and uint are unsigned
    if (bytes instanceof Buffer) {
        const hex = [];
        for (const x of bytes.entries()) {
            hex.push(bufferEntryToHexString(x[1]));
        }
        return hex.join("");
    }
    else if (bytes instanceof Uint8Array) {
        const hex = [];
        for (let i = 0; i < bytes.length; i++) {
            const current = bytes[i];
            hex.push((current >>> 4).toString(16));
            hex.push((current & 0xf).toString(16));
        }
        return hex.join("");
    }
    else if (bytes.type === "Buffer") {
        const hex = [];
        for (const x of bytes.data) {
            hex.push(bufferEntryToHexString(x));
        }
        return hex.join("");
    }
    else {
        return "";
    }
}
exports.bytesToHex = bytesToHex;
////////////////////
// Base32 <-> Hex //
////////////////////
/**
 * Algos base32 to hex string
 * @param data
 * @returns
 */
function base32ToHex(data) {
    // old base 32 decoding
    const decoder = new base32.Decoder({ type: "rfc4648" });
    const out = decoder.write(data).finalize();
    return out.toString("hex");
}
exports.base32ToHex = base32ToHex;
function hexToBase32(hex) {
    // old base 32 encoding
    const encoder = new base32.Encoder({ type: "rfc4648" });
    if (typeof hex === "string") {
        const bufHex = Buffer.from(hex, "hex");
        const base32Out = encoder.write(bufHex).finalize();
        return base32Out;
    }
    else {
        const bufHex = Buffer.from(bytesToHex(hex), "hex");
        const base32Out = encoder.write(bufHex).finalize();
        return base32Out;
    }
}
exports.hexToBase32 = hexToBase32;
////////////////////
// Base64 <-> Hex //
////////////////////
/**
 * For note decoding
 * @param rawdata
 * @returns
 */
function base64ToHex(rawdata) {
    const data = Buffer.from(rawdata, "base64").toString("binary");
    let hexData = "";
    for (let i = 0; i < data.length; i++) {
        const hex = data.charCodeAt(i).toString(16);
        hexData += hex.length === 2 ? hex : "0" + hex;
    }
    return hexData;
}
exports.base64ToHex = base64ToHex;
function hexToBase64(hex) {
    if (typeof hex === "string") {
        return Buffer.from(hex, "hex").toString("base64");
    }
    else {
        return Buffer.from(bytesToHex(hex), "hex").toString("base64");
    }
}
exports.hexToBase64 = hexToBase64;
/////////////////////
// Base64 <-> Text //
/////////////////////
/**
 *
 * @dev used for transaction note parsing
 * @param rawdata
 * @returns
 */
function base64ToText(rawdata) {
    const buff = Buffer.from(rawdata, "base64");
    return buff.toString("ascii");
}
exports.base64ToText = base64ToText;
//////////////////////////
// Algo address <-> Hex //
//////////////////////////
// export function addressToHex(address: string): IAlgoHexAddress {
//    if (!algosdk.isValidAddress(address)) {
//       throw INVALIDADDRESERROR(address);
//    }
//    const dad = algosdk.decodeAddress(address);
//    const publicKey = Buffer.from(dad.publicKey).toString("hex");
//    const checksum = Buffer.from(dad.checksum).toString("hex");
//    return {
//       publicKey: prefix0x(publicKey),
//       checksum: prefix0x(checksum),
//    } as IAlgoHexAddress;
// }
// export function hexToAddress(algoKeyPair: IAlgoHexAddress): string {
//    const conc = unPrefix0x(algoKeyPair.publicKey) + unPrefix0x(algoKeyPair.checksum);
//    const concBytes = hexToBytes(conc);
//    const address = hexToBase32(concBytes);
//    if (!algosdk.isValidAddress(address)) {
//       throw INVALIDPUBKEYPAIRERROR(algoKeyPair);
//    }
//    return address;
// }
///////////////////////////////////////
// Algo address <-> address+checksum //
///////////////////////////////////////
// address checksum
// https://emn178.github.io/online-tools/sha512_256.html hash SHA512/256
// https://github.com/algorand/go-algorand-sdk/blob/develop/types/address.go
/**
 * buffer address to buffer address with checksum
 */
function bufAddToCBufAdd(address) {
    if (address.length === 32) {
        const checksumString = sha512_256.arrayBuffer(address);
        const checksum = checksumString.slice(28, 32);
        return Buffer.concat([address, Buffer.from(checksum)], 36);
    }
    else if (address.length === 36) {
        return address;
    }
    else {
        throw new Error(`Not a valid address buffer: ${address}`);
    }
}
exports.bufAddToCBufAdd = bufAddToCBufAdd;
/////////////////////////////////
// Algo transaction id <-> Hex //
/////////////////////////////////
function txIdToHex(txid) {
    return (0, utils_1.prefix0x)(base32ToHex(txid));
}
exports.txIdToHex = txIdToHex;
function txIdToHexNo0x(txid) {
    return base32ToHex(txid);
}
exports.txIdToHexNo0x = txIdToHexNo0x;
//////////////////////////
// Algo cert id <-> Hex //
//////////////////////////
// export function certToInCert(cert: IAlgoCert): IAlgoIndexerCert {
//    // eslint-disable-next-line @typescript-eslint/no-unused-vars
//    const { prop, ...rest } = cert;
//    const newCert: IAlgoIndexerCert = { prop: { dig: "", encdig: "", oprop: "" }, ...rest } as IAlgoIndexerCert;
//    newCert.prop.dig = bytesToHex(cert.prop.dig);
//    newCert.prop.encdig = bytesToHex(cert.prop.encdig);
//    newCert.prop.oprop = bytesToHex(cert.prop.oprop);
//    return newCert;
// }
////////////////////////////
//// MCC Error handling ////
////////////////////////////
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function algo_ensure_data(data) {
    const error_codes = [400, 401, 404, 500, 503];
    if (error_codes.includes(data.status)) {
        throw (0, utils_1.MccError)(data);
    }
}
exports.algo_ensure_data = algo_ensure_data;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function algo_check_expect_block_out_of_range(data) {
    if (data.status === 404) {
        return true;
    }
    else {
        return false;
    }
}
exports.algo_check_expect_block_out_of_range = algo_check_expect_block_out_of_range;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function algo_check_expect_empty(data) {
    if (data.status === 404) {
        return true;
    }
    else {
        return false;
    }
}
exports.algo_check_expect_empty = algo_check_expect_empty;
////////////////////////////////////////////
//// Message Pack encoding and decoding ////
////////////////////////////////////////////
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mpEncode(obj) {
    // enable the canonical option
    const options = { sortKeys: true };
    return msgpack.encode(obj, options);
}
exports.mpEncode = mpEncode;
function mpDecode(buffer) {
    return msgpack.decode(buffer);
}
exports.mpDecode = mpDecode;
////////////////////////////////////////////
//// Light (Non-Archival) block updates ////
////////////////////////////////////////////
// Reduce code
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function calculateAlgoTxid(gh, gen, stib) {
    const t = stib.txn;
    // Manually add gh/gen to construct a correct transaction object
    if (stib.hgi) {
        t.gh = gh;
        t.gen = gen;
    }
    else {
        t.gh = gh;
        t.gen = "";
    }
    // Modify the fields as needed
    // we have to ensure all addresses are buffers with checksum
    t.snd = bufAddToCBufAdd(t.snd);
    if (t.rcv)
        t.rcv = bufAddToCBufAdd(t.rcv);
    if (t.arcv)
        t.arcv = bufAddToCBufAdd(t.arcv);
    const stxn = {
        txn: algosdk_1.default.Transaction.from_obj_for_encoding(t),
    };
    if ("sig" in stib)
        stxn.sig = stib.sig;
    if ("lsig" in stib)
        stxn.lsig = stib.lsig;
    if ("msig" in stib)
        stxn.msig = stib.msig;
    if ("sgnr" in stib)
        stxn.sgnr = stib.sgnr;
    return stxn.txn.txID();
}
exports.calculateAlgoTxid = calculateAlgoTxid;
function hasher(data) {
    const tohash = concatArrays(Buffer.from("STIB"), new Uint8Array(data));
    return new Uint8Array(sha512_256.array(tohash));
}
exports.hasher = hasher;
function concatArrays(...arrs) {
    const size = arrs.reduce((sum, arr) => sum + arr.length, 0);
    const c = new Uint8Array(size);
    let offset = 0;
    for (let i = 0; i < arrs.length; i++) {
        c.set(arrs[i], offset);
        offset += arrs[i].length;
    }
    return c;
}
exports.concatArrays = concatArrays;
//# sourceMappingURL=algoUtils.js.map