contract GoldenNFT is ERC721Enumerable, Ownable, ReentrancyGuard {
    using Strings for uint256;

    // presale stuff
    IERC721Enumerable private constant PRESALE_TOKEN = IERC721Enumerable(0xF6Ea0642611b3FEc3d3c2d54ce67f29098728C1c);
    mapping(uint256 => uint256) private _presaleTokenUsage;
    bool public _isPresaleMintingDone = false;

    // sale stuff
    uint256 public _cap = 5555;
    uint256 public _maxBuyAmount = 50;
    uint256 public _pricePerToken = 5 * 10**16;
    bool private _isSalePaused = true;
    address payable public _wallet = payable(0xA6C1788Bdc494150f78c8Fc214a2EDcE09CE14c7);

    // reveal stuff
    bool public _idShiftDone = false;
    uint256 public _idShift = 0;


    string private _tokenBaseURI = 'ipfs://QmNqMPBx9WUAXWNASX3fnPVwdSRgz1eToBicTPxmUFPetB';

    constructor() ERC721("Golden NFT", "GOLD") {
        _mintToken(_msgSender());
    }


    // minting
    function mint(uint256 mintAmount) public payable nonReentrant {
        require(!isSalePaused(), "sale is paused");
        require(mintAmount > 0, "buy at least 1 NFT");
        require(mintAmount <= _maxBuyAmount, "its not allowed to buy this much");
        require(mintAmount <= nftsAvailable(), "not enough NFTs available");
        require((_pricePerToken * mintAmount) == msg.value, "exact value in ETH needed");

        if(!_isPresaleMintingDone) {
            require(mintAmount <= presaleTokenMaxBuyAmount(_msgSender()), "you dont have enough presale tokens");
            uint256 reduce_amount = mintAmount;
            for(uint256 i = 0; i < presaleTokenAmount(_msgSender()); i++) {
                uint256 token_id = PRESALE_TOKEN.tokenOfOwnerByIndex(_msgSender(), i);
                uint256 mints_left_on_token = 10 - _presaleTokenUsage[token_id];
                if(reduce_amount <= mints_left_on_token) {
                    _presaleTokenUsage[token_id] = _presaleTokenUsage[token_id]+reduce_amount;
                    reduce_amount = 0;
                } else {
                    _presaleTokenUsage[token_id] = 10;
                    reduce_amount = reduce_amount - mints_left_on_token;
                }
            }
        }

        for (uint256 i = 0; i < mintAmount; i++) {
            _mintToken(_msgSender());
        }
        payable(_wallet).transfer(msg.value);
    }




    function presaleTokenAmount(address account) public view returns(uint256) {
        return PRESALE_TOKEN.balanceOf(account);
    }

    function presaleTokenMaxBuyAmount(address account) public view returns(uint256) {
        uint256 remaining_buy_amount = 0;
        for(uint256 i = 0; i < presaleTokenAmount(account); i++) {
            uint256 token_id = PRESALE_TOKEN.tokenOfOwnerByIndex(account, i);
            remaining_buy_amount+= 10 - _presaleTokenUsage[token_id];
        }
        return remaining_buy_amount;
    }


    function nftsAvailable() public view returns(uint256) {
        return _cap - totalSupply();
    }

    function cap() public view returns(uint256) {
        return _cap;
    }

    function isSalePaused() public view returns(bool) {
        return _isSalePaused;
    }








    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        if(!_idShiftDone) {
            return baseURI;
        }
        uint256 shiftedTokenId = (tokenId+_idShift) % _cap;
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, shiftedTokenId.toString(), string('.json'))) : "";
    }

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




    function pauseSale() external onlyOwner {
        _isSalePaused = true;
    }

    function resumeSale() external onlyOwner {
        _isSalePaused = false;
    }

    function switchToRegularSale() public onlyOwner {
        _isPresaleMintingDone = true;
        _maxBuyAmount = 10;
    }

    function setWallet(address wallet) public onlyOwner {
        _wallet = payable(wallet);
    }

    function setTokenBaseURI(string memory newBaseURI) public onlyOwner {
        _tokenBaseURI = newBaseURI;
    }

    function reveil(string memory newBaseURI) public onlyOwner {
        require(!_idShiftDone, "ID shift is done");
        _tokenBaseURI = newBaseURI;
        _idShiftDone = true;
        _idShift = block.timestamp % _cap;
    }

    function adminMintTo(uint256 mintAmount, address account) public onlyOwner {
        require(mintAmount > 0, "ERC721CappedSale: buy at least 1 NFT");
        require(mintAmount <= nftsAvailable(), "ERC721CappedSale: not enough NFTs available");
        for (uint256 i = 0; i < mintAmount; i++) {
            _mintToken(account);
        }
    }

    function emergencyWithdraw() public onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }


    function _mintToken(address destinationAddress) internal {
        uint256 newTokenID = totalSupply();
        require(!_exists(newTokenID), "ERC721CappedSale: Token already exist.");
        _safeMint(destinationAddress, newTokenID);
    }

}
function mint(uint256 mintAmount) public payable nonReentrant {
    require(!isSalePaused(), "sale is paused");
    require(mintAmount > 0, "buy at least 1 NFT");
    require(mintAmount <= _maxBuyAmount, "its not allowed to buy this much");
    require(mintAmount <= nftsAvailable(), "not enough NFTs available");
    require((_pricePerToken * mintAmount) == msg.value, "exact value in ETH needed");

    if(!_isPresaleMintingDone) {
        require(mintAmount <= presaleTokenMaxBuyAmount(_msgSender()), "you dont have enough presale tokens");
        uint256 reduce_amount = mintAmount;
        for(uint256 i = 0; i < presaleTokenAmount(_msgSender()); i++) {
            uint256 token_id = PRESALE_TOKEN.tokenOfOwnerByIndex(_msgSender(), i);
            uint256 mints_left_on_token = 10 - _presaleTokenUsage[token_id];
            if(reduce_amount <= mints_left_on_token) {
                _presaleTokenUsage[token_id] = _presaleTokenUsage[token_id]+reduce_amount;
                reduce_amount = 0;
            } else {
                _presaleTokenUsage[token_id] = 10;
                reduce_amount = reduce_amount - mints_left_on_token;
            }
        }
    }

    for (uint256 i = 0; i < mintAmount; i++) {
        _mintToken(_msgSender());
    }
    payable(_wallet).transfer(msg.value);
}

// minting