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

    string public PROVENANCE = "";

    uint256 public startingIndexBlock;

    uint256 public startingIndex;

    uint256 public mintPrice = 30000000000000000; //0.03 ETH

    uint256 public maxPurchase = 20;

    uint256 public MAX_NFTS;

    uint256 public START_MINT;

    uint256 public MINT_KEEP_SEC;

    constructor(
        string memory name,
        string memory symbol,
        uint256 maxNftSupply,
        string memory baseURI,
        uint256 mintStart,
        uint256 mintKeepMinutes
    ) ERC721(name, symbol) {
        _setBaseURI(baseURI);
        MAX_NFTS = maxNftSupply;
        START_MINT = mintStart;
        MINT_KEEP_SEC = mintKeepMinutes * 60;
    }

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

    /**
     * Set some NFTs aside
     */
    function reserveNFTs() public onlyOwner {
        uint256 supply = totalSupply();
        uint256 i;
        for (i = 0; i < 30; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    /**
     * DM Gargamel in Discord that you're standing right behind him.
     */
    function setStartMint(uint256 mintStart, uint256 mintKeepMinutes)
        public
        onlyOwner
    {
        START_MINT = mintStart;
        MINT_KEEP_SEC = mintKeepMinutes * 60;
    }

    /*
     * Set mint price
     */
    function setMintPrice(uint256 price) public onlyOwner {
        mintPrice = price;
    }

    /*
     * Set max purchase
     */
    function setMaxPurchase(uint256 newMaxPurchase) public onlyOwner {
        maxPurchase = newMaxPurchase;
    }

    /*
     * Set provenance once it's calculated
     */
    function setProvenanceHash(string memory provenanceHash) public onlyOwner {
        PROVENANCE = provenanceHash;
    }

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

    /**
     * Mints NFT
     */
    function mint(uint256 numberOfTokens) public payable {
        require(block.timestamp > START_MINT, "Has not yet started to mint");
        require(
            MINT_KEEP_SEC == 0 ||
                block.timestamp < (START_MINT + MINT_KEEP_SEC),
            "Minting has stopped"
        );
        require(
            numberOfTokens <= maxPurchase,
            "Can only mint limited tokens at a time"
        );
        require(
            totalSupply().add(numberOfTokens) <= MAX_NFTS,
            "Purchase would exceed max supply of NFTs"
        );
        require(
            mintPrice.mul(numberOfTokens) <= msg.value,
            "Ether value sent is not correct"
        );

        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_NFTS) {
                _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_NFTS) {
            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 = uint256(blockhash(startingIndexBlock)) % MAX_NFTS;
        // 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_NFTS;
        }
        // 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 BoredApeYachtClub contract
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function mint(uint256 numberOfTokens) public payable {
    require(block.timestamp > START_MINT, "Has not yet started to mint");
    require(
        MINT_KEEP_SEC == 0 ||
            block.timestamp < (START_MINT + MINT_KEEP_SEC),
        "Minting has stopped"
    );
    require(
        numberOfTokens <= maxPurchase,
        "Can only mint limited tokens at a time"
    );
    require(
        totalSupply().add(numberOfTokens) <= MAX_NFTS,
        "Purchase would exceed max supply of NFTs"
    );
    require(
        mintPrice.mul(numberOfTokens) <= msg.value,
        "Ether value sent is not correct"
    );

    for (uint256 i = 0; i < numberOfTokens; i++) {
        uint256 mintIndex = totalSupply();
        if (totalSupply() < MAX_NFTS) {
            _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_NFTS) {
        startingIndexBlock = block.number;
    }
}

/**
 * Mints NFT
 */