contract XBSEpoolthree is LPTokenWrapper, IRewardDistributionRecipient {
    IERC20 public xbse = IERC20(0x69861C005D72C77b9E7EF68F4dF6E7f09d6310eb);  //used as reward
    uint256 public  EPOCH_REWARD = 15624980000000000000000;
    uint256 public  DURATION = 2 weeks;
    uint256 public  phase = 0;
    uint256 public currentEpochReward = EPOCH_REWARD;
    uint256 public totalAccumulatedReward = 0;
    uint8 public currentEpoch = 0;
    uint256 public starttime = 1605200400; //Date and time (GMT): Thursday, November 12, 2020 5:00:00 PM
    uint256 public periodFinish = 0;
    uint256 public rewardRate = 0;
    uint256 public lastUpdateTime;
    uint256 public rewardPerTokenStored;
    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public rewards;

    event RewardAdded(uint256 reward);
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event WithdrawnToTreasury(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);

    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = lastTimeRewardApplicable();
        if (account != address(0)) {
            rewards[account] = earned(account);
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }

    function lastTimeRewardApplicable() public view returns (uint256) {
        return Math.min(block.timestamp, periodFinish);
    }

    function rewardPerToken() public view returns (uint256) {
        if (totalSupply() == 0) {
            return rewardPerTokenStored;
        }
        return
            rewardPerTokenStored.add(
                lastTimeRewardApplicable()
                    .sub(lastUpdateTime)
                    .mul(rewardRate)
                    .mul(1e18)
                    .div(totalSupply())
            );
    }

    function earned(address account) public view returns (uint256) {
        return
            balanceOf(account)
                .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                .div(1e18)
                .add(rewards[account]);
    }

    // stake visibility is public as overriding LPTokenWrapper's stake() function
    function stake(uint256 amount) public updateReward(msg.sender) checkNextEpoch checkStart {
        require(amount > 0, "Cannot stake 0");
        super.stake(amount);
        emit Staked(msg.sender, amount);
    }

    function withdraw(uint256 amount)
        public
        updateReward(msg.sender)
        checkStart
    {
        require(amount > 0, "Cannot withdraw 0");
        uint256 _fee = amount.div(1000).mul(100);
        uint256 _amount = amount.sub(_fee);
        super.withdraw(_amount);
        super.withdrawTreasury(_fee);
        emit Withdrawn(msg.sender, _amount);
        emit WithdrawnToTreasury(msg.sender,_fee);
    }

    function exit() external {
        withdraw(balanceOf(msg.sender));
        getReward();
    }

    function totalRewardsClaimed() public view returns (uint256) {
        return totalAccumulatedReward;
    }

    function getReward() public updateReward(msg.sender) checkStart {
        uint256 reward = earned(msg.sender);
        if (reward > 0) {
            rewards[msg.sender] = 0;
            xbse.safeTransfer(msg.sender, reward);
            totalAccumulatedReward = totalAccumulatedReward.add(reward);
            emit RewardPaid(msg.sender, reward);
        }
    }

    modifier checkNextEpoch() {
        
        if (block.timestamp >= periodFinish) {
            phase++;
            if(phase == 2) {
                DURATION = 4 weeks;
                currentEpochReward = 15625040000000000000000;
                rewardRate = currentEpochReward.div(DURATION);
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(currentEpochReward);
            } else if (phase == 3){
                DURATION = 6 weeks;
                currentEpochReward = 11718780000000000000000;
                rewardRate = currentEpochReward.div(DURATION);
                periodFinish = block.timestamp.add(DURATION);
                 emit RewardAdded(currentEpochReward);
            } else if (phase == 4){
                DURATION = 8 weeks;
                currentEpochReward = 7812400000000000000000;
                rewardRate = currentEpochReward.div(DURATION);
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(currentEpochReward);
            }
            else if (phase > 4){
                uint256 balance = xbse.balanceOf(address(this));
                require(balance > 0,"No more rewards in the contract");
                 xbse.safeTransfer(owner(), balance);
            }
        }
        _;
    }

    function afterItEnds() public onlyRewardDistribution {
        require(block.timestamp > periodFinish);
        uint256 balance = xbse.balanceOf(address(this));
        require(balance > 0,"No more rewards in the contract");
        xbse.safeTransfer(msg.sender, balance);
    }

    modifier checkStart() {
        require(block.timestamp > starttime, "not start");
        _;
    }

    function notifyRewardAmount()
        external
        onlyRewardDistribution
        updateReward(address(0))
    {
       /* if (totalAccumulatedReward.add(currentEpochReward) > TOTAL_REWARD) {
            currentEpochReward = TOTAL_REWARD.sub(totalAccumulatedReward); // limit total reward
        }*/

        rewardRate = currentEpochReward.div(DURATION);
        //totalAccumulatedReward = totalAccumulatedReward.add(currentEpochReward);
        phase++;
        lastUpdateTime = block.timestamp;
        periodFinish = starttime.add(DURATION);
        emit RewardAdded(currentEpochReward);
    }
}
function stake(uint256 amount) public updateReward(msg.sender) checkNextEpoch checkStart {
        require(amount > 0, "Cannot stake 0");
        super.stake(amount);
        emit Staked(msg.sender, amount);
    }
// stake visibility is public as overriding LPTokenWrapper's stake() function