contract LoungeLoversSC is 
    ERC721Enumerable, 
    AccessControlEnumerable,
    Ownable 
{
    using Strings for uint256;
    using Counters for Counters.Counter;
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    Counters.Counter private _tokenIds;
    
    string public baseURI;
    string public baseExtension = "";
    string public hiddenURI;

    bool public isSaleActive = false;
    bool public isTokenRevealed = false;
    bool public isWhitelistActive = true;

    uint256 public TOTAL_TOKENS = 10000;
    uint256 public MAX_TOKENS_PER_PUBLIC_SALE = 20;
    uint256 public MAX_TOKENS_PER_WHITELIST_SALE = 4;

    string private baseTokenURI;
    uint256 public price_public = 0.12 ether;
    uint256 public price_whitelist = 0.09 ether;
    mapping(address => bool) public whitelisted;


     /**
     * Grants `DEFAULT_ADMIN_ROLE`
     * account that deploys the contract.
     *
     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
     * See {ERC721-tokenURI}.
     */
    constructor(
        string memory name,
        string memory symbol,
        string memory _initURIHidden,
        string memory _initBaseURI
    ) ERC721(name, symbol) {

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        setBaseURI(_initBaseURI);
        setHiddenURI(_initURIHidden);
    }

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

    // open functions
    function safeMint(uint256 _amount) public payable {
        require(isSaleActive, "Sale is currently not active");
        require(TOTAL_TOKENS > _amount + _tokenIds.current() + 1, "Not enough tokens left to buy.");

        if (isWhitelistActive){
            require(_amount > 0 && _amount < MAX_TOKENS_PER_WHITELIST_SALE + 1, "Amount of tokens exceeds amount of tokens you can purchase in a single purchase.");
        } 
        else {
            require(_amount > 0 && _amount < MAX_TOKENS_PER_PUBLIC_SALE + 1, "Amount of tokens exceeds amount of tokens you can purchase in a single purchase.");
        }

        if (msg.sender != owner()){
            if (whitelisted[msg.sender] != true){
                require(!isWhitelistActive, "Sorry you are not on the whitelist");
            }
            
            if (isWhitelistActive){
                require(msg.value >= price_whitelist * _amount, "Amount of ether sent not correct.");
            }
            else{
                require(msg.value >= price_public * _amount, "Amount of ether sent not correct.");
            }
        }

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

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory){
        require(_exists(tokenId),"ERC721Metadata: URI query for nonexistent token");
    
        if(isTokenRevealed == false) {
        return hiddenURI;
        }

        string memory currentBaseURI = _baseURI();
        return bytes(currentBaseURI).length > 0
            ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
            : "";
    }

    
    // owner functions
    function showMetadata (bool _state) public onlyOwner {
        isTokenRevealed = _state;
    }

    function startSale (bool _state) public onlyOwner {
        isSaleActive = _state;
    }

    function whitelistSale (bool _state) public onlyOwner {
        isWhitelistActive = _state;
    }

    function setPricePublic (uint256 _price) public onlyOwner {
        price_public = _price;
    }

    function setPriceWhitelist (uint256 _price) public onlyOwner {
        price_whitelist = _price;
    }

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

    function setMaxPublicPuchaseTokens (uint256 _newMax) public onlyOwner {
        MAX_TOKENS_PER_PUBLIC_SALE = _newMax;
    }

    function setMaxWhitelistPuchaseTokens (uint256 _newMax) public onlyOwner {
        MAX_TOKENS_PER_WHITELIST_SALE = _newMax;
    }

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

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

    function whitelistUser(address[] memory _address) public onlyOwner {
        for (uint i = 0; i < _address.length; i++) {
                whitelisted[_address[i]] = true;
            }
    }
    
    function removeWhitelistUser(address _address) public onlyOwner {
        whitelisted[_address] = false;
    }

    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        require(balance > 0, "Balance is empty!");
        payable(msg.sender).transfer(balance);
    }

    function multiAirDropSafeMint(address[] memory _address, uint256[] memory _amount) public onlyOwner {
        require(TOTAL_TOKENS > _address.length + _tokenIds.current() + 1, "Not enough tokens left to buy.");

        for (uint i = 0; i < _address.length; i++){
            uint counting = 0;
            while (counting != _amount[i]){
                _safeMint(_address[i], _tokenIds.current());
                _tokenIds.increment();
                counting++;
            }
        }
    }

    // override functions
    function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721Enumerable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }
}
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721Enumerable) returns (bool) {
    return super.supportsInterface(interfaceId);
}

// override functions