contract PeacefulToadz is ERC721, Ownable {

    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdCounter;

  //constants
    uint public holdersaleCost = 0.066 ether;
    uint public presaleCost = 0.066 ether;
    uint public publicsaleCost = 0.066 ether;
    uint public maxSupply = 8888;
    uint private holderMaxMintAmount = 3;
    uint private publicMaxMintAmount = 25;
    uint private reservedOwnerMint=100;
    
  //variable s
    bool public paused = false;
    bool public baseURILocked = false;
    bool public publicsale = false;
    bool public holdersale = false;
    bool public presale = false;
    address public proxyRegistryAddress;
    bytes32 private merkleRoot;
    string public contractURL;
    string public baseURI;
    string public baseExtension = ".json";

    address public token1address;
    address public token2address;
    address public coldwallet;
  
  //Arrays
    mapping(address => uint) publicSaleMinted; 
    mapping(address => uint) holderSaleRemaining; 
    mapping(address => bool) holderSaleUsed; 
    mapping(address => uint) whitelistRemaining; 
    mapping(address => bool) whitelistUsed; 

  //Actions
    event BaseTokenURIChanged(string baseURI);
    event ContractURIChanged(string contractURL);

    constructor(string memory _name, string memory _symbol,string memory _initBaseURI,string memory _initcontractURI,address _initProxyAddress,bytes32 _merkleRoot,address _token1address, address _token2address,address _coldwallet) ERC721(_name, _symbol) {   
        setcontractURI(_initcontractURI);
        setBaseURI(_initBaseURI);
        setMerkleRoot(_merkleRoot);
        _tokenIdCounter.increment();
        token1address=_token1address;
        token2address=_token2address;
        proxyRegistryAddress=_initProxyAddress;
        coldwallet=_coldwallet;
        mint_function(reservedOwnerMint);
    }

    // internal
    function mint_function(uint _mintAmount) internal {
        require(!paused,"Contract is paused");
        uint current = _tokenIdCounter.current();
        require(current + _mintAmount <= maxSupply,"You're exceeding maximum tokens");

        for (uint i = 1; i <= _mintAmount; i++) {
          mintInternal();
        }
    }

    function mintInternal() internal {
        uint tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _mint(msg.sender, tokenId);
    }
  

  // public
    function totalSupply() public view returns (uint) {
        return _tokenIdCounter.current() - 1;
    }

    function holderSaleMint(uint _mintAmount) external payable{
        require(holdersale, "Holder minting is not active");
        require(msg.value >= holdersaleCost * _mintAmount,"You have unsufficient fund!");
        
        Groupies token1 = Groupies(token1address);
        Toadz token2 = Toadz(token2address);
        uint token1Balance = token1.balanceOf(msg.sender);
        uint token2Balance = token2.balanceOf(msg.sender);
        require(!(token1Balance==0 && token2Balance==0),"You Are Not Holder");

        if (!holderSaleUsed[msg.sender]){
            holderSaleUsed[msg.sender]=true;
            holderSaleRemaining[msg.sender]=holderMaxMintAmount;
        }
        require(holderSaleRemaining[msg.sender]-_mintAmount>=0,"You are exceed maximum mint amount");
        holderSaleRemaining[msg.sender] -= _mintAmount;
        mint_function(_mintAmount); 
    }

    function preSaleMint(uint _mintAmount, uint _MaxMintAmount,bool _earlyInvest, bytes32[] memory proof) external payable {
        require(presale, "Pre-sale minting is not active");
        require(_earlyInvest || msg.value >= presaleCost * _mintAmount,"You have unsufficient fund!");
        require( MerkleProof.verify(proof,merkleRoot, keccak256(abi.encodePacked(msg.sender,_MaxMintAmount,_earlyInvest))),"You're not in whitelist!");
        
        if (!whitelistUsed[msg.sender]){
            whitelistUsed[msg.sender]=true;
            whitelistRemaining[msg.sender]=_MaxMintAmount;
        }
        require(whitelistRemaining[msg.sender]-_mintAmount>=0,"You're exceeding registered amount");
        whitelistRemaining[msg.sender]-=_mintAmount;
        mint_function(_mintAmount);
    }
    
    function publicSaleMint(uint _mintAmount) external payable {
        require(publicsale, "Public sale not started yet");
        require(_mintAmount <= 5,"You can only 5 mint per TX");
        require(publicMaxMintAmount >= publicSaleMinted[msg.sender]+_mintAmount,"You are exceed maximum mint amount");
        require(msg.value >= publicsaleCost * _mintAmount,"You have unsufficient fund!");
        
        publicSaleMinted[msg.sender] += _mintAmount;
        mint_function(_mintAmount);
    }
    
    function tokenURI(uint tokenId) public view virtual override returns (string memory)
    {
        require(  _exists(tokenId),"ERC721Metadata: URI query for nonexistent token" );
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, Strings.toString(tokenId), baseExtension)) : "";
    }
    
    function contractURI() public view returns (string memory) {
      return contractURL;
    }
    
    //ADMIN FUNCTIONS
    function lockContract() public onlyOwner(){
      baseURILocked=true;
    }
    
    function ownershiptransfer(address _newOwner) public onlyOwner(){
        transferOwnership(_newOwner);
    }
    
    function setMerkleRoot(bytes32 _merkleRoot) public onlyOwner() {
        merkleRoot = _merkleRoot;
    }
    
    function setBaseURI(string memory _newBaseURI) public onlyOwner() {
        require(!baseURILocked,"We Can't change baseURI");
        baseURI = _newBaseURI;
        emit BaseTokenURIChanged(_newBaseURI);
    }
    
    function setBaseExtension(string memory _newBaseExtension) public onlyOwner() {
        baseExtension = _newBaseExtension;
    }
    
    function setcontractURI(string memory _newcontractURI) public onlyOwner() {
        contractURL = _newcontractURI;
        emit ContractURIChanged(_newcontractURI);
    }

  

    function toogleSection(uint section) public onlyOwner() {
        require (section<4,"Please select section");
        if (section==0) paused=!paused;
        if (section==1) presale=!presale;
        if (section==2) publicsale=!publicsale;
        if (section==3) holdersale=!holdersale;
    }


    function changePrice(uint section, uint price) public onlyOwner() {
        require (section<4,"Please select section");
        if (section==0){
            publicsaleCost=price;
            presaleCost=price;
            holdersaleCost=price;
        }
        if (section==1) publicsaleCost=price;
        if (section==2) presaleCost=price;
        if (section==3) holdersaleCost=price;
        
    }
    function setAddress(uint section, address _address) public onlyOwner() {
        require (section<4,"Please select section");
        if (section==0){
            token1address=_address;
        }
        if (section==1){
            token2address=_address;
        }
        if (section==2){
            proxyRegistryAddress=_address;
        }
        if (section==3){
            coldwallet=_address;
        }
    
    }
      
    function withdraw() public payable {
        require(coldwallet != address(0), "coldwallet is the zero address");
        require(payable(coldwallet).send(address(this).balance));
    }

    function tokensOfOwner(address _owner, uint startId, uint endId) external view returns(uint[] memory ) {
        uint tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            return new uint[](0);
        } else {
            uint[] memory result = new uint[](tokenCount);
            uint index = 0;

            for (uint tokenId = startId; tokenId < endId; tokenId++) {
                if (index == tokenCount) break;

                if (ownerOf(tokenId) == _owner) {
                    result[index] = tokenId;
                    index++;
                }
            }
            return result;
        }
    }
    // Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings.
    function isApprovedForAll(address owner, address operator) override public view returns (bool)
    {
        // Whitelist OpenSea proxy contract for easy trading.
        ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress);
        if (address(proxyRegistry.proxies(owner)) == operator) {
            return true;
        }
        return super.isApprovedForAll(owner, operator);
    }
}
function mint_function(uint _mintAmount) internal {
    require(!paused,"Contract is paused");
    uint current = _tokenIdCounter.current();
    require(current + _mintAmount <= maxSupply,"You're exceeding maximum tokens");

    for (uint i = 1; i <= _mintAmount; i++) {
      mintInternal();
    }
}

// internal