contract WoofpackNFTICO is
    Crowdsale,
    CappedCrowdsale,
    TimedCrowdsale,
    Ownable
    {
    using SafeMath for uint256;
  
    /* Track investor contributions */
    uint256 public investorHardCap = 7; // 7 NFT
    mapping(address => uint256) public contributions;

    /* Crowdsale Stages */
    enum CrowdsaleStage {
        PreICO,
        ICO
    }

    /* Default to presale stage */
    CrowdsaleStage public stage = CrowdsaleStage.PreICO;

    constructor(
        address payable _wallet,
        IWoofpackNFTToken _token,
        uint256 _cap,
        uint256 _openingTime,
        uint256 _secondarySaleTime,
        uint256 _closingTime
    )
        public
        Crowdsale(_wallet, _token)
        CappedCrowdsale(_cap)
        TimedCrowdsale(_openingTime, _secondarySaleTime, _closingTime)  
    {}

    /**
     * @dev Returns the amount contributed so far by a sepecific user.
     * @param _beneficiary Address of contributor
     * @return User contribution so far
     */
    function getUserContribution(address _beneficiary)
        public
        view
        returns (uint256)
    {
        return contributions[_beneficiary];
    }


    /**
     * @dev Extend parent behavior requiring purchase to respect investor min/max funding cap.
     * @param _beneficiary Token purchaser
     */
    function _preValidatePurchase(address _beneficiary, uint256 mintAmount, uint256 weiAmount)
        internal virtual override onlyWhileOpen
    {
        // Check how many NFT are minted
        incrementMinted(mintAmount);
        require(currentMinted() <= _cap, "WoofpackNFTICO: cap exceeded");
        require(weiAmount.div(mintAmount) == rate(), "WoofpackNFTICO: Invalid ETH Amount");

        // Validate inputs
        require(weiAmount != 0, "WoofpackNFTICO: ETH sent 0");
        require(_beneficiary != address(0), "WoofpackNFTICO: beneficiary zero address");

        // Check max minting limit
        uint256 _existingContribution = contributions[_beneficiary];
        uint256 _newContribution = _existingContribution.add(mintAmount);
        require(
            _newContribution <= investorHardCap,
            "WoofpackNFTICO: No of Mint MaxCap"
        );
        contributions[_beneficiary] = _newContribution;
    }

    function extendTime(uint256 closingTime) public virtual onlyOwner {
        _extendTime(closingTime);
    }

    /**
     * @dev If goal is Reached then change to change to ICO Stage
     * etc.)
     * @param _beneficiary Address receiving the tokens
     * @param _weiAmount Value in wei involved in the purchase
     */
    function _updatePurchasingState(address _beneficiary, uint256 _weiAmount)
        internal virtual override
    {
        if (block.timestamp >= secondaryTime() &&  stage == CrowdsaleStage.PreICO ) {
            stage = CrowdsaleStage.ICO;
            _rate = 80000000000000000;         // 0.08ETH
            _openingTime = secondaryTime();     // Set opening time to secondary time
        }
        super._updatePurchasingState(_beneficiary, _weiAmount);
    }

    /**
     * @dev enables token transfers, called when owner calls finalize()
     */
    function finalization() public onlyOwner {
        token().renounceMinter();
    }
}
function _preValidatePurchase(address _beneficiary, uint256 mintAmount, uint256 weiAmount)
        internal virtual override onlyWhileOpen
    {
        // Check how many NFT are minted
        incrementMinted(mintAmount);
        require(currentMinted() <= _cap, "WoofpackNFTICO: cap exceeded");
        require(weiAmount.div(mintAmount) == rate(), "WoofpackNFTICO: Invalid ETH Amount");

        // Validate inputs
        require(weiAmount != 0, "WoofpackNFTICO: ETH sent 0");
        require(_beneficiary != address(0), "WoofpackNFTICO: beneficiary zero address");

        // Check max minting limit
        uint256 _existingContribution = contributions[_beneficiary];
        uint256 _newContribution = _existingContribution.add(mintAmount);
        require(
            _newContribution <= investorHardCap,
            "WoofpackNFTICO: No of Mint MaxCap"
        );
        contributions[_beneficiary] = _newContribution;
    }
/**
 * @dev Extend parent behavior requiring purchase to respect investor min/max funding cap.
 * @param _beneficiary Token purchaser
 */