import { findCryptoCurrencyById, getCryptoCurrencyById } from "./currencies";
import asatokens from "./data/asa";
import bep20tokens from "./data/bep20";
import cardanoNativeTokens from "./data/cardanoNative";
import erc20tokens from "./data/erc20";
import esdttokens from "./data/esdt";
import polygonTokens from "./data/polygon-erc20";
import stellarTokens from "./data/stellar";
import casperTokens from "./data/casper";
import trc10tokens from "./data/trc10";
import trc20tokens from "./data/trc20";
const emptyArray = [];
const tokensArray = [];
const tokensArrayWithDelisted = [];
const tokensByCryptoCurrency = Object.create(null);
const tokensByCryptoCurrencyWithDelisted = Object.create(null);
const tokensById = Object.create(null);
const tokensByTicker = Object.create(null);
const tokensByAddress = Object.create(null);
const tokensByCurrencyAddress = Object.create(null);
const tokenListHashes = new Set();
addTokens(erc20tokens.map(convertERC20));
addTokens(polygonTokens.map(convertERC20));
addTokens(trc10tokens.map(convertTRONTokens("trc10")));
addTokens(trc20tokens.map(convertTRONTokens("trc20")));
addTokens(bep20tokens.map(convertBEP20));
addTokens(asatokens.map(convertAlgorandASATokens));
addTokens(esdttokens.map(convertElrondESDTTokens));
addTokens(cardanoNativeTokens.map(convertCardanoNativeTokens));
addTokens(stellarTokens.map(convertStellarTokens));
addTokens(casperTokens.map(convertCasperTokens));
const defaultTokenListOptions = Object.assign(Object.create(null), {
    withDelisted: false,
});
export function createTokenHash(token) {
    return token
        ? `${token.id}${token.contractAddress}${token.delisted}${token.disableCountervalue}${token.ticker}${token.ledgerSignature}`
        : "";
}
const __clearObject = (obj) => {
    for (const key in obj) {
        delete obj[key];
    }
};
export function __clearAllLists() {
    tokensArray.length = 0;
    tokensArrayWithDelisted.length = 0;
    __clearObject(tokensByCryptoCurrency);
    __clearObject(tokensByCryptoCurrencyWithDelisted);
    __clearObject(tokensById);
    __clearObject(tokensByTicker);
    __clearObject(tokensByAddress);
    __clearObject(tokensByCurrencyAddress);
    tokenListHashes.clear();
}
export function listTokens(options) {
    const { withDelisted } = { ...defaultTokenListOptions, ...options };
    return withDelisted ? tokensArrayWithDelisted : tokensArray;
}
export function listTokensForCryptoCurrency(currency, options) {
    const { withDelisted } = { ...defaultTokenListOptions, ...options };
    if (withDelisted) {
        return tokensByCryptoCurrencyWithDelisted[currency.id] || emptyArray;
    }
    return tokensByCryptoCurrency[currency.id] || emptyArray;
}
export function listTokenTypesForCryptoCurrency(currency) {
    return listTokensForCryptoCurrency(currency).reduce((acc, cur) => {
        const tokenType = cur.tokenType;
        if (acc.indexOf(tokenType) < 0) {
            return [...acc, tokenType];
        }
        return acc;
    }, []);
}
export function findTokenByTicker(ticker) {
    return tokensByTicker[ticker];
}
export function findTokenById(id) {
    return tokensById[id];
}
let deprecatedDisplayed = false;
export function findTokenByAddress(address) {
    if (!deprecatedDisplayed) {
        deprecatedDisplayed = true;
        console.warn("findTokenByAddress is deprecated. use findTokenByAddressInCurrency");
    }
    return tokensByAddress[address.toLowerCase()];
}
export function findTokenByAddressInCurrency(address, currencyId) {
    return tokensByCurrencyAddress[currencyId + ":" + address.toLowerCase()];
}
export const hasTokenId = (id) => id in tokensById;
export function getTokenById(id) {
    const currency = findTokenById(id);
    if (!currency) {
        throw new Error(`token with id "${id}" not found`);
    }
    return currency;
}
function comparePriority(a, b) {
    return Number(!!b.disableCountervalue) - Number(!!a.disableCountervalue);
}
function removeTokenFromArray(array, tokenId) {
    if (array && array.length > 0) {
        const index = array.findIndex(currentToken => currentToken && currentToken.id === tokenId);
        if (index === -1)
            return array;
        return array.splice(index, 1);
    }
}
function removeTokenFromRecord(record, key) {
    tokenListHashes.delete(record[key]);
    delete record[key];
}
function removeTokenFromAllLists(token) {
    const { id, contractAddress, parentCurrency, ticker } = token;
    const lowCaseContract = contractAddress.toLowerCase();
    removeTokenFromRecord(tokensById, id);
    removeTokenFromRecord(tokensByCurrencyAddress, parentCurrency.id + ":" + lowCaseContract);
    removeTokenFromRecord(tokensByAddress, lowCaseContract);
    removeTokenFromRecord(tokensByTicker, ticker);
    removeTokenFromArray(tokensArray, id);
    removeTokenFromArray(tokensArrayWithDelisted, id);
    removeTokenFromArray(tokensByCryptoCurrency[parentCurrency.id], id);
    removeTokenFromArray(tokensByCryptoCurrencyWithDelisted[parentCurrency.id], id);
}
export function addTokens(list) {
    list.forEach(token => {
        if (!token)
            return;
        const tokenHash = createTokenHash(token);
        if (tokenListHashes.has(tokenHash))
            return;
        const { id, contractAddress, parentCurrency, delisted, ticker } = token;
        if (tokensById[id])
            removeTokenFromAllLists(token);
        const lowCaseContract = contractAddress.toLowerCase();
        if (!delisted)
            tokensArray.push(token);
        tokensArrayWithDelisted.push(token);
        tokensById[id] = token;
        if (!tokensByTicker[ticker] || comparePriority(token, tokensByTicker[ticker]) > 0) {
            tokensByTicker[ticker] = token;
        }
        tokensByAddress[lowCaseContract] = token;
        tokensByCurrencyAddress[parentCurrency.id + ":" + lowCaseContract] = token;
        if (!(parentCurrency.id in tokensByCryptoCurrency)) {
            tokensByCryptoCurrency[parentCurrency.id] = [];
        }
        if (!(parentCurrency.id in tokensByCryptoCurrencyWithDelisted)) {
            tokensByCryptoCurrencyWithDelisted[parentCurrency.id] = [];
        }
        if (!delisted)
            tokensByCryptoCurrency[parentCurrency.id].push(token);
        tokensByCryptoCurrencyWithDelisted[parentCurrency.id].push(token);
        tokenListHashes.add(tokenHash);
    });
}
export function convertERC20([parentCurrencyId, token, ticker, magnitude, name, ledgerSignature, contractAddress, disableCountervalue, delisted,]) {
    const parentCurrency = findCryptoCurrencyById(parentCurrencyId);
    if (!parentCurrency) {
        return;
    }
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: parentCurrencyId + "/erc20/" + token,
        ledgerSignature,
        contractAddress,
        parentCurrency,
        tokenType: "erc20",
        name,
        ticker,
        delisted,
        disableCountervalue: !!parentCurrency.isTestnetFor || !!disableCountervalue,
        units: [
            {
                name,
                code: ticker,
                magnitude,
            },
        ],
    });
}
export function convertBEP20([parentCurrencyId, token, ticker, magnitude, name, ledgerSignature, contractAddress, disableCountervalue, delisted,]) {
    const parentCurrency = findCryptoCurrencyById(parentCurrencyId);
    if (!parentCurrency) {
        return;
    }
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: parentCurrencyId + "/bep20/" + token,
        ledgerSignature,
        contractAddress,
        parentCurrency,
        tokenType: "bep20",
        name,
        ticker,
        delisted,
        disableCountervalue: !!parentCurrency.isTestnetFor || !!disableCountervalue,
        units: [
            {
                name,
                code: ticker,
                magnitude,
            },
        ],
    });
}
function convertAlgorandASATokens([id, abbr, name, contractAddress, precision, enableCountervalues,]) {
    const parentCurrency = getCryptoCurrencyById("algorand");
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: `algorand/asa/${id}`,
        contractAddress,
        parentCurrency,
        tokenType: "asa",
        name,
        ticker: abbr,
        disableCountervalue: !enableCountervalues,
        units: [
            {
                name,
                code: abbr,
                magnitude: precision,
            },
        ],
    });
}
function convertTRONTokens(type) {
    return ([id, abbr, name, contractAddress, precision, delisted, ledgerSignature]) => {
        const parentCurrency = getCryptoCurrencyById("tron");
        return Object.assign(Object.create(null), {
            type: "TokenCurrency",
            id: `tron/${type}/${id}`,
            contractAddress,
            parentCurrency,
            tokenType: type,
            name,
            ticker: abbr,
            delisted,
            disableCountervalue: false,
            ledgerSignature,
            units: [
                {
                    name,
                    code: abbr,
                    magnitude: precision,
                },
            ],
        });
    };
}
function convertElrondESDTTokens([ticker, identifier, decimals, signature, name, disableCountervalue,]) {
    const ELROND_ESDT_CONTRACT = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u";
    const parentCurrency = getCryptoCurrencyById("elrond");
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: `elrond/esdt/${identifier}`,
        contractAddress: ELROND_ESDT_CONTRACT,
        ledgerSignature: signature,
        parentCurrency,
        tokenType: "esdt",
        disableCountervalue,
        name,
        ticker,
        units: [
            {
                name,
                code: name,
                magnitude: decimals,
            },
        ],
    });
}
function convertCardanoNativeTokens([parentCurrencyId, policyId, assetName, name, ticker, decimals, delisted, disableCountervalue,]) {
    const assetId = policyId + assetName;
    const parentCurrency = getCryptoCurrencyById(parentCurrencyId);
    if (!parentCurrency) {
        return;
    }
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: `${parentCurrencyId}/native/${assetId}`,
        contractAddress: assetId,
        parentCurrency,
        tokenType: "native",
        name,
        ticker,
        delisted,
        disableCountervalue,
        units: [
            {
                name,
                code: ticker,
                magnitude: decimals,
            },
        ],
    });
}
function convertStellarTokens([assetCode, assetIssuer, assetType, name, precision, enableCountervalues,]) {
    const parentCurrency = getCryptoCurrencyById("stellar");
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: `stellar/asset/${assetCode}:${assetIssuer}`,
        contractAddress: assetIssuer,
        parentCurrency,
        tokenType: assetType,
        name,
        ticker: assetCode,
        disableCountervalue: !enableCountervalues,
        units: [
            {
                name,
                code: assetCode,
                magnitude: precision,
            },
        ],
    });
}
function convertCasperTokens([assetCode, assetIssuer, assetType, name, precision, enableCountervalues,]) {
    const parentCurrency = getCryptoCurrencyById("casper");
    return Object.assign(Object.create(null), {
        type: "TokenCurrency",
        id: `casper/asset/${assetCode}:${assetIssuer}`,
        contractAddress: assetIssuer,
        parentCurrency,
        tokenType: assetType,
        name,
        ticker: assetCode,
        disableCountervalue: !enableCountervalues,
        units: [
            {
                name,
                code: assetCode,
                magnitude: precision,
            },
        ],
    });
}
