contract Brainz is ERC20Burnable, BitMonstersAddon {
    using RngLibrary for Rng;

    mapping (uint => uint) public tokenIdToTimestamp;
    Rng private rng = RngLibrary.newRng();

    constructor() ERC20("Brainz", "BRAINZ") {
    }

    function adminMint(address addr, uint256 count) external onlyAdmin {
        _mint(addr, count * 1 ether);
    }

    function adminBurn(address addr, uint256 count) external onlyAdmin {
        _burn(addr, count * 1 ether);
    }

    /**
     * Claims all Brainz from all staked Bit Monsters the caller owns.
     */
    function claimBrainz() external {
        uint count = bitMonsters.balanceOf(msg.sender);
        uint total = 0;

        for (uint i = 0; i < count; ++i) {
            uint tokenId = bitMonsters.tokenOfOwnerByIndex(msg.sender, i);
            uint rewards = calculateRewards(tokenId);
            if (rewards > 0) {
                tokenIdToTimestamp[tokenId] = block.timestamp - ((block.timestamp - tokenIdToTimestamp[tokenId]) % 86400);
            }
            total += rewards;
        }

        _mint(msg.sender, total);
    }

    function rewardRate(BitMonster memory m) public pure returns (uint) {
        return ((m.genesis ? 2 : 1) * (m.special != Special.NONE ? 2 : 1) + (m.superYield ? 1 : 0)) * 1 ether;
    }

    /**
     * Returns the amount of pending Brainz the caller can currently claim.
     */
    function calculateRewards(uint tokenId) public view returns (uint) {
        BitMonster memory m = bitMonsters.getBitMonster(tokenId);
        uint nDays = (block.timestamp - tokenIdToTimestamp[tokenId]) / 86400;

        return rewardRate(m) * nDays;
    }

    /**
     * Tracks the Bit Monster with the given tokenId for reward calculation.
     */
    function register(uint tokenId) external onlyAdmin {
        require(tokenIdToTimestamp[tokenId] == 0, "already staked");
        tokenIdToTimestamp[tokenId] = block.timestamp;
    }

    /**
     * Stake your Brainz a-la OSRS Duel Arena.
     *
     * 50% chance of multiplying your Brainz by 1.9x rounded up.
     * 50% chance of losing everything you stake.
     */
    function stake(uint count) external returns (bool won) {
        require(count > 0, "Must stake at least one BRAINZ");
        require(balanceOf(msg.sender) >= count, "You don't have that many tokens");

        Rng memory rn = rng;

        if (rn.generate(0, 1) == 0) {
            _mint(msg.sender, (count - count / 10) * 1 ether);
            won = true;
        }
        else {
            _burn(msg.sender, count * 1 ether);
            won = false;
        }

        rng = rn;
    }
}
/**
 * @title The contract for the Brainz token and staking. At the moment, these can only be obtained by staking Bit Monsters.
 */
function claimBrainz() external {
    uint count = bitMonsters.balanceOf(msg.sender);
    uint total = 0;

    for (uint i = 0; i < count; ++i) {
        uint tokenId = bitMonsters.tokenOfOwnerByIndex(msg.sender, i);
        uint rewards = calculateRewards(tokenId);
        if (rewards > 0) {
            tokenIdToTimestamp[tokenId] = block.timestamp - ((block.timestamp - tokenIdToTimestamp[tokenId]) % 86400);
        }
        total += rewards;
    }

    _mint(msg.sender, total);
}

/**
 * Claims all Brainz from all staked Bit Monsters the caller owns.
 */