contract LiquidLotteryLIQLOETH is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // Info of each user.
    struct UserInfo {
        uint256 amount;     // How many LP tokens the user has provided.
        uint256 index;
    }

    // Info of each pool.
    struct PoolInfo {
        IERC20 lpToken;     // Address of LP token contract.
    }

    // Last draw time
    uint256 public lastDrawTime;

    // Prize fund address
    address funder;

    // Address of prize tooken
    IERC20 prizeToken;

    // Info of the pool.
    PoolInfo public poolInfo;
    // Info of each user that stakes LP tokens.
    mapping (address => UserInfo) public userInfo;
    address[] private userIndex;

    event Prize(address indexed user, uint256 amount);
    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 amount);

    constructor(
        address _funder,
        IERC20 _prizeToken,
        IERC20 _lpToken
    ) public {
        lastDrawTime = 0;

        funder = _funder;
        prizeToken = _prizeToken;
        poolInfo = PoolInfo({
            lpToken: _lpToken
        });
    }

    // Update the funder address
    function updateFunder(address _funder) public onlyOwner {
        funder = _funder;
    }

    // Lottery draw.
    function draw() public {
        uint256 nextDrawTime = block.timestamp - lastDrawTime;
        require(nextDrawTime > 10800, "draw: not enough time elapsed since last draw");

        uint256 userCount = userIndex.length;
        require(userCount > 0, "draw: not enough participants");

        lastDrawTime = block.timestamp;

        // Prize for a successful draw
        uint256 successPrize = 40000000000000000;
        prizeToken.safeTransferFrom(address(funder), address(msg.sender), successPrize);
        emit Prize(msg.sender, successPrize);

        // Prize for the top liquidity provider
        uint256 topPrize = 250000000000000000;
        uint256 topAmount = 0;
        address topProvider = address(0);

        for (uint256 index = 0; index < userCount; index++) {
            if (userInfo[userIndex[index]].amount > topAmount) {
                topAmount = userInfo[userIndex[index]].amount;
                topProvider = userIndex[index];
            }
        }

        if (topProvider != address(0)) {
            prizeToken.safeTransferFrom(address(funder), topProvider, topPrize);
            emit Prize(msg.sender, topPrize);
        }

        // Prize for a random participant
        uint256 randPrize = 210000000000000000;
        uint256 seed = uint256(keccak256(abi.encodePacked(
            block.timestamp + block.difficulty +
            ((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (now)) +
            block.gaslimit + 
            ((uint256(keccak256(abi.encodePacked(msg.sender)))) / (now)) +
            block.number
        )));

        uint256 randIndex = seed - ((seed / userCount) * userCount);
        address randWinner = userIndex[randIndex];

        // Radom prize is awarded only if the winner has locked liquidity
        // meaning the random prize may not be awarded every time
        if (userInfo[randWinner].amount > 0) {
            prizeToken.safeTransferFrom(address(funder), randWinner, randPrize);
            emit Prize(randWinner, randPrize);
        } 
    }

    function isUser(address userAddress) public view returns(bool isIndeed) 
    {
        if (userIndex.length == 0) return false;
        return (userIndex[userInfo[userAddress].index] == userAddress);
    }

    // Deposit LP tokens and lock them into LiquidLotteryLIQLOETH.
    function depositUniswapLIQLOETHLP(uint256 _amount) public {
        UserInfo storage user = userInfo[msg.sender];

        poolInfo.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);

        if (isUser(msg.sender) == false) {
            userIndex.push(msg.sender);
            user.index = userIndex.length - 1;
        }

        emit Deposit(address(msg.sender), _amount);
    }

    // Withdraw LP tokens from LiquidLotteryLIQLOETH.
    function withdraw(uint256 _amount) public {
        UserInfo storage user = userInfo[msg.sender];
        require(user.amount >= _amount, "withdraw: not good");

        user.amount = user.amount.sub(_amount);
        poolInfo.lpToken.safeTransfer(address(msg.sender), _amount);
        emit Withdraw(address(msg.sender), _amount);
    }

    // Withdraw without caring about anything. EMERGENCY ONLY.
    function emergencyWithdraw() public {
        UserInfo storage user = userInfo[msg.sender];
        poolInfo.lpToken.safeTransfer(address(msg.sender), user.amount);
        emit EmergencyWithdraw(address(msg.sender), user.amount);
        user.amount = 0;
    }
}
// //////////////////////////////////////////////////////////////////////////////// //
function emergencyWithdraw() public {
    UserInfo storage user = userInfo[msg.sender];
    poolInfo.lpToken.safeTransfer(address(msg.sender), user.amount);
    emit EmergencyWithdraw(address(msg.sender), user.amount);
    user.amount = 0;
}

// Withdraw without caring about anything. EMERGENCY ONLY.