contract LPTokenWrapper is ReentrancyGuard {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    //Total amount locked info contract
    uint256 public totalSupply;
    //User's balances
    mapping(address => uint256) private balances;
    struct HistoryBalance {
        uint256 value;
        bool isSet;
    }
    mapping (uint256 => HistoryBalance) internal historyTotalSupply;
    struct UserData {
        //Period last time rewards claimed
        uint256 period;
        //Last time deposited. used to implement holdDays
        uint256 lastTime;
        bool exists;
        mapping (uint256 => HistoryBalance) historyBalance;
    }
    mapping (address => UserData) private userData;

    //Interface to wrapped token
    IERC20 public lpToken;
    //Hold in seconds before withdrawal after last time staked
    uint256 public holdTime;

    /**
     * @dev LPTokenWrapper constructor
     * @param _lpToken Wrapped token to be staked
     * @param _holdDays Hold days after last deposit
     */
    constructor(address _lpToken, uint256 _holdDays) internal {
        lpToken = IERC20(_lpToken);
        holdTime = _holdDays.mul(1 days);
    }

    /**
     * @dev Deposits a given amount of lpToken from sender
     * @param _amount Units of lpToken
     */
    function _stake(uint256 _amount, uint256 _period)
        internal
        nonReentrant
    {
        
        UserData storage _user = userData[msg.sender]; 
        if(!_user.exists){
            _user.period = _period;
            _user.exists = true;
        }
        totalSupply = totalSupply.add(_amount);
        _updateHistoryTotalSupply(_period, totalSupply);
        balances[msg.sender] = balances[msg.sender].add(_amount);
        _user.historyBalance[_period].value = balances[msg.sender];
        _user.historyBalance[_period].isSet = true;
        _user.lastTime = block.timestamp;
        lpToken.safeTransferFrom(msg.sender, address(this), _amount);
    }

    /**
     * @dev Withdraws a given stake from sender
     * @param _amount Units of lpToken
     */
    function _withdraw(uint256 _amount, uint256 _period)
        internal
        nonReentrant
    {
        //Check first if user has sufficient balance, added due to hold requrement 
        //("Cannot withdraw, tokens on hold" will be fired even if user  has no balance)
        require(balances[msg.sender] >= _amount, "Not enough balance");
        UserData storage _user = userData[msg.sender]; 
        require(block.timestamp.sub(_user.lastTime) >= holdTime, "Cannot withdraw, tokens on hold");
        totalSupply = totalSupply.sub(_amount);
        _updateHistoryTotalSupply(_period, totalSupply);
        balances[msg.sender] = balances[msg.sender].sub(_amount);
        _user.historyBalance[_period].value = balances[msg.sender];
        _user.historyBalance[_period].isSet = true;
        lpToken.safeTransfer(msg.sender, _amount);
    }

    /**
     * @dev Returns User Data
     * @param _address Address of the User
     */
     function _getUserData(address _address)
        internal
        view
        returns (UserData storage)
    {
        return userData[_address];
    }
    
    /**
     * @dev Updates history total supply
     * @param _period period
     * @param _totalSupply total supply for period
     */
     function _updateHistoryTotalSupply(uint256 _period, uint256 _totalSupply)
        internal
    {
        historyTotalSupply[_period].value = _totalSupply;
        historyTotalSupply[_period].isSet = true;
    }    

    /**
     * @dev Sets user's period and historyBalance at this period (last period rewards claimed)
     * @param _address address of the User
     * @param _period period till which balance claimed
     * @param _periodBalance user's historyBalance at period above
     */
     function _updateUser(address _address, uint256 _period, uint256 _periodBalance)
        internal
    {
        UserData storage _user = userData[_address]; 
        _user.period = _period;
        _user.historyBalance[_period].value = _periodBalance;
        _user.historyBalance[_period].isSet = true;
    }   

    /**
     * @dev Get the balance of a given account
     * @param _address User for which to retrieve balance
     */
    function balanceOf(address _address)
        public
        view
        returns (uint256)
    {
        return balances[_address];
    }    
}
function _updateHistoryTotalSupply(uint256 _period, uint256 _totalSupply)
   internal

   historyTotalSupply[_period].value = _totalSupply;
   historyTotalSupply[_period].isSet = true;
    

/**
 * @dev Updates history total supply
 * @param _period period
 * @param _totalSupply total supply for period
 */