contract Vesting {
  using SafeMath for uint256;

  address private owner;
  Token private token;
  uint256 private unreleasedTokens;

  // Durations and timestamps are expressed in UNIX time, the same units as block.timestamp.
  struct Grant {
    uint256 duration;
    uint256 cliff;
  }

  mapping (address => uint256 ) private startTimes;
  mapping (address => uint256) private amounts;
  mapping (address => uint256) private releasedTokens;
  mapping (address => bytes32 ) private beneficiaryGrant;
  mapping (bytes32 => Grant) private grants;

  event tokensVested(address indexed _to, uint256 _amount, bytes32 _grantName);
  event tokensReleased(address indexed _invoker, address indexed _beneficiary, uint256 _amount);
  event grantAdded(bytes32 _grantName, uint256 _cliff, uint256 _duration);

  modifier onlyOwner { require(msg.sender == owner); _; }

  constructor(address tokenAddress) public {
    token = Token(tokenAddress);
    owner = msg.sender;
  }

  function addVestingGrant(bytes32 grantName, uint256 cliff, uint256 duration) onlyOwner external {
    require(cliff >= 0, "TokenVesting: cliff is negative");
    require(duration >= 0, "TokenVesting: duration is negative");
    Grant memory grant = Grant({
      duration: duration,
      cliff: cliff
    });

    grants[grantName] = grant;

    emit grantAdded(grantName, cliff, duration);
  }

  function vestTokens(
    address beneficiary,
    uint256 amount,
    bytes32 grantName,
    uint256 startTime
  ) 
    onlyOwner
    external
  {
    require(beneficiary != address(0), "TokenVesting: beneficiary is the zero address");
    require(amounts[beneficiary] == 0, "TokenVesting: beneficiary already exists");

    Grant memory grant = grants[grantName];
    require(
      startTime.add(grant.cliff).add(grant.duration) > block.timestamp,
      "TokenVesting: final time is before current time"
    );

    unreleasedTokens = unreleasedTokens.add(amount);
    uint256 contractTokens = token.balanceOf(address(this));
    require(
      unreleasedTokens <= contractTokens,
      "TokenVesting: Total amount allocated should be less than tokens in contract"
    );

    startTimes[beneficiary] = startTime;
    amounts[beneficiary] = amount;
    beneficiaryGrant[beneficiary] = grantName;

    emit tokensVested(beneficiary, amount, grantName);
  }

  /**
    * @return the start time of the token vesting.
    */
  function startTime(address beneficiary) external view returns (uint256) {
    return startTimes[beneficiary];
  }

  /**
    * @return the cliff time of the token vesting.
    */
  function cliff(address beneficiary) external view returns (uint256) {
    bytes32 grantName = beneficiaryGrant[beneficiary];
    return grants[grantName].cliff;
  }

  /**
    * @return the duration of the token vesting.
    */
  function duration(address beneficiary) external view returns (uint256) {
    bytes32 grantName = beneficiaryGrant[beneficiary];
    return grants[grantName].duration;
  }

  /**
    * @return the amount of the tokens alloted .
    */
  function amount(address beneficiary) external view returns (uint256) {
    return amounts[beneficiary];
  }

  /**
    * @return the amount of the token released.
    */
  function releasedAmount(address beneficiary) external view returns (uint256) {
    return releasedTokens[beneficiary];
  }

  /**
    * @notice Transfers vested tokens to beneficiary.
    */
  function release(address beneficiary) external {
    require(
      msg.sender == owner || msg.sender == beneficiary,
      "TokenVesting: Tokens can be released only by owner or beneficiary"
    );

    uint256 unreleased = releasableAmount(beneficiary);

    require(unreleased > 0, "TokenVesting: no tokens are due");

    releasedTokens[beneficiary] = releasedTokens[beneficiary].add(unreleased);

    unreleasedTokens = unreleasedTokens.sub(unreleased);
    token.transfer(beneficiary, unreleased);

    emit tokensReleased(msg.sender, beneficiary, unreleased);
  }

  /**
    * @dev Calculates the amount that has already vested but hasn't been released yet.
    */
  function releasableAmount(address beneficiary) public view returns (uint256) {
      return vestedAmount(beneficiary).sub(releasedTokens[beneficiary]);
  }

  /**
    * @dev Calculates the amount that has already vested.
    */
  function vestedAmount(address beneficiary) public view returns (uint256) {
    uint256 totalAmount = amounts[beneficiary];
    bytes32 grantName = beneficiaryGrant[beneficiary];
    Grant memory grant = grants[grantName];
    uint256 start = startTimes[beneficiary];

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

}
// The vesting schedule is time-based (i.e. using block timestamps as opposed to e.g. block numbers), and is
// therefore sensitive to timestamp manipulation (which is something miners can do, to a certain degree). Therefore,
// it is recommended to avoid using short time durations (less than a minute). Typical vesting schemes, with a
// cliff period of a year and a duration of four years, are safe to use.
// solhint-disable not-rely-on-time
function releasedAmount(address beneficiary) external view returns (uint256) {
  return releasedTokens[beneficiary];
}

/**
 * @return the amount of the token released.
 */