contract BasicToken is ERC20Basic, Ownable {
  using SafeMath for uint256;

  mapping(address => uint256) balances;
  mapping(address => uint256) bonusTokens;
  mapping(address => uint256) bonusReleaseTime;
  
  mapping(address => bool) internal blacklist;
  address[] internal blacklistHistory;
  
  bool public isTokenReleased = false;
  
  address addressSaleContract;
  event BlacklistUpdated(address badUserAddress, bool registerStatus);
  event TokenReleased(address tokenOwnerAddress, bool tokenStatus);

  uint256 totalSupply_;

  modifier onlyBonusSetter() {
      require(msg.sender == owner || msg.sender == addressSaleContract);
      _;
  }

  /**
  * @dev total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return totalSupply_;
  }

  /**
  * @dev transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);
    require(isTokenReleased);
    require(!blacklist[_to]);
    require(!blacklist[msg.sender]);
    
    if (bonusReleaseTime[msg.sender] > block.timestamp) {
        require(_value <= balances[msg.sender].sub(bonusTokens[msg.sender]));
    }
    
    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256) {
    require(msg.sender == owner || !blacklist[_owner]);
    require(!blacklist[msg.sender]);
    return balances[_owner];
  }

  /**
  * @dev Set the specified address to blacklist.
  * @param _badUserAddress The address of bad user.
  */
  function registerToBlacklist(address _badUserAddress) onlyOwner public {
      if (blacklist[_badUserAddress] != true) {
	  	  blacklist[_badUserAddress] = true;
          blacklistHistory.push(_badUserAddress);
	  }
      emit BlacklistUpdated(_badUserAddress, blacklist[_badUserAddress]);   
  }
  
  /**
  * @dev Remove the specified address from blacklist.
  * @param _badUserAddress The address of bad user.
  */
  function unregisterFromBlacklist(address _badUserAddress) onlyOwner public {
      if (blacklist[_badUserAddress] == true) {
	  	  blacklist[_badUserAddress] = false;
	  }
      emit BlacklistUpdated(_badUserAddress, blacklist[_badUserAddress]);
  }

  /**
  * @dev Check the address registered in blacklist.
  * @param _address The address to check.
  * @return a bool representing registration of the passed address.
  */
  function checkBlacklist (address _address) onlyOwner public view returns (bool) {
      return blacklist[_address];
  }

  function getblacklistHistory() onlyOwner public view returns (address[]) {
      return blacklistHistory;
  }
  
  /**
  * @dev Release the token (enable all token functions).
  */
  function releaseToken() onlyOwner public {
      if (isTokenReleased == false) {
		isTokenReleased = true;
	  }
      emit TokenReleased(msg.sender, isTokenReleased);
  }
  
  /**
  * @dev Withhold the token (disable all token functions).
  */
  function withholdToken() onlyOwner public {
      if (isTokenReleased == true) {
		isTokenReleased = false;
      }
	  emit TokenReleased(msg.sender, isTokenReleased);
  }
  
  /**
  * @dev Set bonus token amount and bonus token release time for the specified address.
  * @param _tokenHolder The address of bonus token holder
  *        _bonusTokens The bonus token amount
  *        _holdingPeriodInDays Bonus token holding period (in days) 
  */  
  function setBonusTokenInDays(address _tokenHolder, uint256 _bonusTokens, uint256 _holdingPeriodInDays) onlyBonusSetter public {
      bonusTokens[_tokenHolder] = _bonusTokens;
      bonusReleaseTime[_tokenHolder] = SafeMath.add(block.timestamp, _holdingPeriodInDays * 1 days);
  }

  /**
  * @dev Set bonus token amount and bonus token release time for the specified address.
  * @param _tokenHolder The address of bonus token holder
  *        _bonusTokens The bonus token amount
  *        _bonusReleaseTime Bonus token release time
  */  
  function setBonusToken(address _tokenHolder, uint256 _bonusTokens, uint256 _bonusReleaseTime) onlyBonusSetter public {
      bonusTokens[_tokenHolder] = _bonusTokens;
      bonusReleaseTime[_tokenHolder] = _bonusReleaseTime;
  }
  
  /**
  * @dev Set bonus token amount and bonus token release time for the specified address.
  * @param _tokenHolders The address of bonus token holder ["0x...", "0x...", ...] 
  *        _bonusTokens The bonus token amount [0,0, ...] 
  *        _bonusReleaseTime Bonus token release time
  */  
  function setBonusTokens(address[] _tokenHolders, uint256[] _bonusTokens, uint256 _bonusReleaseTime) onlyBonusSetter public {
      for (uint i = 0; i < _tokenHolders.length; i++) {
        bonusTokens[_tokenHolders[i]] = _bonusTokens[i];
        bonusReleaseTime[_tokenHolders[i]] = _bonusReleaseTime;
      }
  }

  function setBonusTokensInDays(address[] _tokenHolders, uint256[] _bonusTokens, uint256 _holdingPeriodInDays) onlyBonusSetter public {
      for (uint i = 0; i < _tokenHolders.length; i++) {
        bonusTokens[_tokenHolders[i]] = _bonusTokens[i];
        bonusReleaseTime[_tokenHolders[i]] = SafeMath.add(block.timestamp, _holdingPeriodInDays * 1 days);
      }
  }

  /**
  * @dev Set the address of the crowd sale contract which can call setBonusToken method.
  * @param _addressSaleContract The address of the crowd sale contract.
  */
  function setBonusSetter(address _addressSaleContract) onlyOwner public {
      addressSaleContract = _addressSaleContract;
  }
  
  function getBonusSetter() public view returns (address) {
      require(msg.sender == addressSaleContract || msg.sender == owner);
      return addressSaleContract;
  }
  
  /**
  * @dev Display token holder's bonus token amount.
  * @param _bonusHolderAddress The address of bonus token holder.
  */
  function checkBonusTokenAmount (address _bonusHolderAddress) public view returns (uint256) {
      return bonusTokens[_bonusHolderAddress];
  }
  
  /**
  * @dev Display token holder's remaining bonus token holding period.
  * @param _bonusHolderAddress The address of bonus token holder.
  */
  function checkBonusTokenHoldingPeriodRemained (address _bonusHolderAddress) public view returns (uint256) {
      uint256 returnValue = 0;
      if (bonusReleaseTime[_bonusHolderAddress] > now) {
          returnValue = bonusReleaseTime[_bonusHolderAddress].sub(now);
      }
      return returnValue;
  }
}
/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
function setBonusSetter(address _addressSaleContract) onlyOwner public {
      addressSaleContract = _addressSaleContract;
  }
/**
 * @dev Set the address of the crowd sale contract which can call setBonusToken method.
 * @param _addressSaleContract The address of the crowd sale contract.
 */