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

    bool public saleIsActive = false;
    string public PROVENANCE =
        "60fb632188bea0da4b0596e6a697e11fe62c68e37bcc46f378fee5bcfaaa5e85";
    string public OPENSEA_STORE_METADATA =
        "ipfs://bafkreigugk6gwcbwb25zvh7ekvunrduuiiuy2xle5ynmepfwcdja3lftbq";

    uint256 public constant PER_KITTY_PRICE = 50000000000000000;
    uint256 public constant MAX_KITTY_ALLOWANCE = 25;

    uint256 public MAX_TOTAL_KITTIES;
    uint256 public REVEAL_TIMESTAMP;
    uint256 public startingIndexBlock;
    uint256 public startingIndex;

    constructor(
        string memory name,
        string memory symbol,
        uint256 maxNftSupply,
        uint256 saleStart
    ) ERC721(name, symbol) {
        MAX_TOTAL_KITTIES = maxNftSupply;
        REVEAL_TIMESTAMP = saleStart + (86400 * 5);
    }

    /**
     *  @dev Pay the electricity bill
     */
    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        msg.sender.transfer(balance);
    }

    /**
     *  @dev Set aside a portion of the total supply for the team
     */
    function reserveKittyCats() public onlyOwner {
        uint256 supply = totalSupply();
        uint256 i;
        for (i = 0; i < 25; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    function setRevealTimestamp(uint256 revealTimeStamp) public onlyOwner {
        REVEAL_TIMESTAMP = revealTimeStamp;
    }

    /**
     *  @dev Set provenance once it's calculated
     */
    function setProvenanceHash(string memory provenanceHash) public onlyOwner {
        require(
            bytes(PROVENANCE).length == 0,
            "Provenance has already been set, no do-overs!"
        );
        PROVENANCE = provenanceHash;
    }

    /**
     *  @dev Set metadata to make OpenSea happy
     */
    function setContractURI(string memory _contractMetadataURI)
        public
        onlyOwner
    {
        OPENSEA_STORE_METADATA = _contractMetadataURI;
    }

    /**
     *  @dev Get contract metadata to make OpenSea happy
     */
    function contractURI() public view returns (string memory) {
        return OPENSEA_STORE_METADATA;
    }

    /**
     *  @dev Set the IPFS baseURI, including ending `/` where JSON is located
     */
    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }

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

    /**
     *  @dev Adopt new kitty cats
     */
    function adoptKittyCats(uint256 numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint Mr. Whiskers");
        require(
            numberOfTokens <= MAX_KITTY_ALLOWANCE,
            "Can only mint 25 tokens at a time"
        );
        require(
            totalSupply().add(numberOfTokens) <= MAX_TOTAL_KITTIES,
            "Purchase would exceed max supply of Mr. Whiskers"
        );
        require(
            PER_KITTY_PRICE.mul(numberOfTokens) <= msg.value,
            "Ether value sent is not correct"
        );

        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_TOTAL_KITTIES) {
                _safeMint(msg.sender, mintIndex);
            }
        }

        /*
            If we haven't set the starting index and this is either 
                1) the last saleable token or 
                2) the first token to be sold after
            the end of pre-sale, set the starting index block
        */
        if (
            startingIndexBlock == 0 &&
            (totalSupply() == MAX_TOTAL_KITTIES ||
                block.timestamp >= REVEAL_TIMESTAMP)
        ) {
            startingIndexBlock = block.number;
        }
    }

    /**
     * @dev 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 =
            uint256(blockhash(startingIndexBlock)) %
            MAX_TOTAL_KITTIES;
        // 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 =
                uint256(blockhash(block.number - 1)) %
                MAX_TOTAL_KITTIES;
        }
        // Prevent default sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex.add(1);
        }
    }

    /**
     * @dev 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 Meows(DAO)'s Genesis Collection
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function adoptKittyCats(uint256 numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint Mr. Whiskers");
        require(
            numberOfTokens <= MAX_KITTY_ALLOWANCE,
            "Can only mint 25 tokens at a time"
        );
        require(
            totalSupply().add(numberOfTokens) <= MAX_TOTAL_KITTIES,
            "Purchase would exceed max supply of Mr. Whiskers"
        );
        require(
            PER_KITTY_PRICE.mul(numberOfTokens) <= msg.value,
            "Ether value sent is not correct"
        );

        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_TOTAL_KITTIES) {
                _safeMint(msg.sender, mintIndex);
            }
        }

        /*
            If we haven't set the starting index and this is either 
                1) the last saleable token or 
                2) the first token to be sold after
            the end of pre-sale, set the starting index block
        */
        if (
            startingIndexBlock == 0 &&
            (totalSupply() == MAX_TOTAL_KITTIES ||
                block.timestamp >= REVEAL_TIMESTAMP)
        ) {
            startingIndexBlock = block.number;
        }
    }
/**
 *  @dev Adopt new kitty cats
 */