contract TokenDistributor is Ownable {
  using SafeMath for uint256;

  // Event
  event Started();
  event AssignToken(address indexed account, uint256 value);
  event ClaimToken(address indexed account, uint256 value);

  // State
  ERC20 private _token;
  bool private _started;
  mapping (address => uint256) private _tokens;
  uint256 private _totalToken;

  constructor(ERC20 token) public {
    require(token != address(0));

    _token = token;
    _started = false;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is started
   */
  modifier whenStarted() {
    require(_started);
    _;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is not started
   */
  modifier whenNotStarted() {
    require(!_started);
    _;
  }

  /**
   * @dev Start the distribution
   */
  function start() public onlyOwner whenNotStarted {
    require(_token.balanceOf(address(this)) == _totalToken);

    _started = true;
    emit Started();
  }

  /**
   * @dev Check distribution status
   */
  function isStarted() public view returns (bool) {
    return _started;
  }

  /**
   * @dev Total number of tokens to distribution
   */
  function totalToken() public view returns (uint256) {
    return _totalToken;
  }

  /**
   * @dev Get number of token for the specified address
   * @param account The address to query the balance of.
   * @return An uint256 representing the distribution amount for the passed address.
   */
  function tokenOf(address account) public view returns (uint256) {
    return _tokens[account];
  }

  /**
   * @dev Assign token to the specified address
   * @param account The address to be assigned.
   * @param value The token amount to be distributed.
   */
  function assignToken(address account, uint256 value) public onlyOwner whenNotStarted {
    require(account != address(0));
    require(_tokens[account] == 0);

    _totalToken = _totalToken.add(value);
    _tokens[account] = value;

    emit AssignToken(account, value);
  }

  /**
   * @dev Multiple assign token to the list of specified address
   * @param accounts The list of address to be assigned.
   * @param values The list of token amount to be distributed.
   */
  function multipleAssignToken(address[] accounts, uint256[] values) public {
    require(accounts.length > 0);
    require(accounts.length == values.length);

    for (uint256 i = 0; i < accounts.length; i++) {
      assignToken(accounts[i], values[i]);
    }
  }

  /**
   * @dev Claim token for current sender
   */
  function claimToken() public {
    claimTokenFor(msg.sender);
  }

  /**
   * @dev Claim token for the specified address
   * @param account The address to be distributed.
   */
  function claimTokenFor(address account) public whenStarted {
    require(account != address(0));

    uint256 value = _tokens[account];
    require(value > 0);

    _tokens[account] = 0;
    _token.transfer(account, value);

    emit ClaimToken(account, value);
  }

  /**
   * @dev Multiple claim token for the list of specified address
   * @param accounts The list of address to be claimed.
   */
  function multipleClaimToken(address[] accounts) public {
    require(accounts.length > 0);

    for (uint256 i = 0; i < accounts.length; i++) {
      claimTokenFor(accounts[i]);
    }
  }

  /**
   * @dev Withdraw excess token to the specified address
   * @param account The address to transfer to.
   */
  function withdrawExcessToken(address account) public onlyOwner {
    require(account != address(0));

    uint256 contractToken = _token.balanceOf(address(this));
    uint256 excessToken = contractToken.sub(_totalToken);
    require(excessToken > 0);

    _token.transfer(account, excessToken);
  }
}
function assignToken(address account, uint256 value) public onlyOwner whenNotStarted {
  require(account != address(0));
  require(_tokens[account] == 0);

  _totalToken = _totalToken.add(value);
  _tokens[account] = value;

  emit AssignToken(account, value);
}

/**
 * @dev Assign token to the specified address
 * @param account The address to be assigned.
 * @param value The token amount to be distributed.
 */