contract timelocks is WhitelistForSelf {
    using SafeERC20 for IERC20;

    IERC20 private immutable _token;

    event LockCreated(
        address indexed user,
        uint256 indexed lockNumber,
        uint256 value,
        uint256 reward,
        uint256 startTime,
        uint256 releaseTime
    );
    event Released(
        address indexed user,
        uint256 indexed lockNumber,
        uint256 actualReleaseTime
    );

    uint256 public lockedTotal;
    uint256 public rewardTotal;

    constructor(IERC20 token_) {
        _token = token_;
    }

    struct LockDetail {
        uint256 value;  //locked mulan value
        uint256 reward; //reward mulanV2 value
        uint256 releaseTime;
        bool released;
    }

    mapping(address => LockDetail[]) public userLocks;

    function getTotalLocksOf(address _user) public view returns (uint256) {
        return userLocks[_user].length;
    }

    function getDetailOf(address _user, uint256 _lockNumber)
        public
        view
        returns (
            uint256 value,
            uint256 reward,
            uint256 releaseTime,
            bool released
        )
    {
        LockDetail memory detail = userLocks[_user][_lockNumber];
        return (
            detail.value,
            detail.reward,
            detail.releaseTime,
            detail.released
        );
    }

    ///@dev it's caller's responsibility to transfer _value token to this contract
    function lockByWhitelist(
        address _user,
        uint256 _value,
        uint256 _reward,
        uint256 _releaseTime
    ) external virtual allowModification returns (bool) {
        require(
            _releaseTime >= block.timestamp,
            "lock time should be after current time"
        );
        require(_value > 0, "value should be above 0");
        uint256 lockNumber = userLocks[_user].length;
        userLocks[_user].push(LockDetail(_value, _reward, _releaseTime, false));
        lockedTotal += _value;
        rewardTotal += _reward;
        emit LockCreated(
            _user,
            lockNumber,
            _value,
            _reward,
            block.timestamp,
            _releaseTime
        );
        return true;
    }

    function canRelease(address _user, uint256 _lockNumber)
        public
        view
        virtual
        returns (bool)
    {
        return
            userLocks[_user][_lockNumber].releaseTime <= block.timestamp &&
            !userLocks[_user][_lockNumber].released;
    }

    function releaseByWhitelist(address _user, uint256 _lockNumber)
        public
        virtual
        allowModification
        returns (bool)
    {
        require(
            canRelease(_user, _lockNumber),
            "still locked or already released"
        );
        LockDetail memory detail = userLocks[_user][_lockNumber];
        _token.safeTransfer(_user, detail.value);
        userLocks[_user][_lockNumber].released = true;
        emit Released(_user, _lockNumber, block.timestamp);
        return true;
    }

}
function lockByWhitelist(
    address _user,
    uint256 _value,
    uint256 _reward,
    uint256 _releaseTime
) external virtual allowModification returns (bool) {
    require(
        _releaseTime >= block.timestamp,
        "lock time should be after current time"
    );
    require(_value > 0, "value should be above 0");
    uint256 lockNumber = userLocks[_user].length;
    userLocks[_user].push(LockDetail(_value, _reward, _releaseTime, false));
    lockedTotal += _value;
    rewardTotal += _reward;
    emit LockCreated(
        _user,
        lockNumber,
        _value,
        _reward,
        block.timestamp,
        _releaseTime
    );
    return true;
}

///@dev it's caller's responsibility to transfer _value token to this contract