contract FRTPoolPHZT is LPTokenWrapper, Ownable {
    IERC20 public REWARD_TOKEN = IERC20(
        0x561695f9556AF49C2B22F71e55b7b0B6F673834f
    ); // EDIT_ME: Reward token --> FRT
    uint256 public constant DURATION = 7 days;

    uint256 public origTotalSupply = 0;

    uint256 public initreward = 0;
    uint256 public starttime = 1606611600; // EDIT_ME: 2020-11-29UTC:01:00+00:00
    uint256 public periodFinish = 0; //The exact time when reward rate is reduced.
    uint256 public rewardRate = 0;
    uint256 public lastUpdateTime;
    uint256 public rewardPerTokenStored;
    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public rewards; //Saves the amount of earned rewards. As the calculations always change the values of RewardperToken

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

    constructor() public {
        origTotalSupply = REWARD_TOKEN.totalSupply();
       
        // 3 pools, ( 0.080 / 0.080 / 0.085) Initial reward percentage that affectes the reward rate
        // => (0.32 / 0.32 / 0.34)  Shares given to each pool, related to the total amount of Tokens=> total 98%
        initreward = origTotalSupply.mul(10).div(1000);

        notifyRewardAmount(initreward);
        //renounceOwnership(); Ownership will pass to timelock
    }

    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);
    }

    // Acording to the rewardrate and the total supply of tokens staked.
    // If total supply is 0, it affects the earned rewards -> rewardPerToken() - userRewardPerTokenPaid =(0).
    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)
        checkhalve
        checkStart
    {
        require(amount > 0, 'Cannot stake 0');
        super.stake(amount);
        emit Staked(msg.sender, amount);
    }

    function withdraw(uint256 amount) public updateReward(msg.sender) {
        require(amount > 0, 'Cannot withdraw 0');
        super.withdraw(amount);
        emit Withdrawn(msg.sender, amount);
    }

    //Withdraws and gets rewards
    function exit() external {
        withdraw(balanceOf(msg.sender));
        getReward();
    }

    function getReward() public updateReward(msg.sender) checkhalve {
        uint256 reward = earned(msg.sender);
        if (reward > 0) {
            rewards[msg.sender] = 0;

            // scaled by nowTotalSupply / origTotalSupply when claiming reward
            uint256 nowTotalSupply = REWARD_TOKEN.totalSupply();
            reward = reward.mul(nowTotalSupply).div(origTotalSupply);

            REWARD_TOKEN.safeTransfer(msg.sender, reward);

            emit RewardPaid(msg.sender, reward);
        }
    }

    modifier checkhalve() {
        if (block.timestamp >= periodFinish) {
            initreward = initreward.mul(75).div(100); // yield reduces to 75% every week

            rewardRate = initreward.div(DURATION);
            periodFinish = block.timestamp.add(DURATION);
            emit RewardAdded(initreward);
        }
        _;
    }
    modifier checkStart() {
        require(block.timestamp > starttime, 'not start');
        _;
    }

    function notifyRewardAmount(uint256 reward)
        private
        updateReward(address(0))
    {
        rewardRate = reward.div(DURATION);

        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp.add(DURATION);
        emit RewardAdded(reward);
    }

    //Affects the reward rate.
    function changeInitReward(uint256 reward) public onlyOwner {
        initreward = reward;
        notifyRewardAmount(initreward); //Will evaluate if its worth to do.
    }

    //In case community decides to move the Rewards to another contract.
    function recoverRewards(address account) public onlyOwner {
        uint256 reward = REWARD_TOKEN.balanceOf(address(this));
        REWARD_TOKEN.safeTransfer(account, reward);
    }




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

//Withdraws and gets rewards