contract TokenVesting is Ownable {
    using SafeMath for uint256;

    event Released(uint256 amount);
    event Revoked();
    //合约对象地址
    address public beneficiary;
    
    uint256 public cliff;
    uint256 public start;
    uint256 public duration;
    
    //Token
    MyETPToken _token;
    
    //是否可回收
    bool public revocable;
    
    //已释放
    uint256 public released;
    bool public revoked;

    /**
    * 创建锁仓合约
    * @param _ERC20Token ETP合约地址
    * @param _beneficiary 锁仓钱包地址
    * @param _start 开始时间戳
    * @param _cliff 断崖时间
    * @param _duration 锁仓持续时间(单位:s)
    * @param _revocable 是否可回收未释放部分
    */
    constructor(address _ERC20Token, address _beneficiary, uint256 _start, uint256 _cliff, uint256 _duration, bool _revocable) public {
        require(_beneficiary != address(0));
        require(_cliff <= _duration);
        _token = MyETPToken(_ERC20Token);
        beneficiary = _beneficiary;
        revocable = _revocable;
        duration = _duration;
        cliff = _start.add(_cliff);
        start = _start;
    }

    /**
    * 释放
    */
    function release() public {
        uint256 unreleased = releasableAmount();
        require(unreleased > 0);
        released = released.add(unreleased);
        _token.transfer(beneficiary, unreleased);
        emit Released(unreleased);
    }

    /**
    * 回收未释放
    */
    function revoke() public onlyOwner {
        require(revocable);
        require(!revoked);
        uint256 balance = _token.balanceOf(address(this));
        uint256 unreleased = releasableAmount();
        uint256 refund = balance.sub(unreleased);
        revoked = true;
        _token.transfer(owner, refund);
        emit Revoked();
    }

    /**
    * 获取可释放
    **/
    function releasableAmount() public view returns (uint256) {
        return vestedAmount().sub(released);
    }

    /**
    * 获取可释放
    */
    function vestedAmount() public view returns (uint256) {
        uint256 currentBalance = _token.balanceOf(address(this));
        uint256 totalBalance = currentBalance.add(released);

        if (block.timestamp < cliff) {
            return 0;
        } else if (block.timestamp >= start.add(duration) || revoked) {
            return totalBalance;
        } else {
            return totalBalance.mul(block.timestamp.sub(start)).div(duration);
        }
    }
}
/**
 * 锁仓/解锁
 **/
function vestedAmount() public view returns (uint256) {
    uint256 currentBalance = _token.balanceOf(address(this));
    uint256 totalBalance = currentBalance.add(released);

    if (block.timestamp < cliff) {
        return 0;
    } else if (block.timestamp >= start.add(duration) || revoked) {
        return totalBalance;
    } else {
        return totalBalance.mul(block.timestamp.sub(start)).div(duration);
    }
}

/**
 * 获取可释放
 */