contract DOCTCrowdsale is Ownable, HasNoContracts, CanReclaimToken, Destructible {
    using SafeMath for uint256;

    uint256 constant  DOCT_TO_ETH_DECIMALS = 10000000000;    //Need this because ETH decimals is 18, while DOCT decimals is 8.

    DOCTToken public token;

    struct Round {
        uint256 start;          //Timestamp of crowdsale round start
        uint256 end;            //Timestamp of crowdsale round end
        uint256 rate;           //Rate: how much TOKEN one will get fo 1 ETH during this round
        uint256 rateBulk;       //Rate for bulk purshases
        uint256 bulkThreshold;  //If purshase more than this amount, bulk rate applied
    }
    Round[] public rounds;          //Array of crowdsale rounds
    uint256 public hardCap;         //Max amount of tokens to mint
    uint256 public tokensMinted;    //Amount of tokens already minted
    bool public finalized;          //crowdsale is finalized

    function DOCTCrowdsale (
        uint256 _hardCap,
        uint256[] roundStarts,
        uint256[] roundEnds,
        uint256[] roundRates,
        uint256[] roundRatesBulk,
        uint256[] roundBulkThreshold
    ) public {
        token = new DOCTToken();
        token.setFounder(owner);
        token.setTransferEnabled(false);

        tokensMinted = token.totalSupply();

        //Check all paramaters are correct and create rounds
        require(_hardCap > 0);                    //Need something to sell
        hardCap = _hardCap;

        initRounds(roundStarts, roundEnds, roundRates, roundRatesBulk, roundBulkThreshold);
    }
    function initRounds(uint256[] roundStarts, uint256[] roundEnds, uint256[] roundRates, uint256[] roundRatesBulk, uint256[] roundBulkThreshold) internal {
        require(
            (roundStarts.length > 0)  &&                //There should be at least one round
            (roundStarts.length == roundEnds.length) &&
            (roundStarts.length == roundRates.length) &&
            (roundStarts.length == roundRatesBulk.length) &&
            (roundStarts.length == roundBulkThreshold.length)
        );                   
        uint256 prevRoundEnd = now;
        rounds.length = roundStarts.length;             //initialize rounds array
        for(uint8 i=0; i < roundStarts.length; i++){
            rounds[i] = Round({start:roundStarts[i], end:roundEnds[i], rate:roundRates[i], rateBulk:roundRatesBulk[i], bulkThreshold:roundBulkThreshold[i]});
            Round storage r = rounds[i];
            require(prevRoundEnd <= r.start);
            require(r.start < r.end);
            require(r.bulkThreshold > 0);
            prevRoundEnd = rounds[i].end;
        }
    }
    function setRound(uint8 roundNum, uint256 start, uint256 end, uint256 rate, uint256 rateBulk, uint256 bulkThreshold) onlyOwner external {
        uint8 round = roundNum-1;
        if(round > 0){
            require(rounds[round - 1].end <= start);
        }
        if(round < rounds.length - 1){
            require(end <= rounds[round + 1].start);   
        }
        rounds[round].start = start;
        rounds[round].end = end;
        rounds[round].rate = rate;
        rounds[round].rateBulk = rateBulk;
        rounds[round].bulkThreshold = bulkThreshold;
    }


    /**
    * @notice Buy tokens
    */
    function() payable public {
        require(msg.value > 0);
        require(crowdsaleRunning());

        uint256 rate = currentRate(msg.value);
        require(rate > 0);
        uint256 tokens = rate.mul(msg.value).div(DOCT_TO_ETH_DECIMALS);
        mintTokens(msg.sender, tokens);
    }

    /**
    * @notice Mint tokens for purshases with Non-Ether currencies
    * @param beneficiary whom to send tokend
    * @param amount how much tokens to send
    * param message reason why we are sending tokens (not stored anythere, only in transaction itself)
    */
    function saleNonEther(address beneficiary, uint256 amount, string /*message*/) onlyOwner external{
        mintTokens(beneficiary, amount);
    }

    /**
    * @notice Bulk mint tokens (different amounts)
    * @param beneficiaries array whom to send tokend
    * @param amounts array how much tokens to send
    * param message reason why we are sending tokens (not stored anythere, only in transaction itself)
    */
    function bulkTokenSend(address[] beneficiaries, uint256[] amounts, string /*message*/) onlyOwner external{
        require(beneficiaries.length == amounts.length);
        for(uint32 i=0; i < beneficiaries.length; i++){
            mintTokens(beneficiaries[i], amounts[i]);
        }
    }
    /**
    * @notice Bulk mint tokens (same amounts)
    * @param beneficiaries array whom to send tokend
    * @param amount how much tokens to send
    * param message reason why we are sending tokens (not stored anythere, only in transaction itself)
    */
    function bulkTokenSend(address[] beneficiaries, uint256 amount, string /*message*/) onlyOwner external{
        require(amount > 0);
        for(uint32 i=0; i < beneficiaries.length; i++){
            mintTokens(beneficiaries[i], amount);
        }
    }

    /**
    * @notice Shows if crowdsale is running
    */ 
    function crowdsaleRunning() constant public returns(bool){
        return !finalized && (tokensMinted < hardCap) && (currentRoundNum() > 0);
    }

    /**
    * @notice Fetches current Round number
    * @return round number (index in rounds array + 1) or 0 if none
    */
    function currentRoundNum() view public returns(uint8) {
        for(uint8 i=0; i < rounds.length; i++){
            if( (now > rounds[i].start) && (now <= rounds[i].end) ) return i+1;
        }
        return 0;
    }
    /**
    * @notice Fetches current rate (how many tokens you get for 1 ETH)
    * @param amount how much ether is received
    * @return calculated rate or zero if no round of crowdsale is running
    */
    function currentRate(uint256 amount) view public returns(uint256) {
        uint8 roundNum = currentRoundNum();
        if(roundNum == 0) {
            return 0;
        }else{
            uint8 round = roundNum-1;
            if(amount < rounds[round].bulkThreshold){
                return rounds[round].rate;
            }else{
                return rounds[round].rateBulk;
            }
        }
    }

    /**
    * @dev Helper function to mint tokens and increase tokensMinted counter
    */
    function mintTokens(address beneficiary, uint256 amount) internal {
        tokensMinted = tokensMinted.add(amount);
        require(tokensMinted <= hardCap);
        assert(token.mint(beneficiary, amount));
    }

    /**
    * @notice Sends collected funds to owner
    */
    function claimEther() public onlyOwner {
        if(this.balance > 0){
            owner.transfer(this.balance);
        }
    }

    /**
    * @notice Finalizes ICO: changes token ownership to founder, allows token transfers
    */
    function finalizeCrowdsale() onlyOwner public {
        finalized = true;
        assert(token.finishMinting());
        token.setTransferEnabled(true);
        token.transferOwnership(owner);
        claimEther();
    }

}
/**
 * @title DocTailor Crowdsale
 */
function currentRate(uint256 amount) view public returns(uint256) {
    uint8 roundNum = currentRoundNum();
    if(roundNum == 0) {
        return 0;
    }else{
        uint8 round = roundNum-1;
        if(amount < rounds[round].bulkThreshold){
            return rounds[round].rate;
        }else{
            return rounds[round].rateBulk;
        }
    }
}

/**
 * @notice Fetches current rate (how many tokens you get for 1 ETH)
 * @param amount how much ether is received
 * @return calculated rate or zero if no round of crowdsale is running
 */