contract Crowdsale is Ownable, ReentrancyGuard {

  using SafeMath for uint256;  
  
  Airtoto public token;
  //IERC20 public token;
  
  //start and end timestamps where investments are allowed (both inclusive)
  uint256 public   startPreICOStage;
  uint256 public   endPreICOStage;
  uint256 public   startICOStage1;
  uint256 public   endICOStage1;  
  uint256 public   startICOStage2;
  uint256 public   endICOStage2; 
  uint256 public   startICOStage3;
  uint256 public   endICOStage3;  

  //balances for softcap
  mapping(address => uint256) public balances;  
  //token distribution
  uint256 public amountOfTokensSold; 
  uint256 public minimumPayment;  
  //AirDrop
  uint256 public valueAirDrop;
  uint8 public airdropOn;
  uint8 public referralSystemOn;
  mapping (address => uint8) public payedAddress; 
  // rate ETH/USD
  uint256 public rateETHUSD;    
  // address where funds are collected
  address public wallet;

/**
* event for token Procurement logging
* @param contributor who Pledged for the tokens
* @param beneficiary who got the tokens
* @param value weis Contributed for Procurement
* @param amount amount of tokens Procured
*/
  event TokenProcurement(address indexed contributor, address indexed beneficiary, uint256 value, uint256 amount, address indexed referrer, uint256 amountReferrer);

  constructor() public {    
    token = createTokenContract();
	// rate ETH - USD
    rateETHUSD = 10000; //2 decimals
    // start and end timestamps where investments are allowed
    // start/end for stage of ICO
    startPreICOStage  = 1544875200; //Sat, 15 Dec 2018 12:00:00 +0000
    endPreICOStage    = 1546084800; //Sat, 29 Dec 2018 12:00:00 +0000	 
    startICOStage1    = 1546084800; //Sat, 29 Dec 2018 12:00:00 +0000
    endICOStage1      = 1547294400; //Sat, 12 Jan 2019 12:00:00 +0000
    startICOStage2    = 1547294400; //Sat, 12 Jan 2019 12:00:00 +0000  
    endICOStage2      = 1550059200; //Wed, 13 Feb 2019 12:00:00 +0000
    startICOStage3    = 1550059200; //Wed, 13 Feb 2019 12:00:00 +0000 
    endICOStage3      = 1552564800; //Thu, 14 Mar 2019 12:00:00 +0000	

    // minimum payment in ETH	
    minimumPayment = 980000000000000000; // 0.98 ether = ca. 150 USD

    // valueAirDrop in tokens
    valueAirDrop = 1 * 1 ether;	
    // address where funds are collected
    wallet = 0xfc19e8fD7564A48b82a51d106e6D0E6098032811;
  }
  
  function setMinimumPayment(uint256 _minimumPayment) public onlyOwner{
    minimumPayment = _minimumPayment;
  } 
  function setValueAirDrop(uint256 _valueAirDrop) public onlyOwner{
    valueAirDrop = _valueAirDrop;
  } 

  function setRateIco(uint256 _rateETHUSD) public onlyOwner  {
    rateETHUSD = _rateETHUSD;
  }  
  // fallback function can be used to Procure tokens
  function () external payable {
    buyTokens(msg.sender);
  }
  
  function createTokenContract() internal returns (Airtoto) {
    return new Airtoto();
  }
  
  function getRateTokeUSD() public view returns (uint256) {
    uint256 rate; //6 decimals
    if (now >= startPreICOStage && now < endPreICOStage){
      rate = 100000;    
    }	
    if (now >= startICOStage1 && now < endICOStage1){
      rate = 100000;    
    } 
    if (now >= startICOStage2 && now < endICOStage2){
      rate = 150000;    
    }    
    if (now >= startICOStage3 && now < endICOStage3){
      rate = 200000;    
    }    	
    return rate;
  }
  
  function getRateIcoWithBonus() public view returns (uint256) {
    uint256 bonus;
    if (now >= startPreICOStage && now < endPreICOStage){
      bonus = 20;    
    }
    if (now >= startICOStage1 && now < endICOStage1){
      bonus = 15;    
    }
    if (now >= startICOStage2 && now < endICOStage2){
      bonus = 10;    
    }   
    if (now >= startICOStage3 && now < endICOStage3){
      bonus = 5;    
    }       
    return rateETHUSD + rateETHUSD.mul(bonus).div(100);
  }  
 
  function bytesToAddress(bytes source) internal pure returns(address) {
    uint result;
    uint mul = 1;
    for(uint i = 20; i > 0; i--) {
      result += uint8(source[i-1])*mul;
      mul = mul*256;
    }
    return address(result);
  }
  function setAirdropOn(uint8 _flag) public onlyOwner{
    airdropOn = _flag;
  } 
  function setReferralSystemOn(uint8 _flag) public onlyOwner{
    referralSystemOn = _flag;
  }   
  function buyTokens(address _beneficiary) public nonReentrant payable {
    uint256 tokensAmount;
    uint256 weiAmount = msg.value;
    uint256 rate;
	uint256 referrerTokens;
	uint256 restTokensAmount;
	uint256 restWeiAmount;
	address referrer; 
    address _this = this;
    uint256 rateTokenUSD;  
    require(now >= startPreICOStage);
    require(now <= endICOStage3);
	require(token.balanceOf(_this) > 0);
    require(_beneficiary != address(0));
	
	if (weiAmount == 0 && airdropOn == 1){ 
	  require(payedAddress[_beneficiary] == 0);
      payedAddress[_beneficiary] = 1;
	  token.transferForICO(_beneficiary, valueAirDrop);
	}
	else{	
	  require(weiAmount >= minimumPayment);
      rate = getRateIcoWithBonus();
	  rateTokenUSD = getRateTokeUSD();
      tokensAmount = weiAmount.mul(rate).mul(10000).div(rateTokenUSD);
	  // referral system
	  if(msg.data.length == 20 && referralSystemOn == 1) {
        referrer = bytesToAddress(bytes(msg.data));
        require(referrer != msg.sender);
	    // add tokensAmount to the referrer
        referrerTokens = tokensAmount.mul(5).div(100);
	    // add tokensAmount to the referral
	    tokensAmount = tokensAmount + tokensAmount.mul(5).div(100);
      }
	  // last sale of tokens
      if (tokensAmount.add(referrerTokens) > token.balanceOf(_this)) {
	    restTokensAmount = tokensAmount.add(referrerTokens) - token.balanceOf(_this);
	    tokensAmount = token.balanceOf(_this);
	    referrerTokens = 0;
	    restWeiAmount = restTokensAmount.mul(rateTokenUSD).div(rate).div(10000);
	  }
        amountOfTokensSold = amountOfTokensSold.add(tokensAmount);
	    balances[_beneficiary] = balances[_beneficiary].add(msg.value);
	  if (referrerTokens != 0){
        token.transferForICO(referrer, referrerTokens);	  
	  }
	  if (restWeiAmount != 0){
	    _beneficiary.transfer(restWeiAmount);
		weiAmount = weiAmount.sub(restWeiAmount);
	  }
      token.transferForICO(_beneficiary, tokensAmount);
	  wallet.transfer(weiAmount);
      emit TokenProcurement(msg.sender, _beneficiary, weiAmount, tokensAmount, referrer, referrerTokens);
	}
  }
  function manualSendTokens(address _to, uint256 _value) public onlyOwner{
    address _this = this;
    require(_value > 0);
	require(_value <= token.balanceOf(_this));
    require(_to != address(0));
    amountOfTokensSold = amountOfTokensSold.add(_value);
    token.transferForICO(_to, _value);
	emit TokenProcurement(msg.sender, _to, 0, _value, address(0), 0);
  } 
  function pause() public onlyOwner{
    token.pause();
  }
  function unpause() public onlyOwner{
    token.unpause();
  }
 
}
function () external payable {
  buyTokens(msg.sender);
}

// fallback function can be used to Procure tokens