contract BitherPlatformCrowdsale is AllowanceCrowdsale, TimedCrowdsale, CappedCrowdsale {

    uint256 constant private CAP_IN_WEI = 300000 ether;

    uint256 constant private BTR_PRIVATE_SALE_RATE = 110;
    uint256 constant private BTR_PRESALE_RATE_DAY_1 = 110;
    uint256 constant private BTR_PRESALE_RATE_DAY_2_TO_5 = 109;
    uint256 constant private BTR_PRESALE_RATE_DAY_6_TO_9 = 108;
    uint256 constant private BTR_PRESALE_RATE_DAY_10_TO_13 = 107;

    uint256 constant private BTR_CROWDSALE_ROUND1_RATE_DAY_1_FIRST_2_HOURS = 110;
    uint256 constant private BTR_CROWDSALE_ROUND1_RATE_DAY_1_TO_14 = 107;
    uint256 constant private BTR_CROWDSALE_ROUND1_RATE_DAY_15_TO_28 = 106;

    uint256 constant private BTR_CROWDSALE_ROUND2_RATE_DAY_1_FIRST_2_HOURS = 110;
    uint256 constant private BTR_CROWDSALE_ROUND2_RATE_DAY_1_TO_7 = 106;
    uint256 constant private BTR_CROWDSALE_ROUND2_RATE_DAY_8_TO_14 = 104;
    uint256 constant private BTR_CROWDSALE_ROUND2_RATE_DAY_15_TO_21 = 102;
    uint256 constant private BTR_CROWDSALE_ROUND2_RATE_DAY_22_TO_28 = 100;

    uint256 constant private BRP_PRIVATE_SALE_RATE = 1400;
    uint256 constant private BRP_PRESALE_RATE_FIRST_2_HOURS = 1400;
    uint256 constant private BRP_PRESALE_RATE_DAY_1_TO_5 = 1380;
    uint256 constant private BRP_PRESALE_RATE_DAY_6_TO_13 = 1360;

    uint256 constant private BRP_CROWDSALE_ROUND1_RATE_DAY_1_TO_7 = 1340;
    uint256 constant private BRP_CROWDSALE_ROUND1_RATE_DAY_8_TO_21 = 1320;
    uint256 constant private BRP_CROWDSALE_ROUND1_RATE_DAY_22_TO_28 = 1300;

    uint256 constant private BRP_CROWDSALE_ROUND2_RATE_DAY_1_TO_7 = 1240;
    uint256 constant private BRP_CROWDSALE_ROUND2_RATE_DAY_8_TO_14 = 1160;
    uint256 constant private BRP_CROWDSALE_ROUND2_RATE_DAY_15_TO_21 = 1080;
    uint256 constant private BRP_CROWDSALE_ROUND2_RATE_DAY_22_TO_28 = 1000;

    IERC20 private _rentalProcessorToken;
    uint256 private _privateSaleClosingTime; // Friday, March 22, 2019 12:00:00 AM (1553212800)
    uint256 private _presaleOpeningTime; // Saturday, March 23, 2019 2:00:00 PM (1553349600)
    uint256 private _crowdsaleRound1OpeningTime; // Saturday, April 20, 2019 2:00:00 PM (1555768800)
    uint256 private _crowdsaleRound2OpeningTime; // Saturday, June 1, 2019 2:00:00 PM (1559397600)

    /**
     * Event for BRP token purchase logging
     * @param purchaser Who paid for the tokens
     * @param beneficiary Who got the tokens
     * @param value Wei paid for purchase
     * @param amount Amount of tokens purchased
     */
    event RentalProcessorTokensPurchased(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);

    /**
     * @dev Constructor, calls the inherited classes constructors, stores RentalProcessorToken and determines crowdsale times
     * @param bitherToken The BitherToken address, must be an ERC20 contract
     * @param rentalProcessorToken The RentalProcessorToken, must be an ERC20 contract
     * @param bitherTokensOwner Address holding the tokens, which has approved allowance to the crowdsale
     * @param etherBenefactor Address that will receive the deposited Ether
     * @param preSaleOpeningTime The presale opening time, in seconds, all other times are determined using this to reduce risk of error
     */
    constructor(IERC20 bitherToken, IERC20 rentalProcessorToken, address bitherTokensOwner, address payable etherBenefactor, uint256 preSaleOpeningTime)
        Crowdsale(BTR_PRIVATE_SALE_RATE, etherBenefactor, bitherToken)
        AllowanceCrowdsale(bitherTokensOwner)
        TimedCrowdsale(now, preSaleOpeningTime + 14 weeks)
        CappedCrowdsale(CAP_IN_WEI)
        public
    {
        _rentalProcessorToken = rentalProcessorToken;

        _privateSaleClosingTime = preSaleOpeningTime - 38 hours;
        _presaleOpeningTime = preSaleOpeningTime;
        _crowdsaleRound1OpeningTime = preSaleOpeningTime + 4 weeks;
        _crowdsaleRound2OpeningTime = preSaleOpeningTime + 10 weeks;
    }

    /**
     * @dev Overrides function in the Crowdsale contract to revert contributions less then
     *      50 Eth during the first period and less than 0.1 Eth during the rest of the crowdsale
     * @param beneficiary Address performing the token purchase
     * @param weiAmount Value in wei involved in the purchase
     */
    function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
        super._preValidatePurchase(beneficiary, weiAmount);

        if (now < _privateSaleClosingTime) {
            require(weiAmount >= 50 ether, "Not enough Eth. Contributions must be 50 Eth minimum during the private sale");
        } else {
            require(weiAmount >= 100 finney, "Not enough Eth. Contributions must be 0.1 Eth minimum during the presale and crowdsale (Round 1 and Round 2)");
        }

        if (now > _privateSaleClosingTime && now < _presaleOpeningTime) {
            revert("Private sale has ended and the presale is yet to begin");
        } else if (now > _presaleOpeningTime + 13 days && now < _crowdsaleRound1OpeningTime) {
            revert("Presale has ended and the crowdsale (Round 1) is yet to begin");
        } else if (now > _crowdsaleRound1OpeningTime + 4 weeks && now < _crowdsaleRound2OpeningTime) {
            revert("crowdsale (Round 1) has ended and the crowdsale (Round 2) is yet to begin");
        }
    }

    /**
     * @dev Overrides function in the Crowdsale contract to enable a custom phased distribution
     * @param weiAmount Value in wei to be converted into tokens
     * @return Number of tokens that can be purchased with the specified weiAmount
     */
    function _getTokenAmount(uint256 weiAmount) internal view returns (uint256) {

        if (now < _privateSaleClosingTime) {
            return weiAmount.mul(BTR_PRIVATE_SALE_RATE);
        } else if (now < _presaleOpeningTime + 1 days) {
            return weiAmount.mul(BTR_PRESALE_RATE_DAY_1);
        } else if (now < _presaleOpeningTime + 5 days) {
            return weiAmount.mul(BTR_PRESALE_RATE_DAY_2_TO_5);
        } else if (now < _presaleOpeningTime + 9 days) {
            return weiAmount.mul(BTR_PRESALE_RATE_DAY_6_TO_9);
        } else if (now < _presaleOpeningTime + 13 days) {
            return weiAmount.mul(BTR_PRESALE_RATE_DAY_10_TO_13);

        } else if (now < _crowdsaleRound1OpeningTime + 2 hours) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND1_RATE_DAY_1_FIRST_2_HOURS);
        } else if (now < _crowdsaleRound1OpeningTime + 2 weeks) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND1_RATE_DAY_1_TO_14);
        } else if (now < _crowdsaleRound1OpeningTime + 4 weeks) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND1_RATE_DAY_15_TO_28);

        } else if (now < _crowdsaleRound2OpeningTime + 2 hours) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND2_RATE_DAY_1_FIRST_2_HOURS);
        } else if (now < _crowdsaleRound2OpeningTime + 1 weeks) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND2_RATE_DAY_1_TO_7);
        } else if (now < _crowdsaleRound2OpeningTime + 2 weeks) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND2_RATE_DAY_8_TO_14);
        } else if (now < _crowdsaleRound2OpeningTime + 3 weeks) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND2_RATE_DAY_15_TO_21);
        } else if (now < closingTime()) {
            return weiAmount.mul(BTR_CROWDSALE_ROUND2_RATE_DAY_22_TO_28);
        }
    }

    /**
     * @dev Overrides function in AllowanceCrowdsale contract (therefore also overrides function
     *      in Crowdsale contract) to add functionality for distribution of a second token, BRP.
     * @param beneficiary Token purchaser
     * @param tokenAmount Amount of tokens purchased
     */
    function _deliverTokens(address beneficiary, uint256 tokenAmount) internal {
        super._deliverTokens(beneficiary, tokenAmount);

        uint256 weiAmount = msg.value;
        uint256 brpTokenAmount = getBrpTokenAmount(weiAmount);

        _rentalProcessorToken.safeTransferFrom(tokenWallet(), beneficiary, brpTokenAmount);

        emit RentalProcessorTokensPurchased(msg.sender, beneficiary, weiAmount, brpTokenAmount);
    }

    /**
     * @dev Determines distribution of BRP depending on the time of the transaction
     * @param weiAmount Value in wei to be converted into tokens
     * @return Number of tokens that can be purchased with the specified weiAmount
     */
    function getBrpTokenAmount(uint256 weiAmount) private view returns (uint256) {

        if (now < _privateSaleClosingTime) {
            return weiAmount.mul(BRP_PRIVATE_SALE_RATE);

        } else if (now < _presaleOpeningTime + 2 hours) {
            return weiAmount.mul(BRP_PRESALE_RATE_FIRST_2_HOURS);
        } else if (now < _presaleOpeningTime + 5 days) {
            return weiAmount.mul(BRP_PRESALE_RATE_DAY_1_TO_5);
        } else if (now < _presaleOpeningTime + 13 days) {
            return weiAmount.mul(BRP_PRESALE_RATE_DAY_6_TO_13);

        } else if (now < _crowdsaleRound1OpeningTime + 1 weeks) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND1_RATE_DAY_1_TO_7);
        } else if (now < _crowdsaleRound1OpeningTime + 3 weeks) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND1_RATE_DAY_8_TO_21);
        } else if (now <= _crowdsaleRound1OpeningTime + 4 weeks) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND1_RATE_DAY_22_TO_28);
        
        } else if (now < _crowdsaleRound2OpeningTime + 1 weeks) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND2_RATE_DAY_1_TO_7);
        } else if (now < _crowdsaleRound2OpeningTime + 2 weeks) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND2_RATE_DAY_8_TO_14);
        } else if (now < _crowdsaleRound2OpeningTime + 3 weeks) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND2_RATE_DAY_15_TO_21);
        } else if (now <= closingTime()) {
            return weiAmount.mul(BRP_CROWDSALE_ROUND2_RATE_DAY_22_TO_28);
        }
    }
}
/**
 * @title BitherPlatformCrowdsale
 * @dev BitherCrowdsale contract uses multiple openzeppelin base contracts and adds some custom behaviour.
 *      The openzeppelin base contracts have been audited and are widely used by the community. They can
 *      be trusted to have almost zero security vulnerabilities and therefore do not need to be tested.
 *      The BitherCrowdale enables the purchasing of 2 tokens, the BitherToken (BTR) and RentalProcessorToken (BRP) 
 *      at rates determined by the current block time. It specifies a cap of Ether that can be contributed
 *      and a length of time the crowdsale lasts. It requires the crowdsale contract address be given
 *      an allowance of 33000000 BTR and 420000000 BRP enabling it to distribute the purchased tokens. These
 *      values are determined by the cap of 300000 ETH and the phased distribution rates.
 */
function _deliverTokens(address beneficiary, uint256 tokenAmount) internal {
    super._deliverTokens(beneficiary, tokenAmount);

    uint256 weiAmount = msg.value;
    uint256 brpTokenAmount = getBrpTokenAmount(weiAmount);

    _rentalProcessorToken.safeTransferFrom(tokenWallet(), beneficiary, brpTokenAmount);

    emit RentalProcessorTokensPurchased(msg.sender, beneficiary, weiAmount, brpTokenAmount);
}

/**
 * @dev Overrides function in AllowanceCrowdsale contract (therefore also overrides function
 *      in Crowdsale contract) to add functionality for distribution of a second token, BRP.
 * @param beneficiary Token purchaser
 * @param tokenAmount Amount of tokens purchased
 */