contract Apes is ERC721Enumerable, Ownable {
    using Strings for uint256;
    
    mapping (uint256 => address) private _ogminter;
    
    uint256 public constant MAX_APES = 4500;
    uint256 public constant MAX_PURCHASES_PER_TRANSACTION = 20;
    uint256 public constant TOTAL_RESERVED_APES = 25;

    bool public isSaleActive = false;
    uint256 public price = 60000000000000000;
    string internal _baseTokenURI;
    
    constructor(string memory baseURI) ERC721("Passive Apes", "Apes")  {
        updateBaseURI(baseURI);

    }

    modifier saleHasNotEnded{
        require(totalSupply() < MAX_APES, "Sale has ended.");
        _;
    }

    function mintApes(uint256 numTokens) public payable saleHasNotEnded {        
        if(msg.sender != owner()){
            require(isSaleActive, "Sale is not active.");
        }
        require(SafeMath.add(totalSupply(), numTokens) <= MAX_APES, "Exceeds total Ape supply.");
        require(totalSupply() < MAX_APES, "Sale has ended.");
        require(numTokens <= MAX_PURCHASES_PER_TRANSACTION, "Exceeds max purchases.");
        require(msg.value >= calculatePrice(numTokens), "Insufficient funds.");

        for(uint256 i = 0; i < numTokens; i++) {
            _safeMint(msg.sender, totalSupply());
        
        }
    }
    
    function calculatePrice(uint256 numTokens) internal view returns (uint256) {
        return price * numTokens;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, "/", tokenId.toString(), ".json")) : "";
    }
    
    function getBaseURI() public onlyOwner view returns (string memory) {
        return _baseURI();
    }
    /**
    *  Update tokenURI through baseURI. 
    *  This function will be called when setting the initial tokenURI and revealing.
    */ 
    function updateBaseURI(string memory baseURI) public onlyOwner {
        _baseTokenURI = baseURI;
    }
    
    function updatePrice(uint newPrice) public onlyOwner {
        price = newPrice;
    }
    function setUnlockCost(uint256 cost) public onlyOwner {
        UnlockCost = cost;
    }
    function setSaleActive(bool isActive) public onlyOwner {
        isSaleActive = isActive;
    }
    function withdrawFee() public payable onlyOwner {
        require(payable(_msgSender()).send(address(this).balance));
    }
    function withdrawAll() public payable onlyOwner {
        require(payable(_msgSender()).send(address(this).balance));
    }
}
function updateBaseURI(string memory baseURI) public onlyOwner {
    _baseTokenURI = baseURI;
}

/**
 *  Update tokenURI through baseURI. 
 *  This function will be called when setting the initial tokenURI and revealing.
 */