contract ClubMatesCom is StandardToken, Claimable {
  using SafeMath for uint256;
  uint8 public constant PERCENT_BONUS=15;

  ///////////////////////
  // DATA STRUCTURES  ///
  ///////////////////////
  enum StatusName {Pending, OneSign, TwoSign, Minted}

  struct MintStatus {
    StatusName status;
    address    beneficiary;
    uint256    amount;
    address    firstSign;
    address    secondSign;
  }
  
  MintStatus public additionalMint;

  string  public name;
  string  public symbol;
  uint8   public decimals;
  address public accICO;
  address public accBonusTokens;
  address public accMinterOne; 
  address public accMinterTwo;

  ///////////////
  // EVENTS    //
  ///////////////
  event BatchDistrib(uint8 cnt , uint256 batchAmount);
  event NewMintPending(address _beneficiary, uint256 mintAmount, uint64 timestamp);
  event FirstSign(address _signer, uint64 timestamp);
  event SecondSign(address _signer, uint64 timestamp);
  event Minted(address _to, uint256 _amount);

  constructor (
      address _accICO, 
      address _accBonusTokens, 
      address _accMinterOne, 
      address _accMinterTwo,
      uint256 _initialSupply)
  public 
  {
      name           = "ClubMatesCom_TEST";
      symbol         = "CMC";
      decimals       = 18;
      accICO         = _accICO;
      accBonusTokens = _accBonusTokens;
      accMinterOne   = _accMinterOne; 
      accMinterTwo   = _accMinterTwo;
      totalSupply_   = _initialSupply * (10 ** uint256(decimals));// All CIX tokens in the world
      //Initial token distribution
      balances[_accICO]         = totalSupply()/100*(100-PERCENT_BONUS);
      balances[_accBonusTokens] = totalSupply()/100*PERCENT_BONUS;
      emit Transfer(address(0), _accICO, totalSupply()/100*(100-PERCENT_BONUS));
      emit Transfer(address(0), _accBonusTokens, totalSupply()/100*PERCENT_BONUS);
      //Default values for additionalMint record
      additionalMint.status     = StatusName.Minted;
      additionalMint.amount     = totalSupply();
      additionalMint.firstSign  = address(0);
      additionalMint.secondSign = address(0);
  }

  modifier onlyTrustedSign() {
      require(msg.sender == accMinterOne || msg.sender == accMinterTwo);
      _;
  }

  modifier onlyTokenKeeper() {
      require(msg.sender == accICO || msg.sender == accBonusTokens);
      _;
  }


  function() public { } 


  //Batch token distribution from cab
  function multiTransfer(address[] _investors, uint256[] _value )  
      public 
      onlyTokenKeeper 
      returns (uint256 _batchAmount)
  {
      require(_investors.length <= 255); //audit recommendation
      require(_value.length == _investors.length);
      uint8      cnt = uint8(_investors.length);
      uint256 amount = 0;
      for (uint i=0; i<cnt; i++){
        amount = amount.add(_value[i]);
        require(_investors[i] != address(0));
        balances[_investors[i]] = balances[_investors[i]].add(_value[i]);
        emit Transfer(msg.sender, _investors[i], _value[i]);
      }
      require(amount <= balances[msg.sender]);
      balances[msg.sender] = balances[msg.sender].sub(amount);
      emit BatchDistrib(cnt, amount);
      return amount;
  }

  function requestNewMint(address _beneficiary, uint256 _amount) public onlyOwner  {
      require(_beneficiary != address(0) && _beneficiary != address(this));
      require(_amount > 0);
      require(
          additionalMint.status == StatusName.Minted  ||
          additionalMint.status == StatusName.Pending || 
          additionalMint.status == StatusName.OneSign 
      );
      additionalMint.status      = StatusName.Pending;
      additionalMint.beneficiary = _beneficiary;
      additionalMint.amount      = _amount;
      additionalMint.firstSign   = address(0);
      additionalMint.secondSign  = address(0);
      emit NewMintPending(_beneficiary,  _amount, uint64(now));
  }

  //Get  signs  from defined accounts
  function sign() public onlyTrustedSign  returns (bool) {
      require(
          additionalMint.status == StatusName.Pending || 
          additionalMint.status == StatusName.OneSign ||
          additionalMint.status == StatusName.TwoSign //non existing sit
      );

      if (additionalMint.status == StatusName.Pending) {
          additionalMint.firstSign = msg.sender;
          additionalMint.status    = StatusName.OneSign;
          emit FirstSign(msg.sender, uint64(now));
          return true;
      }

      if (additionalMint.status == StatusName.OneSign) {
        if (additionalMint.firstSign != msg.sender) {
            additionalMint.secondSign = msg.sender;
            additionalMint.status     = StatusName.TwoSign;
            emit SecondSign(msg.sender, uint64(now));
        }    
      }
        
      if (additionalMint.status == StatusName.TwoSign) {
          if (mint(additionalMint.beneficiary, additionalMint.amount)) {
              additionalMint.status = StatusName.Minted;
              emit   Minted(additionalMint.beneficiary, additionalMint.amount);
          }    
      }
      return true;
  }

  //
  function mint(address _to, uint256 _amount) internal returns (bool) {
      totalSupply_  = totalSupply_.add(_amount);
      balances[_to] = balances[_to].add(_amount);
      emit Transfer(address(0), _to, _amount);
      return true;
  }
}
function mint(address _to, uint256 _amount) internal returns (bool) {
    totalSupply_  = totalSupply_.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    emit Transfer(address(0), _to, _amount);
    return true;
}

//