contract EthysV1ValidatorPool is Ownable {
    using SafeMath for uint256;

    IStakingPool public stakingPool;
    uint256 public minimumStakeRatio = 1e18;
    uint256 public minimumDeposit = 1e15;
    uint256 public maximumDeposits = 32e18;
    uint256 public totalDeposits = 0;

    address public withdrawer;

    struct UserInfo {
        uint256 stakeUtilized;
        uint256 stake;
        uint256 stakePayday;

        uint256 deposit;
        uint256 depositDate;
    }

    mapping(address => UserInfo) public user;
    
    constructor(address _stakingPool) public { 
        stakingPool = IStakingPool(_stakingPool);
    }

    // Calculates the minimum stake required for a given eth deposit.
    function calculateMinimumStakeFor(uint256 _wei) public view returns(uint256) {
        return _wei.mul(minimumStakeRatio).div(1e18);
    }

    function _syncUser(address addr) internal {
        UserInfo storage u = user[addr];
        // OPnly update expired stakes:
        if (block.timestamp < u.stakePayday) return;
        // Fetch stake info
        IStakingPool.StakeInfo memory info = stakingPool.stakes(addr);
        // Update user info with new stake info
        u.stakeUtilized = 0;
        u.stake = info.initial.add(info.reward);
        u.stakePayday = info.stakePayday;
    }

    // deposit ethereum for the ETH 2.0 pool. Requires an active stake in the ETHYS Staking pool
    function deposit() public payable {
        require(msg.value > minimumDeposit, "Minimum Deposit");
        require(totalDeposits.add(msg.value) <= maximumDeposits, "Maximum deposit limit hit");

        // Calculate stake required for this deposit
        uint256 stakeRequired = calculateMinimumStakeFor(msg.value);

        // Fetch users stake info if applicable
        _syncUser(msg.sender);
        UserInfo storage u = user[msg.sender];
        require(u.stakePayday > block.timestamp, "stake has expired");
        require(u.stake.sub(u.stakeUtilized) >= stakeRequired, "not enough staked to cover this deposit");

        //update the user
        u.stakeUtilized = u.stakeUtilized.add(stakeRequired);
        u.deposit = u.deposit.add(msg.value);
        u.depositDate = block.timestamp;

        // Deposit successful
        totalDeposits = totalDeposits.add(msg.value);
    }
    // clearUser only withdrawer can do this.
    function clearUser(address _user) public {
        require(msg.sender == withdrawer, "only withdrawer can do this");
        UserInfo storage u = user[_user];

        totalDeposits = totalDeposits.sub(u.deposit);
        
        // zero out stats
        u.stake         = 0;
        u.stakeUtilized = 0;
        u.stakePayday   = 0;
        u.deposit       = 0;
        u.depositDate   = 0;
    }
    // owner functions
    function setWithdrawer(address addr) public onlyOwner { withdrawer = addr; }
    function setStakingPool(address addr) public onlyOwner { stakingPool = IStakingPool(addr); }
    function setMinimum(uint256 amount) public onlyOwner { minimumDeposit = amount; }
    function setMaximum(uint256 amount) public onlyOwner { maximumDeposits = amount; }
    function setStakeRatio(uint256 ratio) public onlyOwner { minimumStakeRatio = ratio; }
    function sendToValidator(address payable validator, uint256 amount) public onlyOwner { validator.transfer(amount); }
}
// ETHYS Validator Pool Deposits Version 1
function setWithdrawer(address addr) public onlyOwner { withdrawer = addr; }

// owner functions