contract CryptoSphere is ERC721Enumerable, Ownable {
    using Strings for uint256;

    string public baseURI;
    string public extension = ".json";
    uint256 public cost = 0.05 ether;
    uint256 public maxSupply = 4000;
    uint public maxMintAmount = 20;
    bool public paused = false;
    bool public preSale = false;
    mapping(address => bool) public whitelist;

    constructor(string memory _name, string memory _symbol, string memory _initBaseURI) ERC721 (_name, _symbol){
        setBaseURI(_initBaseURI);
    }

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

//    function presaleMint(uint _mintAmount) public payable {
//        require(preSale, "Pre-sale is not currently running");
//        require(whitelist[msg.sender] == true, "Address not whitelisted for presale");
//        mint(_mintAmount);
//    }
//
//    // public
//    function mint(uint _mintAmount) public payable {
//        uint256 supply = totalSupply();
//        require(!paused, "Minting currently on hold");
//        require(_mintAmount > 0, "Amount to mint can not be 0");
//        require(_mintAmount <= maxMintAmount, "Maximum mint amount exceeded");
//        require(supply + _mintAmount<= maxSupply, "Purchase would exceed max supply of NFT");
//
//        if (msg.sender != owner()) {
//            require(msg.value >= cost * _mintAmount, "Amount sent less than the cost of minting NFT(s)");
//        }
//
//        for (uint256 i = 1; i <= _mintAmount; i++) {
//            //require(supply + i < maxSupply + 1, "MaxSupply Reached");
//            _safeMint(msg.sender, supply + i);
//        }
//    }


    function mint(uint _mintAmount) public payable {
        require(!paused , "Minting currently on hold");
        require(!preSale, "Minting is in pre-sale mode");
        mintX(_mintAmount);
    }

    function presaleMint(uint _mintAmount) public payable {
        require(preSale, "Pre-sale is not currently running");
        require(whitelist[msg.sender] == true, "Address not whitelisted for presale");
        mintX(_mintAmount);
    }

    function mintX(uint _mintAmount) private {
        uint256 supply = totalSupply();
        require(_mintAmount > 0, "Amount to mint can not be 0");
        require(_mintAmount <= maxMintAmount, "Maximum mint amount exceeded");
        require(supply + _mintAmount<= maxSupply, "Purchase would exceed max supply of NFT");

        if (msg.sender != owner()) {
            require(msg.value >= cost * _mintAmount, "Amount sent less than the cost of minting NFT(s)");
        }

        for (uint256 i = 1; i <= _mintAmount; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    function walletOfOwner(address _owner) public view returns (uint256[] memory){
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory tokenIds = new uint256[](ownerTokenCount);
        for (uint256 i; i < ownerTokenCount; i++) {
            tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokenIds;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory){

        require(_exists(tokenId), "ERC721Metadata: tokenURI queried for nonexistent token");
        string memory currentBaseURI = _baseURI();
        return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), extension)) : "";
    }

    function whitelistAddresses(address[] memory addresses) public onlyOwner() {
        for (uint256 i; i < addresses.length; i++) {
            address addressI = addresses[i];
            whitelist[addressI] = true;
        }
    }

    function whitelistAddressesRemove(address[] memory addresses) public onlyOwner() {
        for (uint256 i; i < addresses.length; i++) {
            address addressI = addresses[i];
            whitelist[addressI] = false;
        }
    }

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

    function togglePause() public onlyOwner() {
        paused = !paused;
    }

    function togglePreSale() public onlyOwner() {
        preSale = !preSale;
    }

    function withdraw() public payable onlyOwner() {
        require(payable(msg.sender).send(address(this).balance));
    }

}
function mint(uint _mintAmount) public payable {
    require(!paused , "Minting currently on hold");
    require(!preSale, "Minting is in pre-sale mode");
    mintX(_mintAmount);
}

//    function presaleMint(uint _mintAmount) public payable {
//        require(preSale, "Pre-sale is not currently running");
//        require(whitelist[msg.sender] == true, "Address not whitelisted for presale");
//        mint(_mintAmount);
//    }
//
//    // public
//    function mint(uint _mintAmount) public payable {
//        uint256 supply = totalSupply();
//        require(!paused, "Minting currently on hold");
//        require(_mintAmount > 0, "Amount to mint can not be 0");
//        require(_mintAmount <= maxMintAmount, "Maximum mint amount exceeded");
//        require(supply + _mintAmount<= maxSupply, "Purchase would exceed max supply of NFT");
//
//        if (msg.sender != owner()) {
//            require(msg.value >= cost * _mintAmount, "Amount sent less than the cost of minting NFT(s)");
//        }
//
//        for (uint256 i = 1; i <= _mintAmount; i++) {
//            //require(supply + i < maxSupply + 1, "MaxSupply Reached");
//            _safeMint(msg.sender, supply + i);
//        }
//    }