contract SilcCrowdsale is CappedCrowdsale, MyRefundableCrowdsale {

  // Sale Stage
  // ============
  enum CrowdsaleStage { phase1, phase2, phase3 }
  CrowdsaleStage public stage = CrowdsaleStage.phase1; // By default it's Pre Sale
  // =============

  // Token Distribution
  // =============================
  uint256 public maxTokens = 20000000000000000000000000000;          // 20,000,000,000
  uint256 public tokensForEcosystem = 3500000000000000000000000000;  //  3,500,000,000
  uint256 public tokensForTeam = 2500000000000000000000000000;       //  2,500,000,000
  uint256 public tokensForAdvisory = 1000000000000000000000000000;   //  1,000,000,000

  uint256 public totalTokensForSale = 3000000000000000000000000000;  // 3,000,000,000 SILC = 30,000 ETH
  // ==============================

  // Rate
  uint256 public rateForPhase1 = 110000;
  uint256 public rateForPhase2 = 105000;
  uint256 public rateForPhase3 = 100000;

  // can be negative, because of refund.
  // ==================
  int256 public totalWeiRaisedDuringPhase1;
  int256 public totalWeiRaisedDuringPhase2;
  int256 public totalWeiRaisedDuringPhase3;
  // ===================

  uint256 public totalTokenSupply;

  // Events
  event EthTransferred(string text);
  event EthRefunded(string text);


  // Constructor
  // ============
  function SilcCrowdsale(uint256 _startTime, uint256 _endTime, uint256 _rate, address _wallet, uint256 _goal, uint256 _cap) 
    CappedCrowdsale(_cap) 
    FinalizableCrowdsale() 
    MyRefundableCrowdsale(_goal) 
    Crowdsale(_startTime, _endTime, _rate, _wallet) public {
      require(_goal <= _cap);
  }
  // =============

  // Token Deployment
  // =================
  function createTokenContract() internal returns (MintableToken) {
    return new SilcToken(); // Deploys the ERC20 token. Automatically called when crowdsale contract is deployed
  }
  // ==================

  // Crowdsale Stage Management
  // =========================================================

  // Change Crowdsale Stage. Available Options: phase1, phase2
  function setCrowdsaleStage(uint value) public onlyOwner {

      CrowdsaleStage _stage;

      if (uint(CrowdsaleStage.phase1) == value) {
        _stage = CrowdsaleStage.phase1;
      } else if (uint(CrowdsaleStage.phase2) == value) {
        _stage = CrowdsaleStage.phase2;
      } else if (uint(CrowdsaleStage.phase3) == value) {
        _stage = CrowdsaleStage.phase3;
      }


      stage = _stage;

      if (stage == CrowdsaleStage.phase1) {
        setCurrentRate(rateForPhase1);
      } else if (stage == CrowdsaleStage.phase2) {
        setCurrentRate(rateForPhase2);
      } else if (stage == CrowdsaleStage.phase3) {
        setCurrentRate(rateForPhase3);
      }
  }

  // Change the current rate
  function setCurrentRate(uint256 _rate) private {
      rate = _rate;
  }

  function calculateWeiForStage(int256 value) {
      if (stage == CrowdsaleStage.phase1) {
        totalWeiRaisedDuringPhase1 = totalWeiRaisedDuringPhase1 + value;
      } else if (stage == CrowdsaleStage.phase2) {
        totalWeiRaisedDuringPhase2 = totalWeiRaisedDuringPhase2 + value;
      } else if (stage == CrowdsaleStage.phase3) {
        totalWeiRaisedDuringPhase3 = totalWeiRaisedDuringPhase3 + value;
      }
  }

  // ================ Stage Management Over =====================

  // Token Purchase
  // =========================
  function () external payable {
      //uint256 tokensThatWillBeMintedAfterPurchase = msg.value.mul(rate);
      //if ((stage == CrowdsaleStage.phase1) && (token.totalSupply() + tokensThatWillBeMintedAfterPurchase > totalTokensForSaleDuringpreSale)) {
      //  msg.sender.transfer(msg.value); // Refund them
      //  EthRefunded("phase1 Limit Hit");
      //  return;
      //}
      require(msg.value >= 0.1 ether); // 0.1 ETH
      buyTokens(msg.sender);
      totalTokenSupply = token.totalSupply();
      calculateWeiForStage(int256(msg.value));
  }

  mapping (address => uint256) tokenIssued;

  function buyTokens(address beneficiary) public payable {
    require(beneficiary != address(0));
    require(validPurchase());

    uint256 weiAmount = msg.value;

    // calculate token amount to be created
    uint256 tokens = weiAmount.mul(rate);

    // update state
    weiRaised = weiRaised.add(weiAmount);

    token.mint(beneficiary, tokens);
    tokenIssued[beneficiary] = tokenIssued[beneficiary].add(tokens);

    TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);

    forwardFunds();
  }

  function getTokenIssued(address contributor) public view returns (uint256 token) {
    return tokenIssued[contributor];
  }

  function forwardFunds() internal {
      if (stage == CrowdsaleStage.phase1) {
          //wallet.transfer(msg.value);
          //EthTransferred("forwarding funds to wallet");
          EthTransferred("forwarding funds to refundable vault");
          super.forwardFunds();
      } else if (stage == CrowdsaleStage.phase2) {
          EthTransferred("forwarding funds to refundable vault");
          super.forwardFunds();
      } else if (stage == CrowdsaleStage.phase3) {
          EthTransferred("forwarding funds to refundable vault");
          super.forwardFunds();
      }
  }
  // ===========================

  // Finish: Mint Extra Tokens as needed before finalizing the Crowdsale.
  // ====================================================================

  function finish(address _teamFund, address _ecosystemFund, address _advisoryFund) public onlyOwner {

    require(!isFinalized);
    uint256 alreadyMinted = token.totalSupply();
    require(alreadyMinted < maxTokens);

    uint256 unsoldTokens = totalTokensForSale - alreadyMinted;
    if (unsoldTokens > 0) {
      tokensForEcosystem = tokensForEcosystem + unsoldTokens;
    }

    token.mint(_teamFund,tokensForTeam);
    token.mint(_ecosystemFund,tokensForEcosystem);
    token.mint(_advisoryFund,tokensForAdvisory);
    finalize();
  }
  // ===============================

  // token functions
  function mintSilcToken(address _to, uint256 _amount) public onlyOwner {
    token.mint(_to, _amount);
  }

  function transferTokenOwnership(address newOwner) public onlyOwner {
    token.transferOwnership(newOwner);
  }

  function transferVaultOwnership(address newOwner) public onlyOwner {
    vault.transferOwnership(newOwner);
  }
  // ===============================

  event LogEvent(bytes32 message, uint256 value);
  event RefundRequestCompleted(address contributor, uint256 weiRefunded, uint256 burnedToken);
  function refundRequest() public {
    address contributor = msg.sender;
    SilcToken silcToken = SilcToken(address(token));
    uint256 tokenValance = token.balanceOf(contributor);
    require(tokenValance != 0);
    require(tokenValance >= tokenIssued[contributor]);
    //LogEvent("StartBurn", tokenValance);
    silcToken.burn(contributor, tokenIssued[contributor]);  // burn issued tokens
    tokenIssued[contributor] = 0;
    //LogEvent("StartRefund", token.balanceOf(contributor));
    uint256 weiRefunded = vault.refundWhenNotClosed(contributor);
    weiRaised = weiRaised.sub(weiRefunded);

    calculateWeiForStage(int256(weiRefunded) * -1);

    RefundRequestCompleted(contributor, weiRefunded, tokenValance);
  }

  // for testing `finish()` FUNCTION
  function hasEnded() public view returns (bool) {
    return true;
  }
}
function finish(address _teamFund, address _ecosystemFund, address _advisoryFund) public onlyOwner {

  require(!isFinalized);
  uint256 alreadyMinted = token.totalSupply();
  require(alreadyMinted < maxTokens);

  uint256 unsoldTokens = totalTokensForSale - alreadyMinted;
  if (unsoldTokens > 0) {
    tokensForEcosystem = tokensForEcosystem + unsoldTokens;
  }

  token.mint(_teamFund,tokensForTeam);
  token.mint(_ecosystemFund,tokensForEcosystem);
  token.mint(_advisoryFund,tokensForAdvisory);
  finalize();
}

// ===========================
// Finish: Mint Extra Tokens as needed before finalizing the Crowdsale.
// ====================================================================