contract NonFungiBurls is ERC721Enumerable, Ownable {

    mapping (uint256 => bool) redemptions;
    mapping (address => bool) hasMinted;

    uint256 saleStartTime = 1618592400000;
    uint256 redemptionStartTime = 1618592400000;
    uint256 maxSupply = 20;
    string currentContractURI = "ipfs://QmcEoiot3ksB7wdczFDRrhxtfWtNBnt1cr6sVvpsPYrT7S";
    string currentPermawebURI;
    string baseURI;
    using Strings for uint256;

    constructor(string memory initialBaseURI) ERC721("NonFungiBurls", "NFB") {
        baseURI = initialBaseURI;
    }

    event Redemption(address redeemer, uint256 tokenId);

    /*
        WRITE FUNCTIONS
    */


    //USER FUNCTIONS

    function mintBurl(uint256 tokenId) public returns(uint256){
        //Require the tokenId to not exist
        require(_exists(tokenId) == false, "Token ID already minted!");

        //Require the total supply to be under the maxsupply
        require(totalSupply() < maxSupply, "Maximum tokens already minted!");

        //Require tokenId to be a valid number, 1-20
        require(tokenId > 0 && tokenId < 21, "Token ID must be from 1-20");

        //Require sale to be started
        require(block.timestamp >= saleStartTime, "Sale has not started");

        //Only let each address mint one.
        require(hasMinted[msg.sender] != true, "One eth address can only mint one burl!");


        _mint(msg.sender, tokenId);

        hasMinted[msg.sender] = true;
        redemptions[tokenId] = false;
        return tokenId;
    }

    function redeemBurl(uint256 tokenId) public returns(string memory){
        require(redemptions[tokenId] == false, "Token has already been redeemed!");
        require(ownerOf(tokenId) == msg.sender, "Only token owner can redeem!");
        require(block.timestamp >= redemptionStartTime, "Redemption period has not started!");

        redemptions[tokenId] = true;
        emit Redemption(msg.sender, tokenId);
        return ("Redeemed!");
    }

    //OWNER FUNCTIONS

    function changeSaleStartTime(uint256 newSaleStartTime) public returns (uint256) {
        require(msg.sender == owner(),"Only owner can change sale start time!!");
        saleStartTime = newSaleStartTime;
        return saleStartTime;
    }

    function changeRedemptionStartTime(uint256 newRedemptionStartTime)   public  returns (uint256) {
        require(msg.sender == owner(),"Only owner can change redemption start time!!");
        redemptionStartTime = newRedemptionStartTime;
        return redemptionStartTime;
    }

    function changeContractURI(string memory newContractURI) public  returns (string memory) {
        require(msg.sender == owner(), "Only owner can change contract URI!");
        currentContractURI = newContractURI;
        return (currentContractURI);
    }

    function changePermawebURI(string memory newPermawebURI) public returns (string memory) {
        require(msg.sender == owner(), "Only owner can change permaweb URI!");
        currentPermawebURI = newPermawebURI;
        return (currentPermawebURI);
    }

    /*
        READ FUNCTIONS
    */

    function getRedeemedStatus(uint256 tokenId) public view returns(bool) {
        return redemptions[tokenId];
    }

    function contractURI() public view returns (string memory) {
        return (currentContractURI);
    }

    function permawebURI() public view returns (string memory) {
        return (currentPermawebURI);
    }

    function permawebURIForToken(uint256 tokenId) public view virtual returns(string memory) {
        require(_exists(tokenId), "ERC721Metadata: Permaweb URI query for nonexistent token");

        return string(abi.encodePacked(currentPermawebURI,tokenId.toString(),".html"));
    }

    function tokenURI(uint256 tokenId) public view virtual override returns(string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        if(redemptions[tokenId]){
            return string(abi.encodePacked(baseURI, "redeemed/", tokenId.toString()));
        } else {
            return string(abi.encodePacked(baseURI, "unredeemed/", tokenId.toString()));
        }
    }
}
function getRedeemedStatus(uint256 tokenId) public view returns(bool) {
    return redemptions[tokenId];
}

/*
 READ FUNCTIONS
 */