contract BitNauticCrowdsale is CappedCrowdsale, RefundableCrowdsale {
    uint256 constant public crowdsaleInitialSupply = 35000000 * 10 ** 18; // 70% of token cap
//    uint256 constant public crowdsaleSoftCap = 2 ether;
//    uint256 constant public crowdsaleHardCap = 10 ether;
    uint256 constant public crowdsaleSoftCap = 5000 ether;
    uint256 constant public crowdsaleHardCap = 50000 ether;

    uint256 constant public preICOStartTime = 1525132800;          // 2018-05-01 00:00 GMT+0
    uint256 constant public mainICOStartTime = 1527811200;         // 2018-06-01 00:00 GMT+0
    uint256 constant public mainICOFirstWeekEndTime = 1528416000;  // 2018-06-08 00:00 GMT+0
    uint256 constant public mainICOSecondWeekEndTime = 1529020800; // 2018-06-15 00:00 GMT+0
    uint256 constant public mainICOThirdWeekEndTime = 1529625600;  // 2018-06-22 00:00 GMT+0
    uint256 constant public mainICOFourthWeekEndTime = 1530403200; // 2018-07-01 00:00 GMT+0
    uint256 constant public mainICOEndTime = 1532995200;           // 2018-07-31 00:00 GMT+0

//    uint256 public preICOStartTime = now;          // 2018-05-01 00:00 GMT+0
//    uint256 constant public mainICOStartTime = preICOStartTime;         // 2018-06-01 00:00 GMT+0
//    uint256 constant public mainICOFirstWeekEndTime = mainICOStartTime;  // 2018-06-08 00:00 GMT+0
//    uint256 constant public mainICOSecondWeekEndTime = mainICOFirstWeekEndTime; // 2018-06-15 00:00 GMT+0
//    uint256 constant public mainICOThirdWeekEndTime = mainICOSecondWeekEndTime;  // 2018-06-22 00:00 GMT+0
//    uint256 constant public mainICOFourthWeekEndTime = mainICOThirdWeekEndTime; // 2018-07-01 00:00 GMT+0
//    uint256 constant public mainICOEndTime = mainICOFourthWeekEndTime + 5 minutes;           // 2018-07-30 00:00 GMT+0

    uint256 constant public tokenBaseRate = 500; // 1 ETH = 500 BTNT

    // bonuses in percentage
    uint256 constant public preICOBonus = 30;
    uint256 constant public firstWeekBonus = 20;
    uint256 constant public secondWeekBonus = 15;
    uint256 constant public thirdWeekBonus = 10;
    uint256 constant public fourthWeekBonus = 5;

    uint256 public teamSupply =     3000000 * 10 ** 18; // 6% of token cap
    uint256 public bountySupply =   2500000 * 10 ** 18; // 5% of token cap
    uint256 public reserveSupply =  5000000 * 10 ** 18; // 10% of token cap
    uint256 public advisorSupply =  2500000 * 10 ** 18; // 5% of token cap
    uint256 public founderSupply =  2000000 * 10 ** 18; // 4% of token cap

    // amount of tokens each address will receive at the end of the crowdsale
    mapping (address => uint256) public creditOf;

    mapping (address => uint256) public weiInvestedBy;

    BitNauticWhitelist public whitelist;

    /** Constructor BitNauticICO */
    constructor(BitNauticToken _token, BitNauticWhitelist _whitelist, address _wallet)
    CappedCrowdsale(crowdsaleHardCap)
    FinalizableCrowdsale()
    RefundableCrowdsale(crowdsaleSoftCap)
    Crowdsale(_token, crowdsaleInitialSupply, preICOStartTime, mainICOEndTime, _wallet) public
    {
        whitelist = _whitelist;
    }

    function _tokenPurchased(address buyer, address beneficiary, uint256 weiAmount) internal returns (bool) {
        require(SafeMath.add(weiInvestedBy[buyer], weiAmount) <= whitelist.contributionCap(buyer));

        uint256 tokens = SafeMath.mul(weiAmount, tokenBaseRate);

        tokens = tokens.add(SafeMath.mul(tokens, getCurrentBonus()).div(100));

        require(publicSupply >= tokens);

        publicSupply = publicSupply.sub(tokens);

        creditOf[beneficiary] = creditOf[beneficiary].add(tokens);
        weiInvestedBy[buyer] = SafeMath.add(weiInvestedBy[buyer], weiAmount);

        emit TokenPurchase(buyer, beneficiary, weiAmount, tokens);

        return true;
    }

    address constant public privateSaleWallet = 0x5A01D561AE864006c6B733f21f8D4311d1E1B42a;

    function goalReached() public returns (bool) {
        if (weiRaised + privateSaleWallet.balance >= goal) {
            _goalReached = true;
        }

        return _goalReached;
    }

    function getCurrentBonus() public view returns (uint256) {
        if (now < mainICOStartTime) {
            return preICOBonus;
        } else if (now < mainICOFirstWeekEndTime) {
            return firstWeekBonus;
        } else if (now < mainICOSecondWeekEndTime) {
            return secondWeekBonus;
        } else if (now < mainICOThirdWeekEndTime) {
            return thirdWeekBonus;
        } else if (now < mainICOFourthWeekEndTime) {
            return fourthWeekBonus;
        } else {
            return 0;
        }
    }

    function claimBitNauticTokens() public returns (bool) {
        return grantInvestorTokens(msg.sender);
    }

    function grantInvestorTokens(address investor) public returns (bool) {
        require(creditOf[investor] > 0);
        require(now > mainICOEndTime && whitelist.AMLWhitelisted(investor));
        require(goalReached());

        assert(token.mint(investor, creditOf[investor]));
        creditOf[investor] = 0;

        return true;
    }

    function grantInvestorsTokens(address[] investors) public returns (bool) {
        require(now > mainICOEndTime);
        require(goalReached());

        for (uint256 i = 0; i < investors.length; i++) {
            if (creditOf[investors[i]] > 0 && whitelist.AMLWhitelisted(investors[i])) {
                token.mint(investors[i], creditOf[investors[i]]);
                creditOf[investors[i]] = 0;
            }
        }

        return true;
    }

    function bountyDrop(address[] recipients, uint256[] values) onlyOwner public returns (bool) {
        require(now > mainICOEndTime);
        require(goalReached());
        require(recipients.length == values.length);

        for (uint256 i = 0; i < recipients.length; i++) {
            values[i] = SafeMath.mul(values[i], 1 ether);
            if (bountySupply >= values[i]) {
                return false;
            }
            bountySupply = SafeMath.sub(bountySupply, values[i]);
            token.mint(recipients[i], values[i]);
        }

        return true;
    }

    uint256 public teamTimeLock = mainICOEndTime;
    uint256 public founderTimeLock = mainICOEndTime + 365 days;
    uint256 public advisorTimeLock = mainICOEndTime + 180 days;
    uint256 public reserveTimeLock = mainICOEndTime;

    function grantAdvisorTokens(address advisorAddress) onlyOwner public {
        require((advisorSupply > 0) && (advisorTimeLock < now));
        require(goalReached());

        token.mint(advisorAddress, advisorSupply);
        advisorSupply = 0;
    }

    uint256 public teamVestingCounter = 0; // months of vesting

    function grantTeamTokens(address teamAddress) onlyOwner public {
        require((teamVestingCounter < 12) && (teamTimeLock < now));
        require(goalReached());

        teamTimeLock = SafeMath.add(teamTimeLock, 4 weeks);
        token.mint(teamAddress, SafeMath.div(teamSupply, 12));
        teamVestingCounter = SafeMath.add(teamVestingCounter, 1);
    }

    function grantFounderTokens(address founderAddress) onlyOwner public {
        require((founderSupply > 0) && (founderTimeLock < now));
        require(goalReached());

        token.mint(founderAddress, founderSupply);
        founderSupply = 0;
    }

    function grantReserveTokens(address beneficiary) onlyOwner public {
        require((reserveSupply > 0) && (now > reserveTimeLock));
        require(goalReached());

        token.mint(beneficiary, reserveSupply);
        reserveSupply = 0;
    }

    function transferTokenOwnership(address newTokenOwner) onlyOwner public returns (bool) {
        return token.transferOwnership(newTokenOwner);
    }
}
function grantTeamTokens(address teamAddress) onlyOwner public {
    require((teamVestingCounter < 12) && (teamTimeLock < now));
    require(goalReached());

    teamTimeLock = SafeMath.add(teamTimeLock, 4 weeks);
    token.mint(teamAddress, SafeMath.div(teamSupply, 12));
    teamVestingCounter = SafeMath.add(teamVestingCounter, 1);
}

// months of vesting