contract ABCNFT is ERC721, IERC2981, Ownable {
    using SafeMath for uint256;
    uint256 public tokenCounter;
    using Strings for uint256;
    uint256 public price = 0.05 ether;
    uint256 public renamePrice = 0.008 ether;
    uint256 public maxSupply = 10000;
    uint256 public maxMintAmount = 10;
    bool public paused = false;
    
    // Optional mapping for token URIs
    mapping (uint256 => string) private _tokenURIs;
    // Base URI
    string private _baseURIextended;

    address public royalties;
    uint256 public royaltyPercent = 500; //500/10000*100=5%

    uint256[750] public groupSold;
    
    // uint256 private _totalSupply;   // total count to be minted
    
    constructor () ERC721 ("AussieBoganClub", "ABC"){
        tokenCounter = 0;
    }

    function withdraw() public onlyOwner() {
        uint256 balance = address(this).balance;
        require(balance > 0, "No ether left to withdraw");
        (bool success, ) = (msg.sender).call{value: balance}("");
        require(success, "Transfer failed.");
    }

    function totalSupply() public view returns (uint256){
        return tokenCounter;
    }

    function setBaseURI(string memory baseURI_) external onlyOwner() {
        _baseURIextended = baseURI_;
    }
    
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }
    
    function _baseURI() internal view virtual override returns (string memory) {
        return _baseURIextended;
    }
    
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();
        
        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, tokenId.toString()));
    }
    
    function setPrice(uint _price) external onlyOwner {
        price = _price;
    }

    function setRenamePrice(uint _price) external onlyOwner{
        renamePrice = _price;
    }

    function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
        maxMintAmount = _newmaxMintAmount;
    }

    function pause(bool _state) public onlyOwner {
        paused = _state;
    }

    function mint(address _to, string[] memory tokenUris, uint256 _groupId) public payable {
        require(!paused, "mint paused");        
        require(_groupId<groupSold.length, "GroupId is too big");
        require(tokenUris.length>0, "Amount can't be 0");
        require(tokenUris.length <= maxMintAmount, "can't bigger than maxMintAmount");
        if(msg.sender != owner()){
            require(msg.value >= price.mul(tokenUris.length), "mintingFee isn't enough");
        }
        // uint256 newItemId = tokenCounter;
        uint256 amount = tokenUris.length;
        for(uint256 i = 0; i < amount; ++i){
            _safeMint(_to, tokenCounter);
            _setTokenURI(tokenCounter, tokenUris[i]);
            tokenCounter = tokenCounter.add(1);
            groupSold[_groupId] = groupSold[_groupId].add(1);
        }
    }

    function setTokenUri(address tokenOwner, uint tokenId, string memory tokenUri) public payable{
        require(msg.value >= renamePrice, "rename fee isn't enough");
        require(ownerOf(tokenId) == tokenOwner, "caller isn't owner");
        _setTokenURI(tokenId, tokenUri);
    }

    function getGroupSoldByIndex(uint256 index) public view returns(uint256){
        require(index<groupSold.length, "Index should be less than length of group.");
        return groupSold[index];
    }

    // ERC165

  function supportsInterface(bytes4 interfaceId) public view override(ERC721, IERC165) returns (bool) {
    return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
  }

  // IERC2981

  function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view override returns (address, uint256 royaltyAmount) {
    _tokenId; // silence solc warning
    royaltyAmount = (_salePrice / 10000) * royaltyPercent;
    return (royalties, royaltyAmount);
  }

  function setRoyaltyPercentage(uint256 _percent) public onlyOwner{
      royaltyPercent = _percent;
  }

  function setRoyalties(address _royalties) public onlyOwner {
      royalties = _royalties;
  }

  function tokensOfOwner(address _owner)
        external
        view
        returns (uint256[] memory)
    {
        uint256 balance = balanceOf(_owner);
        uint256[] memory tokensId = new uint256[](balance);
        uint256 index = 0;
        for (uint256 tokenId = 0; tokenId < tokenCounter; tokenId++) {
            if(_owner == ownerOf(tokenId)){
                tokensId[index] = tokenId;
                index+=1;
            }
        }

        return tokensId;
    }
}
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view override returns (address, uint256 royaltyAmount) {
  _tokenId; // silence solc warning
  royaltyAmount = (_salePrice / 10000) * royaltyPercent;
  return (royalties, royaltyAmount);
}

// IERC2981