contract EGLToken is VestedToken {
  //FIELDS
  string public name = "eGold";
  string public symbol = "EGL";
  uint public decimals = 4;
  
  //CONSTANTS
  // Time limits
  uint public constant STAGE_ONE_TIME_END = 24 hours; // first day bonus
  uint public constant STAGE_TWO_TIME_END = 1 weeks; // first week bonus
  uint public constant STAGE_THREE_TIME_END = 28 days; // around one month
  
  // Multiplier for the decimals
  uint private constant MULTIPLIER = 10000;

  //Prices of EGL
  uint public constant PRICE_STANDARD    =  888 *MULTIPLIER; // EGL received per one ETH; MAX_SUPPLY / (valuation / ethPrice)
  uint public constant PRICE_PREBUY      = 1066 *MULTIPLIER; // 1ETH = 20% more EGL 
  uint public constant PRICE_STAGE_ONE   = 1021 *MULTIPLIER; // 1ETH = 15% more EGL
  uint public constant PRICE_STAGE_TWO   =  976 *MULTIPLIER; // 1ETH = 10% more EGL
  uint public constant PRICE_STAGE_THREE =  888 *MULTIPLIER;

  // EGL Token Limits
  uint public constant ALLOC_TEAM =          4444444 *MULTIPLIER; // team + advisors + everything else
  uint public constant ALLOC_CROWDSALE =    (4444444-266666) *MULTIPLIER;
  uint public constant ALLOC_SC = 	          266666 *MULTIPLIER;
  
  uint public constant ALLOC_MAX_PRE =        888888 *MULTIPLIER;
  
  // More ERC20
  uint public totalSupply =                  8888888 *MULTIPLIER; 
  
  // ASSIGNED IN INITIALIZATION
  // Start and end times
  uint public publicStartTime; // Time in seconds public crowd fund starts.
  uint public publicEndTime; // Time in seconds crowdsale ends
  uint public hardcapInWei;

  //Special Addresses
  address public multisigAddress; // Address to which all ether flows.
  address public ownerAddress; // Address of the contract owner. Can halt the crowdsale.

  // Running totals
  uint public weiRaised; // Total Ether raised.
  uint public EGLSold; // Total EGL sold. Not to exceed ALLOC_CROWDSALE
  uint public prebuyPortionTotal; // Total of Tokens purchased by pre-buy. Not to exceed ALLOC_MAX_PRE.
  
  // booleans
  bool public halted; // halts the crowd sale if true.

  // Is completed
  function isCrowdfundCompleted()
    internal
    returns (bool) 
  {
    if (
      now > publicEndTime
      || EGLSold >= ALLOC_CROWDSALE
      || weiRaised >= hardcapInWei
    ) return true;

    return false;
  }

  // MODIFIERS

  //May only be called by the owner address
  modifier only_owner() {
    require(msg.sender == ownerAddress);
    _;
  }

  //May only be called if the crowdfund has not been halted
  modifier is_not_halted() {
    require(!halted);
    _;
  }

  // EVENTS
  event Buy(address indexed _recipient, uint _amount);

  // Initialization contract assigns address of crowdfund contract and end time.
  function EGLToken(
    address _multisig,
    uint _publicStartTime,
    uint _hardcapInWei
  ) {
    ownerAddress = msg.sender;
    publicStartTime = _publicStartTime;
    publicEndTime = _publicStartTime + 28 days;
    multisigAddress = _multisig;

    hardcapInWei = _hardcapInWei;
    
    balances[0x8c6a58B551F38d4D51C0db7bb8b7ad29f7488702] += ALLOC_SC;

    // will be transferred to the team address when it's vested
    balances[ownerAddress] += ALLOC_TEAM;

    balances[ownerAddress] += ALLOC_CROWDSALE;
  }

  // Transfer amount of tokens from sender account to recipient.
  // Only callable after the crowd fund is completed
  function transfer(address _to, uint _value)
    returns (bool)
  {
    if (_to == msg.sender) return; // no-op, allow even during crowdsale, in order to work around using grantVestedTokens() while in crowdsale
    require(isCrowdfundCompleted());
    return super.transfer(_to, _value);
  }

  // Transfer amount of tokens from a specified address to a recipient.
  function transferFrom(address _from, address _to, uint _value)
    returns (bool)
  {
    require(isCrowdfundCompleted());
    return super.transferFrom(_from, _to, _value);
  }

  // function returns the current EGL price.
  function getPriceRate()
      constant
      returns (uint o_rate)
  {
      uint delta = SafeMath.sub(now, publicStartTime);

      if (delta > STAGE_TWO_TIME_END) return PRICE_STAGE_THREE;
      if (delta > STAGE_ONE_TIME_END) return PRICE_STAGE_TWO;

      return (PRICE_STAGE_ONE);
  }

  // calculates wmount of EGL we get, given the wei and the rates we've defined per 1 eth
  function calcAmount(uint _wei, uint _rate) 
    constant
    returns (uint) 
  {
    return SafeMath.div(SafeMath.mul(_wei, _rate), 1 ether);
  } 
  
  // Given the rate of a purchase and the remaining tokens in this tranche, it
  // will throw if the sale would take it past the limit of the tranche.
  // Returns `amount` in scope as the number of EGL tokens that it will purchase.
  function processPurchase(uint _rate, uint _remaining)
    internal
    returns (uint o_amount)
  {
    o_amount = calcAmount(msg.value, _rate);

    require(o_amount <= _remaining);
    require(multisigAddress.send(msg.value));

    balances[ownerAddress] = balances[ownerAddress].sub(o_amount);
    balances[msg.sender] = balances[msg.sender].add(o_amount);

    EGLSold += o_amount;
    weiRaised += msg.value;
  }

  // Default function called by sending Ether to this address with no arguments.
  // Results in creation of new EGL Tokens if transaction would not exceed hard limit of EGL Token.
  function()
    payable
    is_not_halted
  {
    require(!isCrowdfundCompleted());

    uint amount;

    if (now < publicStartTime) {
      // pre-sale
      amount = processPurchase(PRICE_PREBUY, SafeMath.sub(ALLOC_MAX_PRE, prebuyPortionTotal));
      prebuyPortionTotal += amount;
    } else {
      amount = processPurchase(getPriceRate(), SafeMath.sub(ALLOC_CROWDSALE, EGLSold));
    }
    
    Buy(msg.sender, amount);
  }

  // May be used by owner of contract to halt crowdsale and no longer except ether.
  function toggleHalt(bool _halted)
    only_owner
  {
    halted = _halted;
  }

  // failsafe drain
  function drain()
    only_owner
  {
    require(ownerAddress.send(this.balance));
  }

  // public constant 
  function getStatus() 
    constant
    public
    returns (string)
  {
    if (EGLSold >= ALLOC_CROWDSALE) return "tokensSoldOut";
    if (weiRaised >= hardcapInWei) return "hardcapReached";
    
    if (now < publicStartTime) {
      // presale
      if (prebuyPortionTotal >= ALLOC_MAX_PRE) return "presaleSoldOut";
      return "presale";
    } else if (now < publicEndTime) {
      // public sale
      return "public";
    } else {
      return "saleOver";
    }
  }
}
function transfer(address _to, uint _value)
    returns (bool)
  {
    if (_to == msg.sender) return; // no-op, allow even during crowdsale, in order to work around using grantVestedTokens() while in crowdsale
    require(isCrowdfundCompleted());
    return super.transfer(_to, _value);
  }
// Transfer amount of tokens from sender account to recipient.
// Only callable after the crowd fund is completed