contract Crowdsale is Rays {
    // ICO rounds
    enum IcoStages {preSale, preIco, ico} 
    IcoStages Stage;
    bool private crowdsaleFinished;
    
    uint private startPreSaleDate;
    uint private endPreSaleDate;
    uint public preSaleGoal;
    uint private preSaleRaised;
    
    uint private startPreIcoDate;
    uint private endPreIcoDate;
    uint public preIcoGoal;
    uint private preIcoRaised;
    
    uint private startIcoDate;
    uint private endIcoDate;
    uint public icoGoal;
    uint private icoRaised;
    
    uint private softCup; // 2 000 000 $ (300$ = 1 ether)
    uint private totalCup;
    uint private price;
    uint private total;
    uint private reserved;
    uint private hardCup;// 20 000 000 $ (300$ = 1 ether)
    
    struct Benefeciary{ // collect all participants of ICO
        address wallet;
        uint amount;
    }
    Benefeciary[] private benefeciary;
    uint private ethersRefund;
    
    constructor() public {
        startPreSaleDate = 1534723200; // insert here your pre sale start date
        endPreSaleDate = 1536969600; // insert here your pre sale end date
        preSaleGoal = 60000000; // pre-sale goal 
        preSaleRaised = 0; // raised on pre-sale stage
        startPreIcoDate = 1534723200; // insert here your pre ico start date
        endPreIcoDate = 1538265600; // insert here your pre ico end date
        preIcoGoal = 60000000; // pre ico goal 
        preIcoRaised = 0; // raised on pre ico
        startIcoDate = 1534723200; // insert here your ico start date
        endIcoDate = 1546214400; // insert here your ico end date
        icoGoal = 80000000; // ico goal 
        icoRaised = 0; // raised on ico stage
        softCup = 6670 * 10**18; 
        hardCup = 66670 * 10**18;
        totalCup = 0;
        price = 1160;
        total = preSaleGoal + preIcoGoal + icoGoal;
        reserved = (70000000 + 200000000 + 5000000 + 25000000) * 10**18;
        crowdsaleFinished = false;
    }
  
    function getCrowdsaleInfo() private returns(uint stage, 
                                               uint tokenAvailable, 
                                               uint bonus){
        // Token calculating
        if(now <= endPreSaleDate && preSaleRaised < preSaleGoal){
            Stage = IcoStages.preSale;
            tokenAvailable = preSaleGoal - preSaleRaised;
            total -= preSaleRaised;
            bonus = 0; // insert your bonus value on pre sale phase
        } else if(startPreIcoDate <= now && now <= endPreIcoDate && preIcoRaised < preIcoGoal){
            Stage = IcoStages.preIco;
            tokenAvailable = preIcoGoal - preIcoRaised;
            total -= preIcoRaised;
            bonus = 50; // + 50% seems like bonus
        } else if(startIcoDate <= now && now <= endIcoDate && icoRaised < total){
            tokenAvailable = total - icoRaised;
            Stage = IcoStages.ico;
            bonus = 0;
        } else {
            // if ICO has not been started
            revert();
        }
        return (uint(Stage), tokenAvailable, bonus);
    }
    //per 0.1 ether will recieved 116 tokens
    function evaluateTokens(uint _value, address _sender) private returns(uint tokens){
        ethersRefund = 0;
        uint bonus;
        uint tokenAvailable;
        uint stage;
        (stage,tokenAvailable,bonus) = getCrowdsaleInfo();
        tokens = _value * price / 10**18; 
        if(bonus != 0){
            tokens = tokens + (tokens * bonus / 100); // calculate bonus tokens
        } 
        if(tokenAvailable < tokens){ // if not enough tokens in reserve
            tokens = tokenAvailable;
            ethersRefund = _value - (tokens / price * 10**18); // calculate how many ethers will respond to user
            _sender.transfer(ethersRefund);// payback 
        }
        owner.transfer(_value - ethersRefund);
        // Add token value to raised variable
        if(stage == 0){
            preSaleRaised += tokens;
        } else if(stage == 1){
            preIcoRaised += tokens;
        } else if(stage == 2){
            icoRaised += tokens;
        } 
        addInvestor(_sender, _value);
        return tokens;
    }
    
    function addInvestor(address _sender, uint _value) private {
        Benefeciary memory ben;
        for(uint i = 0; i < benefeciary.length; i++){
            if(benefeciary[i].wallet == _sender){
                benefeciary[i].amount = benefeciary[i].amount + _value - ethersRefund;
            }
        }
        ben.wallet = msg.sender;
        ben.amount = msg.value - ethersRefund;
        benefeciary.push(ben);
    }
    
    
    function() public payable {
        require(startPreSaleDate <= now && now <= endIcoDate);
        require(msg.value >= 0.1 ether);
        require(!crowdsaleFinished);
        totalCup += msg.value;
        uint token = evaluateTokens(msg.value, msg.sender);
        // send tokens to buyer
        balances[msg.sender] = balances[msg.sender].add(token * 10**18);
        balances[owner] = balances[owner].sub(token * 10**18);
        emit Transfer(owner, msg.sender, token * 10**18);
    }
    
    function showParticipantWei(address _wallet) public view onlyOwner returns(uint){
        for(uint i = 0; i < benefeciary.length; i++){
            if(benefeciary[i].wallet == _wallet){
                return benefeciary[i].amount;// show in wei
            }
        }
        return 0;
    }
    
    function burnUnsoldTokens() public onlyOwner icoHasFinished{
        _burn(owner, balanceOf(owner).sub(reserved));
    }
    
    function crowdSaleStage() public view returns(string){
        string memory result;
        if(uint(Stage) == 0){
            result = "Pre Sale";
        } else if(uint(Stage) == 1){
            result = "Pre-ICO";
        } else if(uint(Stage) == 2){
            result = "ICO";
        }
        return result;
    }
    
    function preSaleRaise() public view returns(uint){
        return preSaleRaised;
    }
    
    function preIcoRaise() public view returns(uint){
        return preIcoRaised;
    }
    
    function icoRaise() public view returns(uint){
        return icoRaised;
    }
    
    modifier icoHasFinished() {
        require(now >= endIcoDate || icoRaised == total || crowdsaleFinished);
        _;
    }
    
    function endIcoByCup() public onlyOwner{
        require(!crowdsaleFinished);
        require(totalCup >= softCup && totalCup <= hardCup);
        crowdsaleFinished = true;
    }
    
    // Output all funds in wei
    function showAllFunds() public onlyOwner view returns(uint){
        return totalCup;
    }
}
function evaluateTokens(uint _value, address _sender) private returns(uint tokens){
    ethersRefund = 0;
    uint bonus;
    uint tokenAvailable;
    uint stage;
    (stage,tokenAvailable,bonus) = getCrowdsaleInfo();
    tokens = _value * price / 10**18; 
    if(bonus != 0){
        tokens = tokens + (tokens * bonus / 100); // calculate bonus tokens
    } 
    if(tokenAvailable < tokens){ // if not enough tokens in reserve
        tokens = tokenAvailable;
        ethersRefund = _value - (tokens / price * 10**18); // calculate how many ethers will respond to user
        _sender.transfer(ethersRefund);// payback 
    }
    owner.transfer(_value - ethersRefund);
    // Add token value to raised variable
    if(stage == 0){
        preSaleRaised += tokens;
    } else if(stage == 1){
        preIcoRaised += tokens;
    } else if(stage == 2){
        icoRaised += tokens;
    } 
    addInvestor(_sender, _value);
    return tokens;
}

//per 0.1 ether will recieved 116 tokens