contract PayFairTokenCrowdsale is Haltable, Killable, SafeMath {

  /// Total count of tokens distributed via ICO
  uint public constant TOTAL_ICO_TOKENS = 56e6;

  /// Total count of tokens distributed manually on the pre-ICO stage
  uint public constant PRE_ICO_MAX_TOKENS = 33e6;

  /// Miminal tokens funding goal in Wei, if this goal isn't reached during ICO, refund will begin
  uint public constant MIN_ICO_GOAL = 1 ether;

  /// The duration of ICO
  uint public constant ICO_DURATION = 30 days;

  /// The token we are selling
  PayFairToken public token;

  /// tokens will be transfered from this address
  address public multisigWallet;

  /// the UNIX timestamp start date of the crowdsale
  uint public startsAt;

  /// How many wei of funding we have raised
  uint public weiRaised = 0;

  /// How much wei we have returned back to the contract after a failed crowdfund.
  uint public loadedRefund = 0;

  /// How much wei we have given back to investors.
  uint public weiRefunded = 0;

  /// Total count of tokens distributed via pre-ICO
  uint public preIcoTokensDistributed = 0;

  /// Has this crowdsale been finalized
  bool public finalized;

  /// How much ETH each address has invested to this crowdsale
  mapping (address => uint256) public investedAmountOf;

  /// How much tokens this crowdsale has credited for each investor address
  mapping (address => uint256) public tokenAmountOf;

  /// Define preICO pricing schedule using milestones.
  struct Milestone {
      // UNIX timestamp when this milestone kicks in
      uint start;
      // UNIX timestamp when this milestone kicks out
      uint end;
      // How many % tokens will add
      uint bonus;
  }

  /// Define a structure for one investment event occurrence
  struct Investment {
      /// Who invested
      address source;

      /// Weight coefficient of investment (early investment bonus) in %
      uint weight;

      /// Amount invested
      uint weiValue;
  }

  Milestone[] public milestones;
  Investment[] public investments;

  /// State machine
  /// Preparing: All contract initialization calls and variables have not been set yet
  /// Prefunding: We have not passed start time yet
  /// Funding: Active crowdsale
  /// Success: Minimum funding goal reached
  /// Failure: Minimum funding goal not reached before ending time
  /// Finalized: The finalized has been called and succesfully executed\
  /// Refunding: Refunds are loaded on the contract for reclaim.
  enum State {Unknown, Preparing, PreFunding, Funding, Success, Failure, Finalized, Refunding}

  /// A new investment was made
  event Invested(address investor, uint weiAmount);
  /// Refund was processed for a contributor
  event Refund(address investor, uint weiAmount);

  /// @dev Modified allowing execution only if the crowdsale is currently running
  modifier inState(State state) {
    require(getState() == state);
    _;
  }

  /// @dev Constructor
  /// @param _token Pay Fair token address
  /// @param _multisigWallet team wallet
  /// @param _start token ICO start date
  function PayFairTokenCrowdsale(address _token, address _multisigWallet, uint _start) {
    require(_multisigWallet != 0);
    require(_start != 0);

    token = PayFairToken(_token);

    multisigWallet = _multisigWallet;
    startsAt = _start;

    milestones.push(Milestone(startsAt, startsAt + 1 days, 20));
    milestones.push(Milestone(startsAt + 1 days, startsAt + 5 days, 15));
    milestones.push(Milestone(startsAt + 5 days, startsAt + 10 days, 10));
    milestones.push(Milestone(startsAt + 10 days, startsAt + 20 days, 5));
  }

  ///  Don't expect to just send in money and get tokens.
  function() payable {
    buy();
  }

  /// @dev Get the current milestone or bail out if we are not in the milestone periods.
  /// @return Milestone current bonus milestone
  function getCurrentMilestone() private constant returns (Milestone) {
    for (uint i = 0; i < milestones.length; i++) {
      if (milestones[i].start <= now && milestones[i].end > now) {
        return milestones[i];
      }
   }
 }

   /// @dev Make an investment. Crowdsale must be running for one to invest.
   /// @param receiver The Ethereum address who receives the tokens
  function investInternal(address receiver) stopInEmergency private {
    var state = getState();
    require(state == State.Funding);
    require(msg.value > 0);

    // Add investment record
    var weiAmount = msg.value;
    investments.push(Investment(receiver, weiAmount, getCurrentMilestone().bonus + 100));
    investedAmountOf[receiver] = safeAdd(investedAmountOf[receiver], weiAmount);

    // Update totals
    weiRaised = safeAdd(weiRaised, weiAmount);
    // Transfer funds to the team wallet
    multisigWallet.transfer(weiAmount);
    // Tell us invest was success
    Invested(receiver, weiAmount);
  }

  /// @dev Allow anonymous contributions to this crowdsale.
  /// @param receiver The Ethereum address who receives the tokens
  function invest(address receiver) public payable {
    investInternal(receiver);
  }

  function sendPreIcoTokens(address receiver, uint amount) public inState(State.PreFunding) onlyOwner {
    require(receiver != 0);
    require(amount > 0);
    require(safeAdd(preIcoTokensDistributed, amount) <= token.convertToDecimal(PRE_ICO_MAX_TOKENS));

    preIcoTokensDistributed = safeAdd(preIcoTokensDistributed, amount);
    assignTokens(receiver, amount);
  }

  /// @dev The basic entry point to participate the crowdsale process.
  function buy() public payable {
    invest(msg.sender);
  }

  /// @dev Finalize a succcesful crowdsale.
  function finalize() public inState(State.Success) onlyOwner stopInEmergency {
    require(!finalized);

    finalized = true;
    finalizeCrowdsale();
  }

  /// @dev Finalize a succcesful crowdsale.
  function finalizeCrowdsale() internal {
    // Calculate divisor of the total token count
    uint divisor;
    for (uint i = 0; i < investments.length; i++)
       divisor = safeAdd(divisor, safeMul(investments[i].weiValue, investments[i].weight));

    uint localMultiplier = 10 ** 12;
    // Get unit price
    uint unitPrice = safeDiv(safeMul(token.convertToDecimal(TOTAL_ICO_TOKENS), localMultiplier), divisor);

    // Distribute tokens among investors
    for (i = 0; i < investments.length; i++) {
        var tokenAmount = safeDiv(safeMul(unitPrice, safeMul(investments[i].weiValue, investments[i].weight)), localMultiplier);
        tokenAmountOf[investments[i].source] += tokenAmount;
        assignTokens(investments[i].source, tokenAmount);
    }

    token.releaseTokenTransfer();
  }

  /// @dev Allow load refunds back on the contract for the refunding.
  function loadRefund() public payable inState(State.Failure) {
    require(msg.value > 0);
    loadedRefund = safeAdd(loadedRefund, msg.value);
  }

  /// @dev Investors can claim refund.
  function refund() public inState(State.Refunding) {
    uint256 weiValue = investedAmountOf[msg.sender];
    if (weiValue == 0)
      return;
    investedAmountOf[msg.sender] = 0;
    weiRefunded = safeAdd(weiRefunded, weiValue);
    Refund(msg.sender, weiValue);
    msg.sender.transfer(weiValue);
  }

  /// @dev Minimum goal was reached
  /// @return true if the crowdsale has raised enough money to not initiate the refunding
  function isMinimumGoalReached() public constant returns (bool reached) {
    return weiRaised >= MIN_ICO_GOAL;
  }

  /// @dev Check if the ICO goal was reached.
  /// @return true if the crowdsale has raised enough money to be a success
  function isCrowdsaleFull() public constant returns (bool) {
    return isMinimumGoalReached() && now > startsAt + ICO_DURATION;
  }

  /// @dev Crowdfund state machine management.
  /// @return State current state
  function getState() public constant returns (State) {
    if (finalized)
      return State.Finalized;
    if (address(token) == 0 || address(multisigWallet) == 0)
      return State.Preparing;
    if (preIcoTokensDistributed < token.convertToDecimal(PRE_ICO_MAX_TOKENS))
        return State.PreFunding;
    if (now >= startsAt && now < startsAt + ICO_DURATION && !isCrowdsaleFull())
      return State.Funding;
    if (isMinimumGoalReached())
      return State.Success;
    if (!isMinimumGoalReached() && weiRaised > 0 && loadedRefund >= weiRaised)
      return State.Refunding;
    return State.Failure;
  }

  
   /// @dev Dynamically create tokens and assign them to the investor.
   /// @param receiver investor address
   /// @param tokenAmount The amount of tokens we try to give to the investor in the current transaction
   function assignTokens(address receiver, uint tokenAmount) private {
     token.mint(receiver, tokenAmount);
   }
}
/// @title PayFairTokenCrowdsale contract - contract for token sales.
function assignTokens(address receiver, uint tokenAmount) private {
     token.mint(receiver, tokenAmount);
   }
/// @dev Dynamically create tokens and assign them to the investor.
/// @param receiver investor address
/// @param tokenAmount The amount of tokens we try to give to the investor in the current transaction