contract HSPP is ERC721Enumerable, Ownable  {

    using Strings for uint256;
    address private MixContract;
    string _baseTokenURI;
    string public baseExtension = ".json";
    uint256 public _maxperTX = 3;                    // Max per mint
    uint256 public _maxperWalletWaitList = 1;        // Waitlist max mint per wallet
    uint256 public _maxperWalletMint = 3;            // Mint max Mint per wallet
    uint256 public _limit = 100;                     // Supply of token 
    uint256 public _price = 0.2 ether;               // Price 
    uint256 public waitlist_count  =  0;             // Start count for waitlist
    address public fundWallet;                       // Fund Wallet
    bool public _presaleWLPaused = true;             // Contract is paused 
    bool public _PublicPaused = true;                // Contract is paused 
    mapping (address => uint256) public perWallet;   // Mapping per wallet
    mapping (address => bool) public waitlist;       // Mapping per waitlist
    SauceToken public TokenContract;                 // Token functions
    address payable public payments;                 // Payable 
    bool public isBurningActive = false;             // Paused for Burning Function


    // Constructor for the token 
    constructor(string memory initbaseURI, address _fundWallet) ERC721("HS Pepper Party Genesis", "HSPP")  {
        require(_fundWallet != address(0), "Zero address error");
        setBaseURI(initbaseURI);
        fundWallet = _fundWallet;
    }

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

    // Token Public only owner functions 
    function setSauceToken (SauceToken _TokenContract) public onlyOwner{
    // require(_TokenContract != address(0), "Contract address can't be zero address");
        TokenContract = _TokenContract;
    }

    // Public view token 
    function getSauceToken(address __address) public view returns(uint256) {
        require(__address != address(0), "Contract address can't be zero address");
        return TokenContract.balanceOf(__address);
    }

    // Get Allowance  public view 
    function getSauceAllowance(address __address) public view returns(uint256) {
        require(__address != address(0), "Contract address can't be zero address");
        return TokenContract.allowance(__address, address(this));
    }
  
 
    // Mint Function public payable 
    function MINT(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( !_PublicPaused,                                     "Sale paused" );
        require( num <= _maxperTX,                                   "You are exceeding limit of per transaction TLM" );
        require( perWallet[msg.sender] + num <= _maxperWalletMint,   "You are exceeding limit of per wallet TLM" );
        require( num > 0);
        require( supply + num <= _limit,                             "Exceeds maximum HSPP supply" );
        require( msg.value >= _price * num,                          "Ether sent is not correct" );

        for(uint256 i = 1 ; i <= num; i++){
            _safeMint( msg.sender, supply + i );
        }
        perWallet[msg.sender] += num;

    }

    // Mint Function external onlyOwner 
    function mintForAddress(address _to, uint256 _amount) external onlyOwner() {
        require( _to !=  address(0), "Zero address error");
        require( _amount <= _limit, "Exceeds Limit  supply");
        uint256 supply = totalSupply();
        for(uint256 i; i < _amount; i++){
            _safeMint( _to, supply + i );
        }
        _limit -= _amount;  
        }

    // Internal memory with public view for the wallet of owner 
    function walletOfOwner(address _owner) public view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for(uint256 i; i < tokenCount; i++){
            tokensId[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokensId;
    }

    // Waitlist Mint public payable only for the waitlisted people 
    function WAITLIST_MINT(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( !_presaleWLPaused,                                       "Sale paused" ); 
        require( waitlist[msg.sender] == true,                            "Only WAITLIST can mint" );
        require( perWallet[msg.sender] + num <= _maxperWalletWaitList,    "You are exceeding limit of per wallet HSPP" );
        require( supply + num <= _limit,                                  "Exceeds maximum HSPP supply" );
        require( msg.value >= _price * num,                               "Ether sent is not correct" );

        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
        perWallet[msg.sender] += num;
    }

    // Bulk Waitlist wallets 
    function bulk_waitlist(address[] memory addresses) public onlyOwner() {
        for(uint i=0; i < addresses.length; i++){
            address addr = addresses[i];
            if(waitlist[addr] != true && addr != address(0)){
                waitlist[addr] = true;
                waitlist_count++;
            }
        }
    }

    // Function remove Waitlist wallet / only owner 
    function remove_waitlist(address _address) public onlyOwner() {
        require(_address != address(0), "Zero address error");
        waitlist[_address] = false;
        waitlist_count--;
    }
    
  
    // Just in case Eth does some crazy stuff ( Change price ) only owner 
    function setPrice(uint256 _newPrice) public onlyOwner() {
        _price = _newPrice;
    }

    // Set FundWallet
    function setFundWallet(address _fundWallet) public onlyOwner() {
        require(_fundWallet != address(0), "Zero address error");
        fundWallet = _fundWallet;
    }

    // Max per wallet
    function setMaxPerWallet(uint256 limit) public onlyOwner {
        _maxperTX = limit;
    }

    // Token URI
   function tokenURI(uint256 tokenId)
    public
    view
    virtual
    override
    returns (string memory)
    {
    require(
      _exists(tokenId),
      "ERC721Metadata: URI query for nonexistent token"
    );
    string memory currentBaseTokenURI = _baseURI();
    return bytes(currentBaseTokenURI).length > 0
        ? string(abi.encodePacked(currentBaseTokenURI, tokenId.toString(), baseExtension))
        : "";
    }

   // Burn Function used for the HSPP Kitchen 
    function burnForMint(uint256 _tokenId) public {
        require(isBurningActive, "Burning not active");
        require(ownerOf(_tokenId) == msg.sender);
        //Burn token
        _transfer(
            msg.sender,
            0x000000000000000000000000000000000000dEaD,
            _tokenId
        );
    }
    // Pause Burn 
    function toggleBurningActive() public onlyOwner {
        isBurningActive = !isBurningActive;
    }

   // Price Show Public 
    function getPrice() public view returns (uint256){
        return _price;
    }

   // Public Set URI if Needed 
    function setBaseURI(string memory baseURI) public onlyOwner {
        _baseTokenURI = baseURI;
    }

   // Pause Contract public only owner
    function PublicPaused() public onlyOwner {
        _PublicPaused = !_PublicPaused;
    }

   // Pause Contract presaleWL only owner
    function presaleWLPaused() public onlyOwner {
        _presaleWLPaused = !_presaleWLPaused;
    }
   
   // Withdraw Fund 
     function withdrawAll() public payable onlyOwner {
        require(payable(fundWallet).send(address(this).balance));
    }
}
function presaleWLPaused() public onlyOwner {
    _presaleWLPaused = !_presaleWLPaused;
}

// Pause Contract presaleWL only owner