contract BrrToken is ERC20, Ownable {
    using SafeMath for uint256;

    // BRR

    uint256 public lastBrrTime;

    uint256 public totalBrred;

    uint256 public constant BRR_RATE = 4; // brr rate per day (4%)

    uint256 public constant BRR_REWARD = 1;

    // REWARDS

    uint256 public constant POOL_REWARD = 48;

    uint256 public lastRewardTime;

    uint256 public rewardPool;

    mapping (address => uint256) public claimedRewards;

    mapping (address => uint256) public unclaimedRewards;

    // mapping of top holders that owner update before paying out rewards
    mapping (uint256 => address) public topHolder;

    // maximum of top topHolder
    uint256 public constant MAX_TOP_HOLDERS = 50;

    uint256 internal totalTopHolders;

    // Pause for allowing tokens to only become transferable at the end of sale

    address public pauser;

    bool public paused;

    // UNISWAP

    ERC20 internal WETH = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

    IUniswapV2Factory public uniswapFactory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);

    address public uniswapPool;

    // MODIFIERS

    modifier onlyPauser() {
        require(pauser == _msgSender(), "BrrToken: caller is not the pauser.");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "BrrToken: paused");
        _;
    }

    modifier when3DaysBetweenLastSnapshot() {
        require((now - lastRewardTime) >= 3 days, "BrrToken: not enough days since last snapshot taken.");
        _;
    }

    // EVENTS

    event PoolBrred(address brrer, uint256 brrAmount, uint256 newTotalSupply, uint256 newUniswapPoolSupply, uint256 userReward, uint256 newPoolReward);

    event PayoutSnapshotTaken(uint256 totalTopHolders, uint256 totalPayout, uint256 snapshot);

    event PayoutClaimed(address indexed topHolderAddress, uint256 claimedReward);

    constructor()
    public
    Ownable()
    ERC20("Brr Token", "BRR")
    {
        /*
        
            -- March 25, 2020 --
            Senate approves $2.2 trillion coronavirus bill aimed at slowing economic free fall
            
        */
        uint256 initialSupply = 2_200_000_000_000 * 1e18;
        _mint(msg.sender, initialSupply);
        setPauser(msg.sender);
        paused = true;
    }

    function setUniswapPool() external onlyOwner {
        require(uniswapPool == address(0), "BrrToken: pool already created");
        uniswapPool = uniswapFactory.createPair(address(WETH), address(this));
    }

    // PAUSE

    function setPauser(address newPauser) public onlyOwner {
        require(newPauser != address(0), "BrrToken: pauser is the zero address.");
        pauser = newPauser;
    }

    function unpause() external onlyPauser {
        paused = false;

        // Start brring
        lastBrrTime = now;
        lastRewardTime = now;
        rewardPool = 0;
    }

    // TOKEN TRANSFER HOOK

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);
        require(!paused || msg.sender == pauser, "BrrToken: token transfer while paused and not pauser role.");
    }

    // BRRERS

    function getInfoFor(address addr) public view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) {
        return (
            balanceOf(addr),
            claimedRewards[addr],
            balanceOf(uniswapPool),
            _totalSupply,
            totalBrred,
            getBrrAmount(),
            lastBrrTime,
            lastRewardTime,
            rewardPool
        );
    }

    function brrPool() external {
        uint256 brrAmount = getBrrAmount();
        require(brrAmount >= 1 * 1e18, "brrPool: min brr amount not reached.");

        // Reset last brr time
        lastBrrTime = now;

        uint256 userReward = brrAmount.mul(BRR_REWARD).div(100);
        uint256 poolReward = brrAmount.mul(POOL_REWARD).div(100);
        uint256 finalBrr = brrAmount.sub(userReward).sub(poolReward);

        _totalSupply = _totalSupply.sub(finalBrr);
        _balances[uniswapPool] = _balances[uniswapPool].sub(brrAmount);

        totalBrred = totalBrred.add(finalBrr);
        rewardPool = rewardPool.add(poolReward);

        _balances[msg.sender] = _balances[msg.sender].add(userReward);

        IUniswapV2Pair(uniswapPool).sync();

        emit PoolBrred(msg.sender, brrAmount, _totalSupply, balanceOf(uniswapPool), userReward, poolReward);
    }

    function getBrrAmount() public view returns (uint256) {
        if (paused) return 0;
        uint256 timeBetweenLastBrr = now - lastBrrTime;
        uint256 tokensInUniswapPool = balanceOf(uniswapPool);
        uint256 dayInSeconds = 1 days;
        return (tokensInUniswapPool.mul(BRR_RATE)
            .mul(timeBetweenLastBrr))
            .div(dayInSeconds)
            .div(100);
    }

    // Rewards

    function updateTopHolders(address[] calldata holders) external onlyOwner when3DaysBetweenLastSnapshot {
        totalTopHolders = holders.length < MAX_TOP_HOLDERS ? holders.length : MAX_TOP_HOLDERS;

        // Calculate payout and take snapshot
        uint256 toPayout = rewardPool.div(totalTopHolders);
        uint256 totalPayoutSent = rewardPool;
        for (uint256 i = 0; i < totalTopHolders; i++) {
            unclaimedRewards[holders[i]] = unclaimedRewards[holders[i]].add(toPayout);
        }

        // Reset rewards pool
        lastRewardTime = now;
        rewardPool = 0;

        emit PayoutSnapshotTaken(totalTopHolders, totalPayoutSent, now);
    }

    function claimRewards() external {
        require(unclaimedRewards[msg.sender] > 0, "BrrToken: nothing left to claim.");

        uint256 unclaimedReward = unclaimedRewards[msg.sender];
        unclaimedRewards[msg.sender] = 0;
        claimedRewards[msg.sender] = claimedRewards[msg.sender].add(unclaimedReward);
        _balances[msg.sender] = _balances[msg.sender].add(unclaimedReward);

        emit PayoutClaimed(msg.sender, unclaimedReward);
    }
}
/*      
 
 ___.                    _____.__                                   
 \_ |________________  _/ ____\__| ____ _____    ____   ____  ____  
 | __ \_  __ \_  __ \ \   __\|  |/    \\__  \  /    \_/ ___\/ __ \ 
 | \_\ \  | \/|  | \/  |  |  |  |   |  \/ __ \|   |  \  \__\  ___/ 
 |___  /__|   |__|     |__|  |__|___|  (____  /___|  /\___  >___  >
 \/                              \/     \/     \/     \/    \/ 
 
 
 --------------------------------------------------------------------
 https://www.brr.finance
 --------------------------------------------------------------------
 
 
 */
function getInfoFor(address addr) public view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) {
    return (
        balanceOf(addr),
        claimedRewards[addr],
        balanceOf(uniswapPool),
        _totalSupply,
        totalBrred,
        getBrrAmount(),
        lastBrrTime,
        lastRewardTime,
        rewardPool
    );
}

// BRRERS