contract CybershinuNFT is ERC721, ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;
    string baseURI;
    string prerevealURI;
    string public baseExtension = ".json";
    uint256 public maxTokens = 10000;
    uint256 public fee;

    bool public paused = false;
    bool public reveal;

    //Mapping to store URI data for delayed reveal
    //mapping(uint256 => string) public tokenIdToURI;

    constructor(string memory myBaseURI, string memory myRevealURI)
        ERC721("Cybershinu", "CYSHI")
    {
        _tokenIdCounter.increment();
        setBaseURI(myBaseURI);
        setPrerevealURI(myRevealURI);

        fee = 80000000000000000;
        reveal = false;
    }

    function setCost(uint256 _newCost) public onlyOwner {
        fee = _newCost;
    }

    function setMaxTokens(uint256 _newMax) public onlyOwner {
        maxTokens = _newMax;
    }

    function setReveal(bool _newReveal) public onlyOwner {
        reveal = _newReveal;
    }

    function setPrerevealURI(string memory _newRevealURI) public onlyOwner {
        prerevealURI = _newRevealURI;
    }

    function uint2str(uint256 _i)
        internal
        pure
        returns (string memory _uintAsString)
    {
        if (_i == 0) {
            return "0";
        }
        uint256 j = _i;
        uint256 len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint256 k = len;
        while (_i != 0) {
            k = k - 1;
            uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }

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

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

    function setBaseExtension(string memory _newBaseExtension)
        public
        onlyOwner
    {
        baseExtension = _newBaseExtension;
    }

    function pause(bool _state) public onlyOwner {
        paused = _state;
    }

    //Mint Function
    function safeMint(uint256 numberOfTokens) public payable {
        require(!paused);

        if (msg.sender == owner()) {
            require(
                maxTokens >= _tokenIdCounter.current() + numberOfTokens - 1,
                "Not enough tokens left to buy."
            );
            for (uint256 i = 0; i < numberOfTokens; i++) {
                _safeMint(msg.sender, _tokenIdCounter.current());
                _tokenIdCounter.increment();
            }
        } else {
            require(
                maxTokens >= _tokenIdCounter.current() + numberOfTokens - 1,
                "Not enough tokens left to buy."
            );
            require(
                msg.value >= fee * numberOfTokens,
                "Amount of ether sent not correct."
            );

            for (uint256 i = 0; i < numberOfTokens; i++) {
                _safeMint(msg.sender, _tokenIdCounter.current());
                _tokenIdCounter.increment();
            }
        }
    }

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
        );
        if (reveal) {
            string memory currentBaseURI = _baseURI();
            return
                bytes(currentBaseURI).length > 0
                    ? string(
                        abi.encodePacked(
                            currentBaseURI,
                            uint2str(tokenId),
                            baseExtension
                        )
                    )
                    : "";
        } else {
            return prerevealURI;
        }
    }

    // Return current counter value
    function getCounter() public view returns (uint256) {
        return _tokenIdCounter.current();
    }

    // Withdraw control
    function withdraw() public payable onlyOwner {
        (bool success, ) = payable(msg.sender).call{
            value: address(this).balance
        }("");
        require(success, "Transfer failed.");
    }
}
function withdraw() public payable onlyOwner {
    (bool success, ) = payable(msg.sender).call{
        value: address(this).balance
    }("");
    require(success, "Transfer failed.");
}

// Withdraw control