contract Token is RBAC, Pausable{
    using SafeMath for uint256;

    BwinCommons internal commons;
    function setCommons(address _addr) public onlyAdmin {
        commons = BwinCommons(_addr);
    }
    event TokenApproval(address indexed owner, address indexed spender, uint256 value);
    event TokenTransfer(address indexed from, address indexed to, uint256 value);
    event MintForSale(address indexed to, uint256 amount);
    event MintForWorker(address indexed to, uint256 amount);
    event MintForUnlock(address indexed to, uint256 amount);

    function Token() public {
        addRole(msg.sender, ROLE_ADMIN);
    }

    function totalSupply() public view returns (uint256) {
      TokenData td = TokenData(commons.get("TokenData"));
      return td.totalSupply();
    }
    function balanceOf(address _owner) public view returns (uint256) {
      TokenData td = TokenData(commons.get("TokenData"));
      return td.balanceOf(_owner);
    }
    function _transferFrom(address _sender, address _from, address _to, uint256 _value) external whenNotPaused onlyRole("FRONT_TOKEN_USER") returns (bool) {
      InterestHolder ih = InterestHolder(commons.get("InterestHolder"));
      TokenData td = TokenData(commons.get("TokenData"));
      uint256 _balanceFrom = balanceOf(_from);
      uint256 _balanceTo = balanceOf(_to);
      uint256 _allow = allowance(_from, _sender);
      require(_from != address(0));
      require(_sender != address(0));
      require(_to != address(0));
      require(_value <= _balanceFrom);
      require(_value <= _allow);
      td.setBalance(_from,_balanceFrom.sub(_value));
      td.setBalance(_to,_balanceTo.add(_value));
      td.setAllowance(_from, _sender, _allow.sub(_value));
      if(ih != address(0)){
        ih.receiveBalanceUpdate(_from);
        ih.receiveBalanceUpdate(_to);
      }
      emit TokenTransfer(_from, _to, _value);
      return true;
    }

    function allowance(address _owner, address _spender) public view returns (uint256) {
      TokenData td = TokenData(commons.get("TokenData"));
      return td.allowance(_owner,_spender);
    }
    function _approve(address _sender, address _spender, uint256 _value) public onlyRole("FRONT_TOKEN_USER")  whenNotPaused returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      return td.setAllowance(_sender, _spender, _value);
    }
    function _increaseApproval(address _sender, address _spender, uint _addedValue) public onlyRole("FRONT_TOKEN_USER") whenNotPaused returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      td.setAllowance(_sender, _spender, allowance(_sender, _spender).add(_addedValue));
      emit TokenApproval(_sender, _spender, allowance(_sender, _spender));
      return true;
    }
    function _decreaseApproval(address _sender, address _spender, uint _subtractedValue) public onlyRole("FRONT_TOKEN_USER") whenNotPaused returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      uint oldValue = allowance(_sender, _spender);
      if (_subtractedValue > oldValue) {
          td.setAllowance(_sender, _spender, 0);
          //allowed[msg.sender][_spender] = 0;
      } else {
          td.setAllowance(_sender, _spender, oldValue.sub(_subtractedValue));
          //allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
      }
      emit TokenApproval(_sender, _spender, allowance(_sender, _spender));
      return true;
    }

    function unlockAmount(address _to, uint256 _amount) external onlyAdmin returns (bool){
      TokenData td = TokenData(commons.get("TokenData"));
      require(td.totalSupply().add(_amount) <= td.TotalCapacity());
      uint256 unlockedAmount = td.valueOf("unlockedAmount");
      if(_mint(_to, _amount)){
          td.setValue("unlockedAmount",unlockedAmount.add(_amount));
          emit MintForUnlock(_to, _amount);
          return true;
      }
      return false;
    }

    function _mint(address _to, uint256 _amount) internal returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      InterestHolder ih = InterestHolder(commons.get("InterestHolder"));
      require(_to != address(0));
      require(_amount > 0);
      uint256 totalMinted = td.valueOf("totalMinted");
      td.setTotal(td.totalSupply().add(_amount));
      td.setBalance(_to,balanceOf(_to).add(_amount));
      td.setValue("totalMinted",totalMinted.add(_amount));
      if(address(ih) != address(0)){
        ih.receiveBalanceUpdate(_to);
      }
      return true;
    }

    function mintForSale(address _to, uint256 _amount) external onlyRole("TOKEN_SALE") whenNotPaused returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      require(td.totalSupply().add(_amount) <= td.TotalCapacity());
      uint256 saledAmount = td.valueOf("saledAmount");
      if(_mint(_to, _amount)){
          td.setValue("saledAmount",saledAmount.add(_amount));
          emit MintForSale(_to, _amount);
          return true;
      }
      return false;
    }
    function mintForWorker(address _to, uint256 _amount) external onlyRole("TOKEN_WORKER") whenNotPaused returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      require(td.totalSupply().add(_amount) <= td.TotalCapacity());
      uint256 minedAmount = td.valueOf("minedAmount");
      if(_mint(_to, _amount)){
        td.setValue("minedAmount",minedAmount.add(_amount));
        emit MintForWorker(_to, _amount);
        return true;
      }
      return false;
    }
    function transfer(address _from, address _to, uint _value, bytes _data) external whenNotPaused onlyRole("FRONT_TOKEN_USER")  returns (bool success) {

        if (isContract(_to)) {
            return transferToContract(_from, _to, _value, _data);
        }else {
            return transferToAddress(_from, _to, _value);
        }
    }
    //assemble the given address bytecode. If bytecode exists then the _addr is a contract.
    function isContract(address _addr) internal view returns (bool) {
        uint length;
        assembly {
            //retrieve the size of the code on target address, this needs assembly
            length := extcodesize(_addr)
        }
        return (length > 0);
    }
    function _transfer(address _from, address _to, uint256 _value) internal returns (bool) {
      TokenData td = TokenData(commons.get("TokenData"));
      InterestHolder ih = InterestHolder(commons.get("InterestHolder"));
      require(_to != address(0));
      require(_value <= balanceOf(_from));
      td.setBalance(_from,balanceOf(_from).sub(_value));
      td.setBalance(_to,balanceOf(_to).add(_value));
      if(ih != address(0)){
        ih.receiveBalanceUpdate(_from);
        ih.receiveBalanceUpdate(_to);
      }
      emit TokenTransfer(_from, _to, _value);
      return true;
    }

    //function that is called when transaction target is an address
    function transferToAddress(address _from, address _to, uint _value) internal returns (bool success) {
        require(balanceOf(_from) >= _value);
        require(_transfer(_from, _to, _value));
        emit TokenTransfer(_from, _to, _value);
        return true;
    }

    //function that is called when transaction target is a contract
    function transferToContract(address _from, address _to, uint _value, bytes _data) internal returns (bool success) {
        require(balanceOf(_from) >= _value);
        require(_transfer(_from, _to, _value));
        ContractReceiver receiver = ContractReceiver(_to);
        receiver.tokenFallback(msg.sender, _value, _data);
        emit TokenTransfer(msg.sender, _to, _value);
        return true;
    }



}
function transferToContract(address _from, address _to, uint _value, bytes _data) internal returns (bool success) {
    require(balanceOf(_from) >= _value);
    require(_transfer(_from, _to, _value));
    ContractReceiver receiver = ContractReceiver(_to);
    receiver.tokenFallback(msg.sender, _value, _data);
    emit TokenTransfer(msg.sender, _to, _value);
    return true;
}

//function that is called when transaction target is a contract