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

    uint256 public constant maxSupply = 10000;
    uint256 public constant price = 3*10**16;
    uint256 public constant purchaseLimit = 50;
    uint256 internal constant reservable = 240;
    uint256 internal constant team = 40;
    string internal constant _name = "Crypto Pimps";
    string internal constant _symbol = "PIMPS";
    address payable immutable public payee;
    address internal immutable reservee = 0xd95740e361Fc851E1338A7E2E82255176417d4eD;

    string public contractURI;
    string public provenance;
    bool public saleIsActive = true;
    uint256 public saleStart = 1633046340;

    constructor (
        address payable _payee
        ) public ERC721(_name, _symbol) {
        payee = _payee;
    }

    /** 
     * emergency withdraw function, callable by anyone
     */
    function withdraw() public {
        payee.transfer(address(this).balance);
    }

    /**
     * reserve
     */
    function reserve() public onlyOwner {
        require(totalSupply() < reservable, "reserve would exceed reservable");
        uint supply = totalSupply();
        uint i;
        for (i = 0; i < 55; i++) {
            if (totalSupply() < reservable - team) {
                _safeMint(reservee, supply + i);
            } else if (totalSupply() < reservable){
                _safeMint(msg.sender, supply + i);
            }
        }
    }

    /**
     * set provenance if needed
    */
    function setProvenance(string memory _provenance) public onlyOwner {
        provenance = _provenance;
    }

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

    /*
    * set contractURI if needed
    */
    function setContractURI(string memory _contractURI) public onlyOwner {
        contractURI = (_contractURI);
    }


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

    /*
    * updates saleStart
    */
    function setSaleStart(uint256 _saleStart) public onlyOwner {
        saleStart = _saleStart;
    }

    /**
    * mint
    */
    function mint(uint numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint");
        require(block.timestamp >= saleStart, "Sale has not started yet, timestamp too low");
        require(numberOfTokens <= purchaseLimit, "Purchase would exceed purchase limit");
        require(totalSupply().add(numberOfTokens) <= maxSupply, "Purchase would exceed maxSupply");
        require(price.mul(numberOfTokens) <= msg.value, "Ether value sent is too low");
        
        payee.transfer(address(this).balance);        
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < maxSupply) {
                _safeMint(msg.sender, mintIndex);
            }
        }
    }

}
/**
 * @title NFT contract - forked from BAYC
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function setContractURI(string memory _contractURI) public onlyOwner {
    contractURI = (_contractURI);
}

/*
 * set contractURI if needed
 */