contract Minter is BitMonstersAddon {
    using RngLibrary for Rng;

    uint256 constant public WHITELIST_PER = 6;

    address payable private payHere;
    // 0 == "not whitelisted"
    // 1000 + x == "whitelisted and x whitelists left"
    mapping (address => uint256) public whitelist;
    MintingState public mintingState;

    bool[9] public mintedSpecials;
    uint private mintedSpecialsCount = 0;

    Rng private rng;

    constructor(address payable paymentAddress, address[] memory whitelistedAddrs) {
        payHere = paymentAddress;
        whitelist[paymentAddress] = WHITELIST_PER + 1000;
        for (uint i = 0; i < whitelistedAddrs.length; ++i) {
            whitelist[whitelistedAddrs[i]] = WHITELIST_PER + 1000;
        }
        rng = RngLibrary.newRng();
    }

    /**
     * Adds someone to the whitelist.
     */
    function addToWhitelist(address[] memory addrs) external onlyAdmin {
        for (uint i = 0; i < addrs.length; ++i) {
            if (whitelist[addrs[i]] == 0) {
                whitelist[addrs[i]] = WHITELIST_PER + 1000;
            }
        }
    }

    /**
     * Removes someone from the whitelist.
     */
    function removeFromWhitelist(address addr) external onlyAdmin {
        delete whitelist[addr];
    }

    /**
     * Generates a random Bit Monster.
     *
     * 9/6666 bit monsters will be special, which means they're prebuilt images instead of assembled from the 6 attributes a normal Bit Monster has.
     * All 9 specials are guaranteed to be minted by the time all 6666 Bit Monsters are minted.
     * The chance of a special at each roll is roughly even, although there's a slight dip in chance in the mid-range.
     */
    function generateBitMonster(Rng memory rn, bool[9] memory ms) internal returns (BitMonster memory) {
        uint count = bitMonsters.totalSupply();

        int ub = 6666 - int(count) - 1 - (90 - int(mintedSpecialsCount) * 10);
        if (ub < 0) {
            ub = 0;
        }

        BitMonster memory m;
        if (rn.generate(0, uint(ub)) <= (6666 - count) / 666) {
            m = BitMonsterGen.generateSpecialBitMonster(rn, ms);
        }
        else {
            m = BitMonsterGen.generateUnspecialBitMonster(rn);
        }

        if (m.special != Special.NONE) {
            mintedSpecialsCount++;
        }
        rng = rn;
        return m;
    }

    /**
     * Sets the MintingState. See MintingState above.
     * By default, no one is allowed to mint. This function must be called before any Bit Monsters can be minted.
     */
    function setMintingState(MintingState state) external onlyAdmin {
        mintingState = state;
    }

    /**
     * Mints some Bit Monsters.
     *
     * @param count The number of Bit Monsters to mint. Must be >= 1 and <= 10.
     *              You must send 0.06 ETH for each Bit Monster you want to mint.
     */
    function mint(uint count) external payable {
        require(count >= 1 && count <= 10, "Count must be >=1 and <=10");
        require(!Address.isContract(msg.sender), "Contracts cannot mint");
        require(mintingState != MintingState.NotAllowed, "Minting is not allowed atm");

        if (mintingState == MintingState.WhitelistOnly) {
            require(whitelist[msg.sender] >= 1000 + count, "Not enough whitelisted mints");
            whitelist[msg.sender] -= count;
        }

        require(msg.value == count * 0.06 ether, "Send exactly 0.06 ETH for each mint");

        Rng memory rn = rng;
        bool[9] memory ms = mintedSpecials;

        for (uint i = 0; i < count; ++i) {
            bitMonsters.createBitMonster(generateBitMonster(rn, ms), msg.sender);
        }

        rng = rn;
        mintedSpecials = ms;

        Address.sendValue(payHere, msg.value);
    }

    /**
     * Mint for a giveaway.
     */
    function giveawayMint(address[] memory winners) external onlyAdmin {
        Rng memory rn = rng;

        for (uint i = 0; i < winners.length; ++i) {
            bitMonsters.createBitMonster(BitMonsterGen.generateUnspecialBitMonster(rn), winners[i]);
        }

        rng = rn;
    }
}
function mint(uint count) external payable {
    require(count >= 1 && count <= 10, "Count must be >=1 and <=10");
    require(!Address.isContract(msg.sender), "Contracts cannot mint");
    require(mintingState != MintingState.NotAllowed, "Minting is not allowed atm");

    if (mintingState == MintingState.WhitelistOnly) {
        require(whitelist[msg.sender] >= 1000 + count, "Not enough whitelisted mints");
        whitelist[msg.sender] -= count;
    }

    require(msg.value == count * 0.06 ether, "Send exactly 0.06 ETH for each mint");

    Rng memory rn = rng;
    bool[9] memory ms = mintedSpecials;

    for (uint i = 0; i < count; ++i) {
        bitMonsters.createBitMonster(generateBitMonster(rn, ms), msg.sender);
    }

    rng = rn;
    mintedSpecials = ms;

    Address.sendValue(payHere, msg.value);
}

/**
 * Mints some Bit Monsters.
 *
 * @param count The number of Bit Monsters to mint. Must be >= 1 and <= 10.
 *              You must send 0.06 ETH for each Bit Monster you want to mint.
 */