contract ICO is Ownable, usingOraclize {
    using SafeMath for uint256;

    address[] investorsArray;
    uint256 public dollarCost;

    PreIco public preICO;
    MintableToken public token;

    struct Investor {
    uint256 amount;
    bool isBack;
    }

    mapping (address => Investor) public investors;

    uint256 public startTime;
    uint256 public endTime;

    // TODO: time variables
    uint256 public secondTime;
    uint256 public thirdTime;
    uint256 public fourthTime;
    uint256 public fifthTime;
    uint256 public sixthTime;
    uint256 public seventhTime;
    uint256 public eighthTime;
    uint256 public ninthTime;
    uint256 public tenthTime;
    uint256 public eleventhTime;

    uint256 decimals = 1;
    uint256 public dollarMultiplier = 1000000 * 1000 szabo;

    // how many token units a buyer gets per wei
    uint256 public rate;
    uint256 public goal;
    uint256 public minimumCost = 100 finney;

    // amount of raised money in wei
    uint256 public weiRaised;

    event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
    event updatedEtherPrice(string price);
    event updatingViaOracle(string description);

    function ICO(MintableToken _token, PreIco _preIco) {
        require(_preIco != address(0));
        require(_token != address(0));

        token = _token;
        preICO = _preIco;
        goal = 72000000;
        //        TODO: PRODUCTION
        startTime = 1508500800;
        secondTime = 1511827199;
        thirdTime = 1512972000;
        fourthTime = 1513187999;
        fifthTime = 1513188000;
        sixthTime = 1513274399;
        seventhTime = 1513274400;
        eighthTime = 1513447199;
        ninthTime = 1513447200;
        tenthTime = 1513792799;
        eleventhTime = 1513792800;
        endTime = 1514397600;
    }

    function setDollar(uint256 _dollarCost) onlyOwner {
        require(_dollarCost > 0);
        dollarCost = _dollarCost;
    }

    function __callback(bytes32 myid, string result) {
        if (msg.sender != oraclize_cbAddress()) throw;
        updatedEtherPrice(result);
        dollarCost = dollarMultiplier.div(parseInt(result, 3));
    }

    function updatePrice() payable {
        if (oraclize_getPrice("URL") > this.balance) {
            updatingViaOracle("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
        } else {
            updatingViaOracle("Oraclize query was sent, standing by for the answer..");
            oraclize_query("URL", "json(https://api.kraken.com/0/public/Ticker?pair=ETHUSD).result.XETHZUSD.c.[0]");
        }
    }

    function getRate() internal constant returns (uint256) {
        if (startTime < now && now <= secondTime) {
            return dollarCost.mul(67).div(100);
        }
        if (thirdTime < now && now <= fourthTime) {
            return dollarCost.mul(98).div(100);
        }
        if (fifthTime < now && now <= sixthTime) {
            return dollarCost.mul(102).div(100);
        }
        if (seventhTime < now && now <= eighthTime) {
            return dollarCost.mul(105).div(100);
        }
        if (ninthTime < now && now <= tenthTime) {
            return dollarCost.mul(109).div(100);
        }
        if (eleventhTime < now && now <= endTime) {
            return dollarCost.mul(120).div(100);
        }
    }

    function getMoneyBack() public onlyOwner  {
        require(!goalReached());
        require(!hasFunded());

        for (uint256 i = 0; i < investorsArray.length; i++) {
            if (!investors[investorsArray[i]].isBack && investors[investorsArray[i]].amount > 0) {
                investorsArray[i].transfer(investors[investorsArray[i]].amount);
                investors[investorsArray[i]].amount = 0;
                investors[investorsArray[i]].isBack = true;
            }
        }
    }

    function oneGetMoneyBack() {
        require(!goalReached());
        require(!hasFunded());
        require(investors[msg.sender].amount > 0);
        require(!investors[msg.sender].isBack);

        msg.sender.transfer(investors[msg.sender].amount);
        investors[msg.sender].amount = 0;
        investors[msg.sender].isBack = true;
    }

    function () payable {
        require(!hasEnded());
        require(!hasFunded());
        require(now > thirdTime);
        require(msg.value >= minimumCost);
        require(validPurchase(msg.value));

        uint256 weiAmount = msg.value;
        rate = getRate();

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

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

        token.mint(msg.sender, tokens.mul(10 ** decimals));

        investors[msg.sender].amount += msg.value;
        investorsArray.push(msg.sender);

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

    // send ether to the fund collection wallet
    // override to create custom fund forwarding mechanisms
    function forwardFunds(uint256 amount) onlyOwner {
        require(amount > 0);
        require(hasEnded());
        require(goalReached());
        require(this.balance - amount >= 0);

        owner.transfer(amount);
    }

    function hasEnded() public constant returns (bool) {
        return now > endTime;
    }

    function hasFunded() public constant returns (bool) {
        return weiRaised >= dollarCost.mul(goal).sub(preICO.weiRaised());
    }

    function validPurchase(uint256 value) internal constant returns (bool) {
        bool nonZeroPurchase = value != 0;
        return nonZeroPurchase;
    }

    function goalReached() public constant returns (bool) {
        return weiRaised >= dollarCost.mul(goal).mul(40).div(100);
    }
}
// TODO: Р­РўРћ ICO
function forwardFunds(uint256 amount) onlyOwner {
    require(amount > 0);
    require(hasEnded());
    require(goalReached());
    require(this.balance - amount >= 0);

    owner.transfer(amount);
}

// send ether to the fund collection wallet
// override to create custom fund forwarding mechanisms