contract FTICrowdsale is Ownable {
  using SafeMath for uint256;

  uint256 public rate;
  uint256 public minPurchase;
  uint256 public maxSupply;

  // Tokens, reserved for owners
  uint256 public stage1ReleaseTime;
  uint256 public stage2ReleaseTime;
  uint256 public stage3ReleaseTime;

  // Amount of reserved tokens
  uint256 public stage1Amount;
  uint256 public stage2Amount;
  uint256 public stage3Amount;

  bool public stage1Released;
  bool public stage2Released;
  bool public stage3Released;

  /**
   * @dev Money is sent to this wallet upon tokens purchase
   */
  address payable public wallet;

  bool public isPaused;

  FTIToken public token;

  constructor () public {
    token = new FTIToken();

    minPurchase = 0.00000000000005 ether; // price of the minimum part of the token
    rate = 0.000194 ether;

    maxSupply = 2395600000 * (10 ** 10); // 2395600000 * (10^(decimals))
    wallet = 0x8D44D27D2AF7BE632baA340eA52E443756ea1aD3;

    stage1ReleaseTime = now + 180 days; // 6 months
    stage2ReleaseTime = now + 270 days; // 9 months
    stage3ReleaseTime = now + 365 days; // 12 months

    stage1Amount = 299540000 * (10 ** uint256(token.decimals()));
    stage2Amount = 299540000 * (10 ** uint256(token.decimals()));
    stage3Amount = 299540000 * (10 ** uint256(token.decimals()));
  }

  /**
   * @dev This function suspends the tokens purchase
   */
  function pause() public onlyOwner {
    require(!isPaused, 'Sales must be not paused');
    isPaused = true;
  }

  /**
   * @dev This function resumes the purchase of tokens
   */
  function unpause() public onlyOwner {
    require(isPaused, 'Sales must be paused');
    isPaused = false;
  }

  /**
   * @dev Function set new wallet address.
   * @param newWallet Address of new wallet.
   */
  function changeWallet(address payable newWallet) public onlyOwner {
    require(newWallet != address(0));
    wallet = newWallet;
  }

  /**
   * @dev This function set new token owner.
   */
  function transferTokenOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    token.transferOwnership(newOwner);
  }

  /**
   * @dev This function burn all unsold tokens.
   */
  function burnUnsold() public onlyOwner {
    token.burn(token.balanceOf(address(this)));
  }

  /**
   * @dev This function releases tokens reserved for owners.
   */
  function releaseStage1() public onlyOwner {
    require(now > stage1ReleaseTime, 'Release time has not come yet');
    require(stage1Released != true, 'Tokens already released');

    stage1Released = true;
    token.mint(wallet, stage1Amount);
  }

  /**
   * @dev This function releases tokens reserved for owners.
   */
  function releaseStage2() public onlyOwner {
    require(now > stage2ReleaseTime, 'Release time has not come yet');
    require(stage2Released != true, 'Tokens already released');

    stage2Released = true;
    token.mint(wallet, stage2Amount);
  }

  /**
   * @dev This function releases tokens reserved for owners.
   */
  function releaseStage3() public onlyOwner {
    require(now > stage3ReleaseTime, 'Release time has not come yet');
    require(stage3Released != true, 'Tokens already released');

    stage3Released = true;
    token.mint(wallet, stage3Amount);
  }

  /**
   * @dev Fallback function
   */
  function() external payable {
    buyTokens();
  }

  function buyTokens() public payable {
    require(!isPaused, 'Sales are temporarily paused');

    address payable inv = msg.sender;
    require(inv != address(0));

    uint256 weiAmount = msg.value;
    require(weiAmount >= minPurchase, 'Amount of ether is not enough to buy even the smallest token part');

    uint256 cleanWei; // amount of wei to use for purchase excluding change and max supply overflows
    uint256 change;
    uint256 tokens;
    uint256 tokensNoBonuses;
    uint256 totalSupply;
    uint256 supply;

    tokensNoBonuses = weiAmount.mul(1E10).div(rate);

    if (weiAmount >= 10 ether) {
      tokens = tokensNoBonuses.mul(112).div(100);
    } else if (weiAmount >= 5 ether) {
      tokens = tokensNoBonuses.mul(105).div(100);
    } else {
      tokens = tokensNoBonuses;
    }

    totalSupply = token.totalSupply();
    supply = totalSupply.sub(token.balanceOf(address(this)));

    if (supply.add(tokens) > maxSupply) {
      tokens = maxSupply.sub(supply);
      require(tokens > 0, 'There are currently no tokens for sale');
      if (tokens >= tokensNoBonuses) {
        cleanWei = weiAmount;
      } else {
        cleanWei = tokens.mul(rate).div(1E10);
        change = weiAmount.sub(cleanWei);
      }
    } else {
      cleanWei = weiAmount;
    }

    if (token.balanceOf(address(this)) >= tokens) {
      token.transfer(inv, tokens);
    } else if (token.balanceOf(address(this)) == 0) {
      token.mint(inv, tokens);
    } else {
      uint256 mintAmount = tokens.sub(token.balanceOf(address(this)));

      token.mint(address(this), mintAmount);
      token.transfer(inv, tokens);
    }

    wallet.transfer(cleanWei);

    if (change > 0) {
      inv.transfer(change); 
    }
  }
}
function releaseStage1() public onlyOwner {
  require(now > stage1ReleaseTime, 'Release time has not come yet');
  require(stage1Released != true, 'Tokens already released');

  stage1Released = true;
  token.mint(wallet, stage1Amount);
}

/**
 * @dev This function releases tokens reserved for owners.
 */