contract LANCCrowdsale is Ownable {
  using SafeMath for uint256;

  address public fundDepositAddress = 0xE700569B98D4BF25E05c64C96560f77bCD44565E;

  uint256 public currentPeriod = 0;
  bool public isFinalized = false;
  // 0 = Not Started
  // 1 = PrePresale
  // 2 = Presale
  // 3 = Round 1
  // 4 = Round 2
  // 5 = Finished

  mapping (uint256 => uint256) public rateMap;
  mapping (address => uint256) powerDayAddressLimits;

  uint256 public powerDayRate; 
  uint256 public powerDayEthPerPerson = 10;
  uint256 public presaleStartTime;
  uint256 public powerDayEndTime;

  uint256 public constant capPresale =  57 * (10**5) * 10**18;
  uint256 public constant capRound1 =  (288 * (10**5) * 10**18);
  uint256 public constant capRound2 =  (484 * (10**5) * 10**18);

  uint256 public rate = 0; // LANC per ETH

  // The token being sold
  LANCToken public token;

  /**
   * event for token purchase logging
   * @param purchaser who paid for the tokens
   * @param beneficiary who got the tokens
   * @param value weis paid for purchase
   * @param amount amount of tokens purchased
   */
  event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);


  function LANCCrowdsale() public {
    
    // Initilize with rates.

    rateMap[1] = 2100; // PrePresale rate
    powerDayRate = 2000; // Powerday rate in presale.
    rateMap[2] = 1900;  // Presale rate
    rateMap[3] = 1650;  // Round 1 rate
    rateMap[4] = 1400;  // Round 2 rate
    rateMap[5] = 0; 
  }

  function setTokenContract(address _token) public onlyOwner {
        require(_token != address(0) && token == address(0));
        require(LANCToken(_token).owner() == address(this));
        require(LANCToken(_token).totalSupply() == 0);
        require(!LANCToken(_token).mintingFinished());

        token = LANCToken(_token);
   }

   function mint(address _to, uint256 _amount) public onlyOwner {
       require(token != address(0));
       require(!LANCToken(token).mintingFinished());
       require(LANCToken(token).owner() == address(this));

       token.mint(_to, _amount);
   }

   // Backup function in case of ETH price fluctuations

  function updateRates(uint256 rateIdx, uint256 newRate) public onlyOwner {
    require(rateIdx > 0 && rateIdx < 5);
    require(newRate > 0);

    rateMap[rateIdx] = newRate;

    if (rateIdx == currentPeriod) {
      rate = newRate;
    }
  }

  function updatePowerDayRate(uint256 newRate) public onlyOwner {
      powerDayRate = newRate;
  }

  function switchSaleState() public onlyOwner {
    require(token != address(0));

    if (currentPeriod > 4) {
      revert(); // Finished, last state is 4
    }

    currentPeriod = currentPeriod + 1;

    if (currentPeriod == 2) {
      presaleStartTime = now;
      powerDayEndTime = (presaleStartTime + 1 days);
    }

    rate = rateMap[currentPeriod];
  }

  function () external payable {
    buyTokens(msg.sender);
  }

  // low level token purchase function
  function buyTokens(address beneficiary) public payable {
    require(token != address(0));
    require(beneficiary != address(0));
    require(validPurchase());

    uint256 weiAmount = msg.value;
    uint256 currentRate = rate;
    uint256 tokens;
    bool inPowerDay = saleInPowerDay();

    // calculate token amount to be created   
    
    // Assign power day rate if in power day.
    if (inPowerDay == true) {
      tokens = weiAmount.mul(powerDayRate);      
    } else {
      tokens = weiAmount.mul(currentRate);      
    }
    
    // calculate supply after potential token mint
    uint256 checkedSupply = token.totalSupply().add(tokens);
    require(willFitInCap(checkedSupply));
    // check if new supply fits within current cap.

    if (inPowerDay == true) {
      uint256 newWeiAmountPerSender = powerDayAddressLimits[msg.sender].add(weiAmount);

      // Check if the person has reached their power day limit.
      if (newWeiAmountPerSender > powerDayPerPersonCapInWei()) {
        revert();
      } else {
        powerDayAddressLimits[msg.sender] = newWeiAmountPerSender;
      }
    }

    // Generate the tokens by using MintableToken's mint method.
    
    token.mint(beneficiary, tokens);

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

    forwardFunds();
  }

  function saleInPowerDay() internal view returns (bool) {
    bool inPresale = (currentPeriod == 2);
    bool inPowerDayPeriod = (now >= presaleStartTime && now <= powerDayEndTime);

    return inPresale && inPowerDayPeriod;
  }

  function powerDayPerPersonCapInWei() public view returns (uint) {
    require(token != address(0));
      // Calculate per-person cap in wei during power day.

    return powerDayEthPerPerson * (10**token.decimals()); 
  }
  

  function willFitInCap(uint256 checkedSupply) internal view returns (bool) {
    if (currentPeriod == 1 || currentPeriod == 2) {
      return (checkedSupply <= capPresale);
    } else if (currentPeriod == 3) {
      return (checkedSupply <= capRound1);
    } else if (currentPeriod == 4) {
      return (checkedSupply <= capRound2);
    }

    return false;
  }

  // @return true if the transaction can buy tokens
  function validPurchase() internal view returns (bool) {
    bool tokenAssigned = (token != address(0));
    bool inStartedState = (currentPeriod > 0 && currentPeriod < 5);
    bool nonZeroPurchase = msg.value != 0;

    return tokenAssigned && inStartedState && nonZeroPurchase && !isFinalized;
  }

  // Finalize the sale and calculate final token supply and distribute amounts.
  function finalizeSale() public onlyOwner {
    if (isFinalized == true) {
      revert();
    }

    uint newTokens = token.totalSupply();

    // Raise the remaining amounts
    token.mint(fundDepositAddress, newTokens);

    token.finishMinting();
    token.transferOwnership(owner);

    isFinalized = true;
  }

  // @return true if crowdsale event has ended
  function hasEnded() public view returns (bool) {
    return currentPeriod > 4;
  }

  // send ether to the fund collection wallet
  // override to create custom fund forwarding mechanisms
  function forwardFunds() internal {
    fundDepositAddress.transfer(msg.value);
  }

  function powerDayRemainingLimitOf(address _owner) public view returns (uint256 balance) {
    return powerDayAddressLimits[_owner];
  }

}
function updateRates(uint256 rateIdx, uint256 newRate) public onlyOwner {
  require(rateIdx > 0 && rateIdx < 5);
  require(newRate > 0);

  rateMap[rateIdx] = newRate;

  if (rateIdx == currentPeriod) {
    rate = newRate;
  }
}

// Backup function in case of ETH price fluctuations