contract TroyNFT is ERC721, ERC721URIStorage, ERC721Enumerable, Governance {
    
    mapping(address => bool) public _minters;
    
    string private baseURI = "https://nft-api.troyswap.io/token/";
    
    constructor() ERC721("TROY-NFT", "TROY-NFT") {
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    
    function _baseURI() internal view virtual override(ERC721) returns (string memory)
    {
        return baseURI;
    }

    /**
     * @dev Function to mint tokens.
     * @param to The address that will receive the minted token.
     * @param tokenId The token id to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(address to, uint256 tokenId) external returns (bool) {
        require(_minters[msg.sender], "!minter");
        _mint(to, tokenId);
        _setTokenURI(tokenId, DegoUtil.uintToString(tokenId));
        return true;
    }
    
    function setBaseURI(string memory baseURI_) external onlyGovernance() {
        baseURI = baseURI_;
    }
    
    function contractURI() public view returns (string memory) {
        return string(abi.encodePacked(_baseURI(), "0"));
    }
    
    /**
     * @dev Function to safely mint tokens.
     * @param to The address that will receive the minted token.
     * @param tokenId The token id to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function safeMint(address to, uint256 tokenId) public returns (bool) {
        require(_minters[msg.sender], "!minter");
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, DegoUtil.uintToString(tokenId));
        return true;
    }

    /**
     * @dev Function to safely mint tokens.
     * @param to The address that will receive the minted token.
     * @param tokenId The token id to mint.
     * @param _data bytes data to send along with a safe transfer check.
     * @return A boolean that indicates if the operation was successful.
     */
    function safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public returns (bool) {
        require(_minters[msg.sender], "!minter");
        _safeMint(to, tokenId, _data);
        _setTokenURI(tokenId, DegoUtil.uintToString(tokenId));
        return true;
    }

    function addMinter(address minter) public onlyGovernance {
        _minters[minter] = true;
    }

    function removeMinter(address minter) public onlyGovernance {
        _minters[minter] = false;
    }

    /**
     * @dev Burns a specific ERC721 token.
     * @param tokenId uint256 id of the ERC721 token to be burned.
     */
    function burn(uint256 tokenId) external {
        //solhint-disable-next-line max-line-length
        require(_minters[msg.sender], "!minter");
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "caller is not owner nor approved"
        );
        _burn(tokenId);
    }

    /**
     * @dev Gets the list of token IDs of the requested owner.
     * @param owner address owning the tokens
     * @return uint256[] List of token IDs owned by the requested address
     */
    function tokensOfOwner(address owner) public view returns (uint256[] memory) {
        uint256 balance = balanceOf(owner);
        uint256[] memory tokens = new uint256[](balance);

        for (uint i = 0; i < balance; i++) {
           tokens[i]= tokenOfOwnerByIndex(owner, i);
        }

         return tokens;
    }
}
function tokensOfOwner(address owner) public view returns (uint256[] memory) {
    uint256 balance = balanceOf(owner);
    uint256[] memory tokens = new uint256[](balance);

    for (uint i = 0; i < balance; i++) {
       tokens[i]= tokenOfOwnerByIndex(owner, i);
    }

     return tokens;
}

/**
 * @dev Gets the list of token IDs of the requested owner.
 * @param owner address owning the tokens
 * @return uint256[] List of token IDs owned by the requested address
 */