contract Lockable is Context {
  using SafeMath for uint;

  struct TimeLock {
    uint amount;
    uint expiresAt;
  }

  mapping(address => bool) private _lockers;
  mapping(address => bool) private _locks;
  mapping(address => TimeLock[]) private _timeLocks;

  event LockerAdded(address indexed account);
  event LockerRemoved(address indexed account);
  event Locked(address indexed account);
  event Unlocked(address indexed account);
  event TimeLocked(address indexed account);
  event TimeUnlocked(address indexed account);

  /**
    * @dev Throws if called by any account other than the locker.
    */
  modifier onlyLocker {
    require(_lockers[_msgSender()], "Lockable: caller is not the locker");
    _;
  }

  /**
    * @dev Returns whether the address is locker.
    */
  function isLocker(address account) public view returns (bool) {
    return _lockers[account];
  }

  /**
    * @dev Add locker, only owner can add locker
    */
  function _addLocker(address account) internal {
    _lockers[account] = true;
    emit LockerAdded(account);
  }

  /**
    * @dev Remove locker, only owner can remove locker
    */
  function _removeLocker(address account) internal {
    _lockers[account] = false;
    emit LockerRemoved(account);
  }

  /**
    * @dev Returns whether the address is locked.
    */
  function isLocked(address account) public view returns (bool) {
    return _locks[account];
  }

  /**
    * @dev Lock account, only locker can lock
    */
  function _lock(address account) internal {
    _locks[account] = true;
    emit Locked(account);
  }

  /**
    * @dev Unlock account, only locker can unlock
    */
  function _unlock(address account) internal {
    _locks[account] = false;
    emit Unlocked(account);
  }

  /**
    * @dev Add time lock, only locker can add
    */
  function _addTimeLock(address account, uint amount, uint expiresAt) internal {
    require(amount > 0);
    require(expiresAt > now);
    _timeLocks[account].push(TimeLock(amount, expiresAt));
  }

  /**
    * @dev Remove time lock, only locker can remove
    * @param account The address want to know the time lock state.
    * @param index Time lock index
    */
  function _removeTimeLock(address account, uint8 index) internal {
    require(_timeLocks[account].length > index);

    uint len = _timeLocks[account].length;
    if (len - 1 != index) { // if it is not last item, swap it
      _timeLocks[account][index] = _timeLocks[account][len - 1];
    }
    _timeLocks[account].pop();
  }

  /**
    * @dev Get time lock array length
    * @param account The address want to know the time lock length.
    * @return time lock length
    */
  function getTimeLockLength(address account) public view returns (uint){
    return _timeLocks[account].length;
  }

  /**
    * @dev Get time lock info
    * @param account The address want to know the time lock state.
    * @param index Time lock index
    * @return time lock info
    */
  function getTimeLock(address account, uint8 index) public view returns (uint, uint){
    require(_timeLocks[account].length > index);
    return (_timeLocks[account][index].amount, _timeLocks[account][index].expiresAt);
  }

  /**
    * @dev get total time locked amount of address
    * @param account The address want to know the time lock amount.
    * @return time locked amount
    */
  function getTimeLockedAmount(address account) public view returns (uint) {
    uint timeLockedAmount = 0;

    uint len = _timeLocks[account].length;
    for (uint i = 0; i < len; i++) {
      if (now < _timeLocks[account][i].expiresAt) {
        timeLockedAmount = timeLockedAmount.add(_timeLocks[account][i].amount);
      }
    }
    return timeLockedAmount;
  }
}
/**
 * @dev Contract for locking mechanism.
 * Locker can add and remove locked account.
 * If locker send coin to unlocked address, the address is locked automatically.
 */
function getTimeLock(address account, uint8 index) public view returns (uint, uint){
  require(_timeLocks[account].length > index);
  return (_timeLocks[account][index].amount, _timeLocks[account][index].expiresAt);
}

/**
 * @dev Get time lock info
 * @param account The address want to know the time lock state.
 * @param index Time lock index
 * @return time lock info
 */