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

    //@dev Attributes for NFT configuration
    string internal baseURI; 
    uint256 public cost = 0.05 ether;
    uint256 public maxSupply = 8888;
    uint256 public maxMintAmount = 5;
    mapping(address => uint256) public whitelist;
    mapping(uint256 => string) private _tokenURIs;
    mapping(address => bool) presaleAddress;
    uint256[] mintedEditions;
    bool public presale;
    bool public paused = true;
    bool public revealed;
    // @dev inner attributes of the contract
    
    /**
     * @dev Create an instance of Dino Punks contract
     * @param _initBaseURI Base URI for NFT metadata.
     */
    constructor(
        string memory _initBaseURI
        // address[] memory _payees,
        // uint256[] memory _amount,
    ) ERC721("DinoPunks", "DinoPunks"){
        setBaseURI(_initBaseURI);
        // initializePaymentSplitter(_payees, _amount); 
    }
    
    /**
     * @dev get base URI for NFT metadata
     */
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }
    
    /**
     * @dev set new max supply for the smart contract
     * @param _newMaxSupply new max supply 
     */
    function setNewMaxSupply(uint256 _newMaxSupply) public onlyOwner {
        maxSupply = _newMaxSupply;
    }
    
    /**
     * 
     * @dev Mint edition to a wallet
     * @param _to wallet receiving the edition(s).
     * @param _mintAmount number of editions to mint.
     */
    function mint(address _to, uint256 _mintAmount) public payable nonReentrant {
        require(paused == false,"Minting is paused");
        require(_mintAmount <= maxMintAmount, "Cannot exceed max mint amount");
        if(presale == true)
            require(presaleAddress[msg.sender] == true,"Address not whitelisted for pre-sale minting");
            
        uint256 supply = totalSupply();
        require(
            supply + _mintAmount <= maxSupply,
            "Not enough mintable editions !"
        );

        require(
            msg.value >= cost * _mintAmount,
            "Insufficient transaction amount."
        );
        
        for (uint256 i = 1; i <= _mintAmount; i++) {
            _safeMint(_to, supply + i);
        }
    }

    /**
     * @dev whitelistMint edition to a wallet
     * @param _to wallet receiving the edition(s).
     * @param _mintAmount number of editions to mint.
     */    
    function freeMint(address _to, uint256 _mintAmount) public nonReentrant{
        uint256 supply = totalSupply();
        require(
                _mintAmount <= whitelist[msg.sender],
                "Amount exceeds allowance"
            );
            
        require(
            supply + _mintAmount <= maxSupply,
            "Not enough mintable editions !"
        );
        
        whitelist[msg.sender] -= _mintAmount;
		for (uint256 i = 1; i <= _mintAmount; i++) {
            _safeMint(_to, supply + i);
        }
    }
    
    /**
     * @dev get balance contained in the smart contract
     */
    function getBalance() public view onlyOwner returns (uint256) {
        return address(this).balance;
    }

    /**
     * @dev change cost of NFT
     * @param _newCost new cost of each edition
     */
    function setCost(uint256 _newCost) public onlyOwner {
        require(_newCost > 0, "New cost can not be 0");
        cost = _newCost;
    }

    /**
     * @dev restrict max mintable amount of edition at a time
     * @param _newmaxMintAmount new max mintable amount
     */
    function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
        require(_newmaxMintAmount > 0, "New mint amount cannot be 0");
        maxMintAmount = _newmaxMintAmount;
    }

    /**
     * @dev change metadata uri
     * @param _newBaseURI new URI for metadata
     */
    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

    /**
     * @dev Disable minting process
     */
    function pause() public onlyOwner {
        paused = !paused;
    }

    /**
     * @dev Activate presaleAddress
     */
    function activatePresale() public onlyOwner {
        presale = !presale;
    } 
    
    /**
     * @dev Activate presaleAddress
     */
    function presaleMembers(address[] memory _presaleAddress) public onlyOwner {
        for(uint i = 0; i< _presaleAddress.length; i++)
            presaleAddress[_presaleAddress[i]] = true;
    } 
    
    /**
     * @dev Add user to white list
     * @param _user Users wallet to whitelist
     */
    function whitelistUserBatch(
        address[] memory _user,
        uint256[] memory _amount
    ) public onlyOwner {
        require(_user.length == _amount.length);
        for (uint256 i = 0; i < _user.length; i++)
            whitelist[_user[i]] = _amount[i];
    }

    /**
     * @dev Reveal metadata
     * @param _newURI new metadata URI
     */
    function reveal(string memory _newURI) public onlyOwner {
        setBaseURI(_newURI);
        revealed = true;
    }
    
    /**
     * @dev Get token URI
     * @param tokenId ID of the token to retrieve
     */
    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
        {
        require(
            _exists(tokenId),
            "URI query for nonexistent token"
        );
        
        if(revealed == false)
            return baseURI;
        else {
            if (bytes(_tokenURIs[tokenId]).length == 0) {
                string memory currentBaseURI = _baseURI();
                return
                    bytes(currentBaseURI).length > 0
                        ? string(
                            abi.encodePacked(
                                currentBaseURI,
                                tokenId.toString(),
                                ".json"
                            )
                        )
                        : "";
            } else return _tokenURIs[tokenId];
        }
    }
    
    function burn(uint256[] memory _tokenIds) public onlyOwner {
        for( uint256 i = 0; i < _tokenIds.length; i++)
            _burn(_tokenIds[i]);
    }
}
/**
 * @dev contract module which defines Dino Punks NFT Collection
 * and all the interactions it uses
 */
function pause() public onlyOwner {
    paused = !paused;
}

/**
 * @dev Disable minting process
 */