contract Namahecrowdsale is Pausable, IndividuallyCappedCrowdsale {

    using SafeMath for uint256;

    uint256 public openingTime;
    uint256 public closingTime;
    bool public isFinalized = false;

    bool public quarterFirst = true;
    bool public quarterSecond = true;
    bool public quarterThird = true;
    bool public quarterFourth = true;

    uint256 public rate = 1000;
    bool public preAllocationsPending = true;         // Indicates if pre allocations are pending
    uint256 public totalAllocated = 0;
    mapping(address => uint256) public allocated;     // To track allocated tokens
    address[] public allocatedAddresses;              // To track list of contributors

    address public constant _controller  = 0x6E21c63511b0dD8f2C67BB5230C5b831f6cd7986;
    address public constant _reserve     = 0xE4627eE46f9E0071571614ca86441AFb42972A66;
    address public constant _promo       = 0x894387C61144f1F3a2422D17E61638B3263286Ee;
    address public constant _holding     = 0xC7592b24b4108b387A9F413fa4eA2506a7F32Ae9;

    address public constant _founder_one = 0x3f7dB633ABAb31A687dd1DFa0876Df12Bfc18DBE;
    address public constant _founder_two = 0xCDb0EF350717d743d47A358EADE1DF2CB71c1E4F;

    uint256 public constant PROMO_TOKEN_AMOUNT   = 6000000E18; // Promotional 6,000,000;
    uint256 public constant RESERVE_TOKEN_AMOUNT = 24000000E18; // Reserved tokens 24,000,000;
    uint256 public constant TEAM_TOKEN_AMOUNT    = 15000000E18; // Team and Advisors 15,000,000 each;

    uint256 public constant QUARTERLY_RELEASE    = 3750000E18; // To allocate 3,750,000;

    MintableToken public token;

    event AllocationApproved(address indexed purchaser, uint256 amount);
    event Finalized();

    constructor (
        uint256 _openingTime,
        uint256 _closingTime,
        uint256 _cap,
        uint256 _miniumInvestment,
        uint256 _individualCap,
        MintableToken _token
    )

    public
    Crowdsale(rate, _controller, _token)
    CappedCrowdsale(_cap)
    IndividuallyCappedCrowdsale(_individualCap, _miniumInvestment)
    {
        openingTime = _openingTime;
        closingTime = _closingTime;
        token = _token;

    }

    /**
    * @dev Reverts if not in crowdsale time range.
    */
    modifier onlyWhileOpen {
        require(block.timestamp >= openingTime && block.timestamp <= closingTime);
        _;
    }

    /**
    * @dev Complete pre-allocations to team, promotions and reserve pool
    */
    function doPreAllocations() external onlyOwner returns (bool) {
        require(preAllocationsPending);

        //Allocate promo tokens immediately
        token.transfer(_promo, PROMO_TOKEN_AMOUNT);

        //Allocate team tokens _team account through internal method
        //_allocateTokens(_team, TEAM_TOKEN_AMOUNT);
        _allocateTokens(_founder_one, TEAM_TOKEN_AMOUNT);
        _allocateTokens(_founder_two, TEAM_TOKEN_AMOUNT);

        //Allocate reserved tokens to _reserve account through internal method
        _allocateTokens(_reserve, RESERVE_TOKEN_AMOUNT);

        totalAllocated = totalAllocated.add(PROMO_TOKEN_AMOUNT);
        preAllocationsPending = false;
        return true;
    }

    /**
    * @dev Approves tokens allocated to a beneficiary
    * @param _beneficiary Token purchaser
    */
    function approveAllocation(address _beneficiary) external onlyOwner returns (bool) {
        require(_beneficiary != address(0));
        require(_beneficiary != _founder_one);
        require(_beneficiary != _founder_two);
        require(_beneficiary != _reserve);

        uint256 allocatedTokens = allocated[_beneficiary];
        token.transfer(_beneficiary, allocated[_beneficiary]);
        allocated[_beneficiary] = 0;
        emit AllocationApproved(_beneficiary, allocatedTokens);

        return true;
    }

    /**
    * @dev Release reserved tokens to _reserve address only after vesting period
    */
    function releaseReservedTokens() external onlyOwner {
        require(block.timestamp > (openingTime.add(52 weeks)));
        require(allocated[_reserve] > 0);

        token.transfer(_reserve, RESERVE_TOKEN_AMOUNT);
        allocated[_reserve] = 0;
    }

    /**
     * @dev Must be called after crowdsale ends, to do some extra finalization
     * work. Calls the contract's finalization function.
     */
    function finalize() external onlyOwner {
        require(!isFinalized);
        require(hasClosed());
        require(!preAllocationsPending);

        finalization();
        emit Finalized();

        isFinalized = true;
    }

    /**
     * @dev Extends crowdsale end date
     */
    function extendCrowdsale(uint256 _closingTime) external onlyOwner {
        require(_closingTime > closingTime);
        require(block.timestamp <= openingTime.add(36 weeks));

        closingTime = _closingTime;
    }

    /**
     * @dev Every quarter release, 25% of token to the founders
     */
    function releaseFounderTokens() external onlyOwner returns (bool) {
        if (quarterFirst && block.timestamp >= (openingTime.add(10 weeks))) {
            quarterFirst = false;
            token.transfer(_founder_one, QUARTERLY_RELEASE);
            token.transfer(_founder_two, QUARTERLY_RELEASE);
            allocated[_founder_one] = allocated[_founder_one].sub(QUARTERLY_RELEASE);
            allocated[_founder_two] = allocated[_founder_two].sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);

        }

        if (quarterSecond && block.timestamp >= (openingTime.add(22 weeks))) {
            quarterSecond = false;
            token.transfer(_founder_one, QUARTERLY_RELEASE);
            token.transfer(_founder_two, QUARTERLY_RELEASE);
            allocated[_founder_one] = allocated[_founder_one].sub(QUARTERLY_RELEASE);
            allocated[_founder_two] = allocated[_founder_two].sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
        }

        if (quarterThird && block.timestamp >= (openingTime.add(34 weeks))) {
            quarterThird = false;
            token.transfer(_founder_one, QUARTERLY_RELEASE);
            token.transfer(_founder_two, QUARTERLY_RELEASE);
            allocated[_founder_one] = allocated[_founder_one].sub(QUARTERLY_RELEASE);
            allocated[_founder_two] = allocated[_founder_two].sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
        }

        if (quarterFourth && block.timestamp >= (openingTime.add(46 weeks))) {
            quarterFourth = false;
            token.transfer(_founder_one, QUARTERLY_RELEASE);
            token.transfer(_founder_two, QUARTERLY_RELEASE);
            allocated[_founder_one] = allocated[_founder_one].sub(QUARTERLY_RELEASE);
            allocated[_founder_two] = allocated[_founder_two].sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
            totalAllocated = totalAllocated.sub(QUARTERLY_RELEASE);
        }

        return true;
    }

    /**
    * @dev Checks whether the period in which the crowdsale is open has already elapsed.
    * @return Whether crowdsale period has elapsed
    */
    function hasClosed() public view returns (bool) {
        return block.timestamp > closingTime;
    }

    /**
    * @dev Returns rate as per bonus structure
    * @return Rate
    */
    function getRate() public view returns (uint256) {

        if (block.timestamp <= (openingTime.add(14 days))) {return rate.add(200);}
        if (block.timestamp <= (openingTime.add(28 days))) {return rate.add(100);}
        if (block.timestamp <= (openingTime.add(49 days))) {return rate.add(50);}

        return rate;
    }

    /**
    * @dev Releases unapproved tokens to _holding address. Only called during finalization.
    */
    function reclaimAllocated() internal {

        uint256 unapprovedTokens = 0;
        for (uint256 i = 0; i < allocatedAddresses.length; i++) {
            // skip counting _team and _reserve allocations
            if (allocatedAddresses[i] != _founder_one && allocatedAddresses[i] != _founder_two && allocatedAddresses[i] != _reserve) {
                unapprovedTokens = unapprovedTokens.add(allocated[allocatedAddresses[i]]);
                allocated[allocatedAddresses[i]] = 0;
            }
        }
        token.transfer(_holding, unapprovedTokens);
    }

    /**
    * @dev Reclaim remaining tokens after crowdsale is complete. Tokens allocated to
    * _team and _balance will be left out to arrive at balance tokens.
    */
    function reclaimBalanceTokens() internal {

        uint256 balanceTokens = token.balanceOf(this);
        balanceTokens = balanceTokens.sub(allocated[_founder_one]);
        balanceTokens = balanceTokens.sub(allocated[_founder_two]);
        balanceTokens = balanceTokens.sub(allocated[_reserve]);
        token.transfer(_controller, balanceTokens);
    }

    /**
    * @dev Overridden to add finalization logic.
    */
    function finalization() internal {
        reclaimAllocated();
        reclaimBalanceTokens();
    }

    /**
    * @dev Overridden to adjust the rate including bonus
    * @param _weiAmount Value in wei to be converted into tokens
    * @return Number of tokens that can be purchased with the given _weiAmount
    */
    function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) {
        uint256 tokenAmount = _weiAmount.mul(getRate());
        return tokenAmount;
    }

    /**
    * @dev Extend parent behavior requiring to be within contributing period.
    * If purchases are paused, transactions fail.
    * @param _beneficiary Token purchaser
    * @param _weiAmount Amount of wei contributed
    */
    function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal view onlyWhileOpen whenNotPaused {
        super._preValidatePurchase(_beneficiary, _weiAmount);
    }

    /**
    * @dev Overriden method to update tokens allocated to a beneficiary
    * @param _beneficiary Address sending ether
    * @param _tokenAmount Number of token to be allocated
    */
    function _allocateTokens(address _beneficiary, uint256 _tokenAmount) internal {
        //token.transfer(_beneficiary, _tokenAmount);
        require(token.balanceOf(this) >= totalAllocated.add(_tokenAmount));
        allocated[_beneficiary] = allocated[_beneficiary].add(_tokenAmount);
        totalAllocated = totalAllocated.add(_tokenAmount);
        allocatedAddresses.push(_beneficiary);

    }
}
function releaseReservedTokens() external onlyOwner {
    require(block.timestamp > (openingTime.add(52 weeks)));
    require(allocated[_reserve] > 0);

    token.transfer(_reserve, RESERVE_TOKEN_AMOUNT);
    allocated[_reserve] = 0;
}

/**
 * @dev Release reserved tokens to _reserve address only after vesting period
 */