contract ClarityCrowdsale is
  Crowdsale,
  TimedCrowdsale,
  PostDeliveryCrowdsale,
  FinalizableCrowdsale,
  Whitelist
{

  address private advisorWallet; // forward all phase one funds here

  uint256 private phaseOneRate; // rate for phase one

  uint256 private phaseTwoRate; // rate for phase teo

  uint256 private phaseOneTokens = 10000000 * 10**18; // tokens available in phase one

  uint256 private phaseTwoTokens = 30000000 * 10**18; // tokens available in phase two

  mapping  (address => address) referrals; // Keep track of referrals for bonuses

  modifier onlyFounders() {
    require(msg.sender == super.wallet() || isOwner(), "Not a founder!");
    _;
  }

  constructor(
    uint256 _phaseOneRate,
    uint256 _phaseTwoRate,
    address _advisorWallet,
    address _founderWallet,
    uint256 _openingTime,
    uint256 _closingTime,
    IERC20 _token
  )
    Crowdsale(_phaseTwoRate, _founderWallet, _token)
    TimedCrowdsale(_openingTime, _closingTime)
    public
  {
      advisorWallet = _advisorWallet;
      phaseOneRate = _phaseOneRate;
      phaseTwoRate = _phaseTwoRate;
  }

  // overridden from Crowdsale parent contract
  function _getTokenAmount(uint256 weiAmount)
    internal view returns (uint256)
  {
    if (phaseOneTokens > 0) {
      uint256 tokens = weiAmount.mul(phaseOneRate);
      if (tokens > phaseOneTokens) {
        uint256 weiRemaining = tokens.sub(phaseOneTokens).div(phaseOneRate);
        tokens = phaseOneTokens.add(super._getTokenAmount(weiRemaining));
      }
      return tokens;
    }

    return super._getTokenAmount(weiAmount);
  }

  // overridden from Crowdsale parent contract
  function _forwardFunds()
    internal
  {
    uint256 tokens;
    if (phaseOneTokens > 0) {
      tokens = msg.value.mul(phaseOneRate);
      if (tokens > phaseOneTokens) {
        uint256 weiRemaining = tokens.sub(phaseOneTokens).div(phaseOneRate);
        phaseOneTokens = 0;
        advisorWallet.transfer(msg.value.sub(weiRemaining));
        tokens = weiRemaining.mul(phaseTwoRate);
        phaseTwoTokens = phaseTwoTokens.sub(tokens);
        super.wallet().transfer(weiRemaining);
      } else {
        phaseOneTokens = phaseOneTokens.sub(tokens);
        advisorWallet.transfer(msg.value);
      }
      return;
    }

    tokens = msg.value.mul(phaseTwoRate);
    phaseTwoTokens = phaseTwoTokens.sub(tokens);
    super._forwardFunds();
  }

  // overridden from Crowdsale parent contract
  function _preValidatePurchase(
    address beneficiary,
    uint256 weiAmount
  )
    internal
    onlyIfWhitelisted(beneficiary)
  {
    require(tokensLeft() >= _getTokenAmount(weiAmount), "Insufficient number of tokens to complete purchase!");
    super._preValidatePurchase(beneficiary, weiAmount);
  }

  // overridden from Crowdsale parent contract
  function _finalization()
    internal
    onlyFounders
  {
    super.token().safeTransfer(super.wallet(), tokensLeft());
    super._finalization();
  }

  function tokensLeft()
    public
    view
    returns (uint256)
  {
    return phaseOneTokens + phaseTwoTokens;
  }

  function addReferral(address beneficiary, address referrer)
    external
    onlyAdmins
    onlyIfWhitelisted(referrer)
    onlyIfWhitelisted(beneficiary)
  {
    referrals[beneficiary] = referrer;
  }

  // overridden from Crowdsale parent contract
  function _processPurchase(
    address beneficiary,
    uint256 tokenAmount
  )
    internal
  {
    if (referrals[beneficiary] != 0) {
      uint256 tokensAvailable = tokensLeft().sub(tokenAmount);
      uint256 bonus = tokenAmount.mul(15).div(100);
      if (bonus >= tokensAvailable) {
        bonus = tokensAvailable;
        phaseTwoTokens = phaseTwoTokens.sub(tokensAvailable);
      } else {
        phaseTwoTokens = phaseTwoTokens.sub(bonus);
      }

      if (bonus > 0) {
        super._processPurchase(referrals[beneficiary], bonus);
      }
    }

    super._processPurchase(beneficiary, tokenAmount);
  }
}
function _getTokenAmount(uint256 weiAmount)
    internal view returns (uint256)
  {
    if (phaseOneTokens > 0) {
      uint256 tokens = weiAmount.mul(phaseOneRate);
      if (tokens > phaseOneTokens) {
        uint256 weiRemaining = tokens.sub(phaseOneTokens).div(phaseOneRate);
        tokens = phaseOneTokens.add(super._getTokenAmount(weiRemaining));
      }
      return tokens;
    }

    return super._getTokenAmount(weiAmount);
  }
// overridden from Crowdsale parent contract