import BN from "bn.js";
import { factory } from "../src/logging";
const log = factory.getLogger("scripts.util");
export async function getBatchExchange(artifacts) {
    const BatchExchange = artifacts.require("BatchExchange");
    return BatchExchange.deployed();
}
export async function getOwl(artifacts) {
    const TokenOWL = artifacts.require("TokenOWL");
    const batchExchange = await getBatchExchange(artifacts);
    const owlAddress = await batchExchange.feeToken();
    return TokenOWL.at(owlAddress);
}
async function setAllowance(token, account, amount, exchange) {
    log.info(`Approving BatchExchange at ${exchange.address} for ${amount} token ${token.address}`);
    await token.approve(exchange.address, amount, {
        from: account,
    });
}
export async function setAllowances(users, amount, exchange, tokens) {
    for (let i = 0; i < users.length; i++) {
        for (let j = 0; j < tokens.length; j++) {
            await setAllowance(tokens[j], users[i], amount, exchange);
        }
    }
}
export async function addTokens(tokenAddresses, account, exchange, owl) {
    // Get amount of required OWL for listing all tokens
    const feeForAddingToken = await exchange.FEE_FOR_LISTING_TOKEN_IN_OWL();
    const totalFees = feeForAddingToken.mul(new BN(tokenAddresses.length));
    // Ensure the user has enough OWL balance
    const balanceOfOWL = await owl.balanceOf(account);
    if (totalFees.gt(balanceOfOWL)) {
        throw new Error("Insufficient balance of fee token to complete request.");
    }
    // Set OWL allowance if necessary
    const allowanceOfOWL = await owl.allowance(account, exchange.address);
    if (totalFees.gt(allowanceOfOWL)) {
        // TODO: Only approve the minimum required amount totalFees.sub(allowanceOfOWL)
        await setAllowance(owl, account, totalFees, exchange);
    }
    // List all tokens (if not listed previously)
    const tokens = [];
    for (const tokenAddress of tokenAddresses) {
        const isTokenListed = await exchange.hasToken(tokenAddress);
        if (!isTokenListed) {
            await exchange.addToken(tokenAddress, { from: account });
            log.info(`Successfully added token ${tokenAddress}`);
        }
        else {
            log.info(`The token ${tokenAddress} was already added`);
        }
        // Get token information
        const tokenId = await exchange.tokenAddressToIdMap(tokenAddress);
        tokens.push({
            id: tokenId.toNumber(),
            address: tokenAddress,
        });
    }
    // Return token information
    return tokens;
}
export async function deleteOrders(orderIds, accounts, exchange) {
    log.info(`Canceling ${orderIds.length} orders for ${accounts.length} users`);
    for (let i = 0; i < orderIds.length; i++) {
        const orderId = orderIds[i];
        const account = accounts[i];
        const cancelReceipt = await exchange.cancelOrders([orderId], {
            from: account,
        });
        const events = cancelReceipt.logs
            .map((log) => log.event)
            .join(", ");
        log.info(`Canceled/Deleted order ${orderId} for user {${account}}. Emitted events: ${events}`);
    }
}
export async function submitSolution(name, batchId, solution, solverAddress, exchange) {
    log.info(`Submit "${name}":
  - Objective value: ${solution.objectiveValue}
  - Touched orders: ${solution.touchedorderIds.join(", ")}
  - Volumes: ${solution.volumes.join(", ")}
  - Prices: ${solution.prices.join(", ")}
  - Token ids for prices: ${solution.tokenIdsForPrice.join(", ")}`);
    const objectiveValue = await exchange.submitSolution(batchId, 1, solution.owners, solution.touchedorderIds, solution.volumes, solution.prices, solution.tokenIdsForPrice, { from: solverAddress });
    log.info(`Transaction for ${name}: ${objectiveValue.tx}`);
}
export async function getBatchId(exchange) {
    const batchId = await exchange.getCurrentBatchId();
    return batchId.toNumber();
}
export async function createMintableToken(artifacts) {
    const ERC20Mintable = artifacts.require("ERC20Mintable");
    return ERC20Mintable.new();
}
async function mintToken(token, account, amount, minter) {
    log.info(`Mint ${amount} of token ${token.address} for user ${account}. Using ${minter} as the minter`);
    await token.mint(account, amount, { from: minter });
}
export async function mintTokens(tokens, users, amount, minter) {
    for (let i = 0; i < tokens.length; i++) {
        for (let j = 0; j < users.length; j++) {
            await mintToken(tokens[i], users[j], amount, minter);
        }
    }
}
async function mintOwlForUser(owl, user, amount, minter) {
    log.info(`Mint ${amount} OWL to ${user}`);
    return owl.mintOWL(user, amount, { from: minter });
}
export async function mintOwl(owl, users, amount, minter) {
    for (let i = 0; i < users.length; i++) {
        await mintOwlForUser(owl, users[i], amount, minter);
    }
}
