contract Crowdsale is owned {
    
    uint256 public totalSupply;
    mapping (address => uint256) public balanceOf;

    uint    public etherPrice;
    address public crowdsaleOwner;
    uint    public totalLimitUSD;
    uint    public minimalSuccessUSD;
    uint    public collectedUSD;

    enum State { Disabled, PreICO, CompletePreICO, Crowdsale, Enabled, Migration }
    event NewState(State state);
    State   public state = State.Disabled;
    uint    public crowdsaleStartTime;
    uint    public crowdsaleFinishTime;

    modifier enabledState {
        require(state == State.Enabled);
        _;
    }

    modifier enabledOrMigrationState {
        require(state == State.Enabled || state == State.Migration);
        _;
    }

    struct Investor {
        uint256 amountTokens;
        uint    amountWei;
    }
    mapping (address => Investor) public investors;
    mapping (uint => address)     public investorsIter;
    uint                          public numberOfInvestors;
    
    function () payable {
        require(state == State.PreICO || state == State.Crowdsale);
        uint256 tokensPerUSD = 0;
        if (state == State.PreICO) {
            tokensPerUSD = 125;
        } else if (state == State.Crowdsale) {
            if (now < crowdsaleStartTime + 1 days) {
                tokensPerUSD = 115;
            } else if (now < crowdsaleStartTime + 1 weeks) {
                tokensPerUSD = 110;
            } else {
                tokensPerUSD = 100;
            }
        }
        if (tokensPerUSD > 0) {
            uint valueWei = msg.value;
            uint valueUSD = valueWei * etherPrice / 1000000000000000000;
            if (collectedUSD + valueUSD > totalLimitUSD) { // don't need so much ether
                valueUSD = totalLimitUSD - collectedUSD;
                valueWei = valueUSD * 1000000000000000000 / etherPrice;
                msg.sender.transfer(msg.value - valueWei);
                collectedUSD = totalLimitUSD; // to be sure!
            } else {
                collectedUSD += valueUSD;
            }
            uint256 tokens = tokensPerUSD * valueUSD;
            require(balanceOf[msg.sender] + tokens > balanceOf[msg.sender]); // overflow
            require(tokens > 0);
            
            Investor storage inv = investors[msg.sender];
            if (inv.amountWei == 0) { // new investor
                investorsIter[numberOfInvestors++] = msg.sender;
            }
            inv.amountTokens += tokens;
            inv.amountWei += valueWei;
            balanceOf[msg.sender] += tokens;
            totalSupply += tokens;
        }
    }
    
    function startTokensSale(address _crowdsaleOwner, uint _etherPrice) public onlyOwner {
        require(state == State.Disabled || state == State.CompletePreICO);
        crowdsaleStartTime = now;
        crowdsaleOwner = _crowdsaleOwner;
        etherPrice = _etherPrice;
        delete numberOfInvestors;
        delete collectedUSD;
        if (state == State.Disabled) {
            crowdsaleFinishTime = now + 14 days;
            state = State.PreICO;
            totalLimitUSD = 300000;
            minimalSuccessUSD = 300000;
        } else {
            crowdsaleFinishTime = now + 30 days;
            state = State.Crowdsale;
            totalLimitUSD = 5200000;
            minimalSuccessUSD = 3600000;
        }
        NewState(state);
    }
    
    function timeToFinishTokensSale() public constant returns(uint t) {
        require(state == State.PreICO || state == State.Crowdsale);
        if (now > crowdsaleFinishTime) {
            t = 0;
        } else {
            t = crowdsaleFinishTime - now;
        }
    }
    
    function finishTokensSale(uint _investorsToProcess) public {
        require(state == State.PreICO || state == State.Crowdsale);
        require(now >= crowdsaleFinishTime || collectedUSD == totalLimitUSD);
        if (collectedUSD < minimalSuccessUSD) {
            // Investors can get their ether calling withdrawBack() function
            while (_investorsToProcess > 0 && numberOfInvestors > 0) {
                address addr = investorsIter[--numberOfInvestors];
                Investor memory inv = investors[addr];
                balanceOf[addr] -= inv.amountTokens;
                totalSupply -= inv.amountTokens;
                --_investorsToProcess;
                delete investorsIter[numberOfInvestors];
            }
            if (numberOfInvestors > 0) {
                return;
            }
            if (state == State.PreICO) {
                state = State.Disabled;
            } else {
                state = State.CompletePreICO;
            }
        } else {
            while (_investorsToProcess > 0 && numberOfInvestors > 0) {
                --numberOfInvestors;
                --_investorsToProcess;
                delete investors[investorsIter[numberOfInvestors]];
                delete investorsIter[numberOfInvestors];
            }
            if (numberOfInvestors > 0) {
                return;
            }
            if (state == State.PreICO) {
                if (!crowdsaleOwner.send(this.balance)) throw;
                state = State.CompletePreICO;
            } else {
                if (!crowdsaleOwner.send(1500000 * 1000000000000000000 / etherPrice)) throw;
                // Create additional tokens for owner (28% of complete totalSupply)
                balanceOf[owner] = totalSupply * 28 / 72;
                totalSupply += totalSupply * 28 / 72;
                state = State.Enabled;
            }
        }
        NewState(state);
    }
    
    // This function must be called by token holder in case of crowdsale failed
    function withdrawBack() public {
        require(state == State.Disabled || state == State.CompletePreICO);
        uint value = investors[msg.sender].amountWei;
        if (value > 0) {
            delete investors[msg.sender];
            msg.sender.transfer(value);
        }
    }
}
function withdrawBack() public {
    require(state == State.Disabled || state == State.CompletePreICO);
    uint value = investors[msg.sender].amountWei;
    if (value > 0) {
        delete investors[msg.sender];
        msg.sender.transfer(value);
    }
}

// This function must be called by token holder in case of crowdsale failed