contract UniswapReward is LPTokenWrapper{
    using SafeERC20 for IERC20;

    IERC20 public _pros = IERC20(0x306Dd7CD66d964f598B4D2ec92b5a9B275D7fEb3);
    address public _teamWallet = 0xde7a7E8Db75D56B095263c63ecB4CfE8157ee3dd;
    address public _rewardPool = 0xde7a7E8Db75D56B095263c63ecB4CfE8157ee3dd;
    address public _allpool = 0xC682bD99eE552B6f7d931aFee2A9425806e155E9;
    
    
    int128 private dayNums;
    int128 baseReward = 8000;
    // uint256 public constant DURATION = 7 days;
    // should do this ? 
    uint256 public  DURATION = 1 days;
    uint256 public _initReward = 0;
    
    uint256 public base_ = 30*10e3;
    uint256 public rate_forReward = 1;
    uint256 public base_Rate_Reward = 100;
    //init amount should be ?
    
    
    uint256 public _startTime =  now + 365 days;
    uint256 public _periodFinish = 0;
    uint256 public _rewardRate = 0;
    uint256 public _lastUpdateTime;
    uint256 public _rewardPerTokenStored;

    uint256 public _teamRewardRate = 0;
    uint256 public _poolRewardRate = 0;
    uint256 public _baseRate = 10000;
    uint256 public _punishTime = 10 days;

    mapping(address => uint256) public _userRewardPerTokenPaid;
    mapping(address => uint256) public _rewards;
    mapping(address => uint256) public _lastStakedTime;

    bool public _hasStart = false;

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


    modifier updateReward(address account) {
        _rewardPerTokenStored = rewardPerToken();
        _lastUpdateTime = lastTimeRewardApplicable();
        if (account != address(0)) {
            _rewards[account] = earned(account);
            _userRewardPerTokenPaid[account] = _rewardPerTokenStored;
        }
        _;
    }
    
    function set_DURATION(uint256 _DURATION) public onlyGovernance{
        DURATION = _DURATION; 
    }
    
    function set_baseReward(int128 _baseReward) public onlyGovernance{
        baseReward = _baseReward;
    }
    
    function set_pros_address(address pri)public onlyGovernance{
        _pros = IERC20(pri);
    }
    function set_teamWallet(address team)public onlyGovernance{
        _teamWallet = team;
    }
    function set_rewardpool(address pool)public onlyGovernance{
        _rewardPool = pool;
    }
    //set the initamount for onwer
    function set_initReward(uint256 initamount) public onlyGovernance{
        _initReward = initamount;
    }
    /* Fee collection for any other token */
    function seize(IERC20 token, uint256 amount) external onlyGovernance{
        require(token != _pros, "reward");
        require(token != _lpToken, "stake");
        token.safeTransfer(_governance, amount);
    }

    function setTeamRewardRate( uint256 teamRewardRate ) public onlyGovernance{
        _teamRewardRate = teamRewardRate;
    }

    function setPoolRewardRate( uint256  poolRewardRate ) public onlyGovernance{
        _poolRewardRate = poolRewardRate;
    }

    function setWithDrawPunishTime( uint256  punishTime ) public onlyGovernance{
        _punishTime = punishTime;
    }

    function lastTimeRewardApplicable() public view returns (uint256) {
        return Math.min(block.timestamp, _periodFinish);
    }
    
    
    function rewardPerToken() public view returns (uint256) { //to change to the address thing for dip problem 
        if (totalPower() == 0) { //totalPower change ----- totaldipost[token] 
            return _rewardPerTokenStored;
        }
        return
            _rewardPerTokenStored.add(
                lastTimeRewardApplicable() 
                    .sub(_lastUpdateTime)
                    .mul(_rewardRate) //change for the _rewardRate[token]
                    .mul(1e18)
                    .div(totalPower()) //change for the totalPower[token] ---- 
            );
    }

    //diposit funtion should define the pri address setprice interface also 
    //function 

    function earned(address account) public view returns (uint256) {
        return
            balanceOfPower(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, string memory affCode)
        public
        updateReward(msg.sender)
        checkHalve
        checkStart
        isRegister
    {
        require(amount > 0, "Cannot stake 0");
        super.stake(amount, affCode);

        _lastStakedTime[msg.sender] = now;

        emit Staked(msg.sender, amount);
    }

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

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

    function getReward() public updateReward(msg.sender) checkHalve checkStart {
        uint256 reward = earned(msg.sender);
        if (reward > 0) {
            _rewards[msg.sender] = 0;
            
            address set_play = AllPool(_allpool).get_Address_pool(msg.sender)==0x0000000000000000000000000000000000000000?_playerBook:AllPool(_allpool).get_Address_pool(msg.sender);
            uint256 fee = IPlayerBook(set_play).settleReward(msg.sender,reward);
            // uint256 fee = IPlayerBook(_playerBook).settleReward(msg.sender, reward);
            if(fee > 0){
                _pros.safeTransfer(_playerBook, fee);
            }
            
            uint256 teamReward = reward.mul(_teamRewardRate).div(_baseRate);
            if(teamReward>0){
                _pros.safeTransfer(_teamWallet, teamReward);
            }
            uint256 leftReward = reward.sub(fee).sub(teamReward);
            uint256 poolReward = 0;

            //withdraw time check

            if(now  < (_lastStakedTime[msg.sender] + _punishTime) ){
                poolReward = leftReward.mul(_poolRewardRate).div(_baseRate);
            }
            if(poolReward>0){
                _pros.safeTransfer(_rewardPool, poolReward);
                leftReward = leftReward.sub(poolReward);
            }

            if(leftReward>0){
                _pros.safeTransfer(msg.sender, leftReward );
            }
      
            emit RewardPaid(msg.sender, leftReward);
        }
    }

    modifier checkHalve() {
        if (block.timestamp >= _periodFinish) {
            // _initReward = _initReward.mul(50).div(100);
            update_initreward();
            _pros.mint(address(this), _initReward);
            _rewardRate = _initReward.div(DURATION);
            _periodFinish = block.timestamp.add(DURATION);
            emit RewardAdded(_initReward);
        }
        _;
    }
    
    modifier checkStart() {
        require(block.timestamp > _startTime, "not start");
        _;
    }
    
    modifier isRegister(){
        require(AllPool(_allpool).is_Re(msg.sender)==true,"address not register or name already register");
        _;
    }
    
    function update_initreward() private {
	    dayNums = dayNums + 1;
        uint256 thisreward = base_.mul(rate_forReward).mul(10**18).mul((base_Rate_Reward.sub(rate_forReward))**(uint256(dayNums-1))).div(base_Rate_Reward**(uint256(dayNums)));
	    _initReward = uint256(thisreward);
	}
//     function update_initreward() private {
// 	    dayNums = dayNums + 1;
//         int128 precision = 10000000;
//         int256 thisreward;
//         int128 BASE_Rate = precision-precision*dayNums/60; 
//         uint256 count = 0;
//         int128[] memory list = new int128[](15);
//         int128 Yun_number = BASE_Rate;
//         int128 d = 0;
//         if(dayNums<=180){
//         for(int128 i=0;i<15;i++){ 
//         	Yun_number = Yun_number*2;
//         	int128 A = 1;
        	
//         	if(Yun_number>precision){ 
//         		d = d+(A<<(63-count));
//         		Yun_number-=precision;
//         		list[count] = int128(1);
//         		count+=1;
//         	}else{
//         		//d = d+(B<<(63-count));
//         		list[count] = int128(0);
//         		count+=1;
//         	}
        	
//         }

// 		thisreward = int256(ABDKMath64x64.toInt(ABDKMath64x64.exp(d)*baseReward));

// 		}else if(dayNums<=25*365){
// 		    thisreward = int256(1000);
// 		}
// 	    thisreward = thisreward*10**18;
// 	    _initReward = uint256(thisreward);
// 	}


    
    
    
    
    // set fix time to start reward
    function startReward(uint256 startTime)
        external
        onlyGovernance
        updateReward(address(0))
    {
        require(_hasStart == false, "has started");
        _hasStart = true;
        _startTime = startTime;
        update_initreward();
        _rewardRate = _initReward.div(DURATION); 
        _pros.mint(address(this), _initReward);
        _lastUpdateTime = _startTime;
        _periodFinish = _startTime.add(DURATION);

        emit RewardAdded(_initReward);
    }

    //

    //for extra reward
    function notifyRewardAmount(uint256 reward)
        external
        onlyGovernance
        updateReward(address(0))
    {
        IERC20(_pros).safeTransferFrom(msg.sender, address(this), reward);
        if (block.timestamp >= _periodFinish) {
            _rewardRate = reward.div(DURATION);
        } else {
            uint256 remaining = _periodFinish.sub(block.timestamp);
            uint256 leftover = remaining.mul(_rewardRate);
            _rewardRate = reward.add(leftover).div(DURATION);
        }
        _lastUpdateTime = block.timestamp;
        _periodFinish = block.timestamp.add(DURATION);
        emit RewardAdded(reward);
    }
}
function seize(IERC20 token, uint256 amount) external onlyGovernance{
    require(token != _pros, "reward");
    require(token != _lpToken, "stake");
    token.safeTransfer(_governance, amount);
}

/* Fee collection for any other token */