contract EarlyAdoptersPassport is ERC721, Ownable {
    using SafeMath for uint256;

    uint256 public startingIndexBlock;

    uint256 public startingIndex;

    uint256 public constant pupPrice = 80000000000000000; //0.08 ETH

    uint public constant maxPurchase = 20;

    uint256 public MAX_PASSPORTS;

    bool public saleIsActive = false;

    constructor(string memory name, string memory symbol, uint256 maxNftSupply) ERC721(name, symbol) {
        MAX_PASSPORTS = maxNftSupply;
    }

    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        msg.sender.transfer(balance);
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }

    /*
    * Pause sale if active, make active if paused
    */
    function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }

    /**
     * @dev Burns `tokenId`. See {ERC721-_burn}.
     *
     * Requirements:
     *
     * - The caller must own `tokenId` or be an approved operator.
     * - Allows Lost Puppy Club to burn passport on claim
     */
    function burn(uint256 tokenId) public virtual {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved");
        _burn(tokenId);
    }

    /**
    * Mints Lost Pups Early Adopters Passport
    */
    function mintPassport(uint numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint passport");
        require(numberOfTokens <= maxPurchase, "Can only mint 20 tokens at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_PASSPORTS, "Purchase would exceed max supply of passports");
        require(pupPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");
        
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_PASSPORTS) {
                _safeMint(msg.sender, mintIndex);
            }
        }

        // If we haven't set the starting index and this is 1) the first token to be sold after
        if (startingIndexBlock == 0) {
            startingIndexBlock = block.number;
        } 
    }

    /**
     * Set the starting index for the collection
     */
    function setStartingIndex() public {
        require(startingIndex == 0, "Starting index is already set");
        require(startingIndexBlock != 0, "Starting index block must be set");
        
        startingIndex = uint(blockhash(startingIndexBlock)) % MAX_PASSPORTS;
        // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
        if (block.number.sub(startingIndexBlock) > 255) {
            startingIndex = uint(blockhash(block.number - 1)) % MAX_PASSPORTS;
        }
        // Prevent default sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex.add(1);
        }
    }

    /**
     * Set the starting index block for the collection, essentially unblocking
     * setting starting index
     */
    function emergencySetStartingIndexBlock() public onlyOwner {
        require(startingIndex == 0, "Starting index is already set");
        
        startingIndexBlock = block.number;
    }
}
/**
 * @title LostPuppyClub Passport contract
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function burn(uint256 tokenId) public virtual {
    //solhint-disable-next-line max-line-length
    require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved");
    _burn(tokenId);
}

/**
 * @dev Burns `tokenId`. See {ERC721-_burn}.
 *
 * Requirements:
 *
 * - The caller must own `tokenId` or be an approved operator.
 * - Allows Lost Puppy Club to burn passport on claim
 */