pragma solidity ^0.4.24;

 
/**
 * @title ERC20 interface + Mint function
 * 
 */
contract ERC20 {
  function totalSupply() public view returns (uint256);
  function balanceOf(address who) public view returns (uint256);
  function transfer(address to, uint256 value) public returns (bool);
  function allowance(address owner, address spender) public view returns (uint256);
  function transferFrom(address from, address to, uint256 value) public returns (bool);
  function approve(address spender, uint256 value) public returns (bool);
  function mint(address _to, uint256 _amount) public returns (bool);
  event Approval(address indexed owner, address indexed spender, uint256 value);
  event Transfer(address indexed from, address indexed to, uint256 value);
}

 
/**
 * @title OwnableWithAdmin 
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract OwnableWithAdmin {
  address public owner;
  address public adminOwner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() public {
    owner = msg.sender;
    adminOwner = msg.sender;
  }
  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Throws if called by any account other than the admin.
   */
  modifier onlyAdmin() {
    require(msg.sender == adminOwner);
    _;
  }

  /**
   * @dev Throws if called by any account other than the owner or admin.
   */
  modifier onlyOwnerOrAdmin() {
    require(msg.sender == adminOwner || msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    emit OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

  /**
   * @dev Allows the current adminOwner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferAdminOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    emit OwnershipTransferred(adminOwner, newOwner);
    adminOwner = newOwner;
  }

}

 
/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  /**
  * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }

  function uint2str(uint i) internal pure returns (string){
      if (i == 0) return "0";
      uint j = i;
      uint length;
      while (j != 0){
          length++;
          j /= 10;
      }
      bytes memory bstr = new bytes(length);
      uint k = length - 1;
      while (i != 0){
          bstr[k--] = byte(48 + i % 10);
          i /= 10;
      }
      return string(bstr);
  }
 
  
}

 
/**
 * @title Crowdsale
 * Contract is payable.
 * Direct transfer of tokens with no allocation.
 *
 *
 */
contract Crowdsale is OwnableWithAdmin {
  using SafeMath for uint256;

  uint256 private constant DECIMALFACTOR = 10**uint256(18);

  event FundTransfer(address backer, uint256 amount, bool isContribution);
  event TokenPurchase(address indexed purchaser, uint256 value, uint256 amount  );
   
  //Is active
  bool internal crowdsaleActive = true;

  // The token being sold
  ERC20 public token;

  // Address where funds are collected
  address public wallet;

  // How many weis one token costs 
  uint256 public rate;

  // Minimum weis one token costs 
  uint256 public minRate; 

  // Minimum buy in weis 
  uint256 public minWeiAmount = 100000000000000000; 

  // Amount of tokens Raised
  uint256 public tokensTotal = 0;

  // Amount of wei raised
  uint256 public weiRaised;

  // Max token amount
  uint256 public hardCap = 0;

  
  // start and end timestamps where investments are allowed (both inclusive)
  uint256 public startTime;
  uint256 public endTime;
  
  //Whitelist
  mapping(address => bool) public whitelist;
  
 
  constructor(uint256 _startTime, uint256 _endTime, address _wallet, ERC20 _token) public {
     
    require(_wallet != address(0));
    require(_token != address(0));

     

    startTime   = _startTime;
    endTime     = _endTime;
  
    wallet = _wallet;
    token = _token;
  }

  // -----------------------------------------
  // Crowdsale external interface
  // -----------------------------------------

  /**
   * @dev fallback function ***DO NOT OVERRIDE***
   */
  function () public payable  {

    //Check if msg sender value is more then 0
    require( msg.value > 0 );

    //Validate crowdSale
    require(isCrowdsaleActive());

    //Validate whitelisted
    require(isWhitelisted(msg.sender));

    // wei sent
    uint256 _weiAmount = msg.value;

    // Minimum buy in weis 
    require(_weiAmount>minWeiAmount);

    // calculate token amount to be created after rate update
    uint256 _tokenAmount = _calculateTokens(_weiAmount);

    //Check hardCap 
    require(_validateHardCap(_tokenAmount));

    //Mint tokens and transfer tokens to buyer
    require(token.mint(msg.sender, _tokenAmount));

    //Update state
    tokensTotal = tokensTotal.add(_tokenAmount);

    //Update state
    weiRaised = weiRaised.add(_weiAmount);

    //Funds log function
    emit TokenPurchase(msg.sender, _tokenAmount , _weiAmount);

    //Transfer funds to wallet
    _forwardFunds();

 
  }

 
  // send ether to the fund collection wallet
  function _forwardFunds() internal {
    wallet.transfer(msg.value);
  }


  /*
   * @dev fiat and btc transfer
   * The company received FIAT or BTC and admin will mint the 
   * amount of tokens directly to the receiving partyâ€™s wallet
   *
  **/
  function fiatTransfer(address _recipient, uint256 _tokenAmount, uint256 _weiAmount) onlyOwnerOrAdmin public{
    
    require(_tokenAmount > 0);      
    require(_recipient != address(0)); 

    //Validate crowdSale
    require(isCrowdsaleActive());

    //Validate whitelisted
    require(isWhitelisted(_recipient));

    // Minimum buy in weis 
    require(_weiAmount>minWeiAmount); 

    //Check hardCap 
    require(_validateHardCap(_tokenAmount));

    //Mint tokens and transfer tokens to buyer
    require(token.mint(_recipient, _tokenAmount));

    //Update state
    tokensTotal = tokensTotal.add(_tokenAmount);

    //Update state
    weiRaised = weiRaised.add(_weiAmount);

    //Funds log function
    emit TokenPurchase(_recipient, _tokenAmount, _weiAmount);

  }

  // @return true if the transaction can buy tokens
  function isCrowdsaleActive() public view returns (bool) {
    bool withinPeriod = now >= startTime && now <= endTime;
    return withinPeriod;
  }

  function _validateHardCap(uint256 _tokenAmount) internal view returns (bool) {
      return tokensTotal.add(_tokenAmount) <= hardCap;
  }


  function _calculateTokens(uint256 _wei) internal view returns (uint256) {
    return _wei.mul(DECIMALFACTOR).div(rate);
  }

 

  /**
   * @dev Update current rate
   * @param _rate How many wei one token costs
   * We need to be able to update the rate as the eth rate changes
   */ 
  function setRate(uint256 _rate) onlyOwnerOrAdmin public{
    require(_rate > minRate);
    rate = _rate;
  }


  function addToWhitelist(address _buyer) onlyOwnerOrAdmin public{
    require(_buyer != 0x0);     
    whitelist[_buyer] = true;
  }
  

  function addManyToWhitelist(address[] _beneficiaries) onlyOwnerOrAdmin public{
    for (uint256 i = 0; i < _beneficiaries.length; i++) {
      if(_beneficiaries[i] != 0x0){
        whitelist[_beneficiaries[i]] = true;
      }
    }
  }


  function removeFromWhitelist(address _buyer) onlyOwnerOrAdmin public{
    whitelist[_buyer] = false;
  }


  // @return true if buyer is whitelisted
  function isWhitelisted(address _buyer) public view returns (bool) {
      return whitelist[_buyer];
  }


  

  // Owner can transfer tokens that are sent here by mistake
  function refundTokens(address _recipient, ERC20 _token) public onlyOwner {
    uint256 balance = _token.balanceOf(this);
    require(_token.transfer(_recipient, balance));
  }


}

 
/**
 * @title BYTMCrowdsale
 *  
 *
*/
contract BYTMCrowdsale is Crowdsale {
  constructor(   
    uint256 _startTime, 
    uint256 _endTime,  
    address _wallet, 
    ERC20 _token
  ) public Crowdsale( _startTime, _endTime,  _wallet, _token) {

    // Initial rate
    //What one token cost in wei
    rate = 870000000000000;   

    // Initial minimum rate
    // rate can't be set below this
    // 0.12 euro
    minRate = 670000000000000;  

    // HardCap 1,000,000,000
    hardCap = 1000000000 * (10**uint256(18)); 

    //min buy amount in wei
    // 100euro
    minWeiAmount = 545000000000000000;

  }
}