contract PHCStakeContract is Ownable, Pausable {
    // Library for safely handling uint256
    using SafeMath for uint256;

    uint256 ONE_DAY;
    uint256 public stakeDays;
    uint256 public maxStakedQuantity;
    address public PhuketTokenAddress;
    uint256 public ratio;
    uint256 public totalStakedTokens;
    uint256 public FEE;
    address public fee_address;
    

    mapping(address => uint256) public stakerBalance;
    mapping(uint256 => StakerData) public stakerData;
    
    mapping (address => Storetime)  Storetimes; 
    mapping(address=>uint256) public laststake;
    
    struct Storetime {
        uint256[] invTime;
    }

    struct StakerData {
        uint256 altQuantity;
        uint256 initiationTimestamp;
        uint256 durationTimestamp;
        uint256 endTime;
        uint256 rewardAmount;
        address staker;
        uint256 exactdate;
        uint256 withdrawable;
        uint256 rewardAmountdata;
        bool isCompleted;
    }
    event StakeCompleted(
        uint256 altQuantity,
        uint256 initiationTimestamp,
        uint256 durationTimestamp,
        uint256 rewardAmount,
        address staker,
        address PhuketTokenAddress,
        address portalAddress
    );

    event Unstake(
        address staker,
        address stakedToken,
        address portalAddress,
        uint256 altQuantity,
        uint256 durationTimestamp,
        uint256 rewardAmount
    ); // When ERC20s are withdrawn
    event BaseInterestUpdated(uint256 _newRate, uint256 _oldRate);

    constructor(uint256 _ratio,address _phuketToken,uint256 _maxStkQty, address _feeaddress) public {
        ratio = _ratio;
        PhuketTokenAddress = _phuketToken;
        maxStakedQuantity = _maxStkQty;
        stakeDays = 365;
        ONE_DAY = 86400;
        FEE = 300;
        fee_address = _feeaddress;
    }
    
    function userInvesmentData(address _address) public view returns(uint256[] memory UserInv)
    {
        return Storetimes[_address].invTime;
    }
    
    function earnedReward(uint256 _id) public view returns(uint256)
    {
        uint256 rewardAmount;
         if(stakerData[_id].endTime <= block.timestamp){
             uint256 exactDays = (stakerData[_id].exactdate).mul(ONE_DAY);
              rewardAmount = _calculateReward(stakerData[_id].altQuantity, ratio, exactDays);
         }
         else
         {
             uint256 days_remaining = (block.timestamp).sub(stakerData[_id].initiationTimestamp);
             rewardAmount = _calculateReward(stakerData[_id].altQuantity, ratio, days_remaining);
         }
         return rewardAmount;
    }

    /* @dev stake function which enable the user to stake Phuket Tokens.
     *  @param _altQuantity, Phuket amount to be staked.
     *  @param _days, how many days Phuket tokens are staked for (in days)
     */
    function stakeALT(uint256 _altQuantity, uint256 _days)
        public
        whenNotPaused
        returns (uint256 rewardAmount)
    {
        require(_days <= stakeDays && _days > 0, "Invalid Days"); // To check days
        require(
            _altQuantity <= maxStakedQuantity && _altQuantity > 0,
            "Invalid Phuket quantity"
        ); // To verify Phuket quantity

        IERC20(PhuketTokenAddress).transferFrom(
            msg.sender,
            address(this),
            _altQuantity
        );

        rewardAmount = 0;

        uint256 _timestamp = block.timestamp;

        if (stakerData[_timestamp].staker != address(0)) {
            _timestamp = _timestamp.add(1);
        }

        stakerData[_timestamp] = StakerData(
            _altQuantity,
            _timestamp,
            _days.mul(ONE_DAY),
            (_days.mul(ONE_DAY)).add(_timestamp),
            rewardAmount,
            msg.sender,
            _days.mul(1e18),
            0,
            0,
            false
        );

        stakerBalance[msg.sender] = stakerBalance[msg.sender].add(_altQuantity);

        totalStakedTokens = totalStakedTokens.add(_altQuantity);

        Storetimes[msg.sender].invTime.push(_timestamp);
        
        laststake[msg.sender]=_timestamp;

        emit StakeCompleted(
            _altQuantity,
            _timestamp,
            _days.mul(ONE_DAY),
            rewardAmount,
            msg.sender,
            PhuketTokenAddress,
            address(this)
        );
    }

    /*  @dev unStake function which enable the user to withdraw his Phuket Tokens.
     *  @param _expiredTimestamps, time when Phuket tokens are unlocked.
     *  @param _amount, amount to be withdrawn by the user.
     */
    function unstakeALT(uint256[] calldata _expiredTimestamps)
        external
        whenNotPaused
        returns (bool)
    {
    
        for (uint256 i = 0; i < _expiredTimestamps.length; i = i.add(1)) {
            
                uint256 withdrawAmount = 0;
                uint256 rewardAmount=0;
                uint256 timestamp = block.timestamp;
               
                 require(!(stakerData[_expiredTimestamps[i]].isCompleted),"Already Unstaked");
                 withdrawAmount =  stakerData[_expiredTimestamps[i]].altQuantity;
                 uint256 initiate_time = stakerData[_expiredTimestamps[i]].initiationTimestamp;
                  
             uint256 exactDays = (timestamp).sub(initiate_time);
             
               
        
                 rewardAmount = _calculateReward(withdrawAmount, ratio, exactDays);
                IERC20(PhuketTokenAddress).transfer(msg.sender, rewardAmount);
          

          stakerData[_expiredTimestamps[i]].withdrawable=withdrawAmount;
          
            stakerBalance[msg.sender] = stakerBalance[msg.sender].sub(withdrawAmount);

        totalStakedTokens = totalStakedTokens.sub(withdrawAmount);

        IERC20(PhuketTokenAddress).transfer(msg.sender, withdrawAmount);
                    
     
         emit Unstake( msg.sender,
                            PhuketTokenAddress,
                            address(this),
                            stakerData[_expiredTimestamps[i]].altQuantity,
                            _expiredTimestamps[i],
                            rewardAmount
                        );
        
        stakerData[_expiredTimestamps[i]].rewardAmountdata=rewardAmount;
        stakerData[_expiredTimestamps[i]].isCompleted=true;
    
    }
       
        
        return true;
    }

    /* @dev to calculate reward Amount
     *  @param _altQuantity , amount of ALT tokens staked.
     *@param _baseInterest rate
     */
    function _calculateReward(
        uint256 _altQuantity,
        uint256 _ratio,
        uint256 _days
    ) public view returns (uint256 rewardAmount) {
        rewardAmount = (_altQuantity.mul(_ratio).mul(_days)).div(1e18);
        rewardAmount = rewardAmount.div(ONE_DAY);
    }

   

    /* @dev to set base interest rate. Can only be called by owner
     *  @param _rate, interest rate (in wei)
     */
    function updateRatio(uint256 _rate) public onlyOwner whenNotPaused {
        ratio = _rate;
    }

    function updateTime(uint256 _time) public onlyOwner whenNotPaused {
        ONE_DAY = _time;
    }
    
    
     function updateFEE(uint256 _fee) public onlyOwner whenNotPaused {
        FEE = _fee;
    }
    
     function updateFeeaddress(address _feeaddress) public onlyOwner whenNotPaused {
        fee_address = _feeaddress;
    }

    function updateQuantity(uint256 _quantity) public onlyOwner whenNotPaused {
        maxStakedQuantity = _quantity;
    }

    /* @dev function to update stakeDays.
     *@param _stakeDays, updated Days .
     */
    function updatestakeDays(uint256 _stakeDays) public onlyOwner {
        stakeDays = _stakeDays;
    }

    /* @dev Funtion to withdraw all Phuket from contract incase of emergency, can only be called by owner.*/
    function withdrawTokens() public onlyOwner {
        IERC20(PhuketTokenAddress).transfer(
            owner(),
            IERC20(PhuketTokenAddress).balanceOf(address(this))
        );
        pause();
    }

    function getTotalrewardTokens() external view returns(uint256){
        return IERC20(PhuketTokenAddress).balanceOf(address(this)).sub(totalStakedTokens);
    }

    /* @dev function to update Phuket contract address.
     *@param _address, new address of the contract.
     */
    function setPhuketTokenAddress(address _address) public onlyOwner {
        PhuketTokenAddress = _address;
    }

    /* @dev function which restricts the user from stakng Phuket tokens. */
    function pause() public onlyOwner {
        _pause();
    }

    /* @dev function which disables the Pause function. */
    function unPause() public onlyOwner {
        _unpause();
    }
}
function pause() public onlyOwner {
    _pause();
}

/* @dev function which restricts the user from stakng Phuket tokens. */