contract OnlyOtters is ERC721Burnable, ERC721Enumerable, Ownable {

    using Strings for uint256;
    using Address for address;

    IDSJ public DSJ;

    string _baseTokenURI;

    uint256 public maxMint = 10;
    uint256 public constant PRICE = 0.03 ether;
    

    bool public active = false;
    bool public redeemActive = false;
    uint public constant MAX_SUPPLY = 5000;
    uint public reserve = 10;

    mapping(uint => bool) public _isBlacklisted;

    constructor(string memory baseURI) ERC721("OnlyOtters", "OOS")  {
        setBaseURI(baseURI);
    }

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

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

    //Mints
    function mint(uint256 qty) external payable {
        uint256 mintIndex = totalSupply();

        require(active, "Sale is not active");
        require( qty < (maxMint + 1),"You can only mint a maximum of 10 Otters" );
        require( mintIndex + qty < MAX_SUPPLY, "Exceeds maximum supply" );
        require(msg.value >= PRICE * qty, "ETH sent is not correct");

        for(uint256 i; i < qty; i++){
          _safeMint(msg.sender, mintIndex + i );
        }
    }
    
    function reserveMint(uint256 reserveQty) public onlyOwner {        
        uint256 mintIndex = totalSupply();
        require( mintIndex + reserveQty < MAX_SUPPLY, "Exceeds maximum supply" );
        require(reserveQty > 0 && reserveQty <= reserve, "Not enough reserve left for team");
        for (uint i; i < reserveQty; i++) {
            _safeMint(msg.sender, mintIndex + i);
        }
        reserve = reserve - reserveQty;
    }
    
    function redeemMint(uint256 dsj1, uint256 dsj2) external payable{
        uint256 mintIndex = totalSupply();
        
        require(active, "Sale is not active");
        require(DSJ.ownerOf(dsj1) == msg.sender, "Must own first DSJ token");
        require(DSJ.ownerOf(dsj2) == msg.sender, "Must own second DSJ token");
        require(dsj1 != dsj2, "Must not be the same DSJ token");
        require(_isBlacklisted[dsj1] == false, "First DSJ Token already claimed");
        require(_isBlacklisted[dsj2] == false, "Second DSJ Token already claimed");
        require(mintIndex < MAX_SUPPLY, "Exceeds maximum supply" );

        _isBlacklisted[dsj1] = true;
        _isBlacklisted[dsj2] = true;

        _safeMint(msg.sender, mintIndex);
    }

    function burn(uint256 _tokenId) public virtual  override{
        uint256 mintIndex = totalSupply();
        require(mintIndex > MAX_SUPPLY,"all tokens needs to be minted");
        _burn(_tokenId);
    }

    function burnNFTs(uint256[] calldata _tokenIds) public onlyOwner{        
        for (uint i; i<_tokenIds.length; i++) {
        _burn(_tokenIds[i]);
        }
    }

    //Get
    function DSJcheck(uint256 _tokenId) public view returns (bool){
        return DSJ.ownerOf(_tokenId) == msg.sender;
    }
    
    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }
    
    function isActive() public view returns(bool) {
        return active;
    }

    //Setters
    function setDSJ(address IDSJAddress) external onlyOwner {
        DSJ = IDSJ(IDSJAddress);
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        _baseTokenURI = baseURI;
    }    

    function saleActive(bool val) public onlyOwner {
        active = val;
    }

    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
}
function DSJcheck(uint256 _tokenId) public view returns (bool){
    return DSJ.ownerOf(_tokenId) == msg.sender;
}

//Get