contract WrappedKevins is ERC721, ERC721Holder, Ownable {
    using Strings for uint256;

    address public constant pixelmonContract =
        0x32973908FaeE0Bf825A343000fE412ebE56F802A;

    string public constant kevinImage =
        "https://tbi2twxegkxhiaetkxigpewdmtxulieagrqawc3tya3zmylk.arweave.net/mFGp2uQyrnQAk1XQZ5LDZO9FoIA0YAsLc8A_3l_mFqE";

    // fallback incase something happens to Arweave (very unlikely)
    string public _kevinBaseUri;

    mapping(uint256 => bool) public kevinTokenIds;

    uint256 private _currSupply;

    constructor() ERC721("Wrapped Kevins", "WKEVINS") {}

    // to add more kevin Ids when new ones hatch
    function addKevinTokenIds(uint256[] memory tokenIds) external onlyOwner {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            kevinTokenIds[tokenIds[i]] = true;
        }
    }

    // receive pixelmon, mint wrapped pixelmon back
    function wrap(uint256 tokenId) external {
        require(kevinTokenIds[tokenId], "not a valid kevin token Id");
        unchecked {
            _currSupply++;
        }
        IPixelmon(pixelmonContract).safeTransferFrom(
            msg.sender,
            address(this),
            tokenId
        );
        _mint(msg.sender, tokenId);
    }

    // receive wrapped pixelmon, burn, then return pixelmon back
    function unwrap(uint256 tokenId) external {
        require(kevinTokenIds[tokenId], "not a valid kevin token Id");
        require(
            _isApprovedOrOwner(msg.sender, tokenId),
            "not approved or owner"
        );
        unchecked {
            _currSupply--;
        }
        _burn(tokenId);
        IPixelmon(pixelmonContract).safeTransferFrom(
            address(this),
            msg.sender,
            tokenId
        );
    }

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

        string memory baseURI = _kevinBaseUri;
        if (bytes(baseURI).length == 0) {
            return
                string(
                    abi.encodePacked(
                        "data:application/json;base64,",
                        Base64.encode(
                            bytes(
                                string(
                                    abi.encodePacked(
                                        '{"name": "Kevin #',
                                        tokenId.toString(),
                                        '", "description": "Wrapped Kevins are original Kevins preserved on a new contract. The underlying metadata is fully on-chain and no royalties are collected. Kevin has been set free.", "image":"',
                                        kevinImage,
                                        '","attributes": [{"trait_type": "Species", "value": "Kevin"}]}'
                                    )
                                )
                            )
                        )
                    )
                );
        }

        return string(abi.encodePacked(baseURI, tokenId.toString()));
    }

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

    // fallback - most likely wont be used
    function setKevinBaseUri(string memory uri) external onlyOwner {
        _kevinBaseUri = uri;
    }
}
function unwrap(uint256 tokenId) external {
    require(kevinTokenIds[tokenId], "not a valid kevin token Id");
    require(
        _isApprovedOrOwner(msg.sender, tokenId),
        "not approved or owner"
    );
    unchecked {
        _currSupply--;
    }
    _burn(tokenId);
    IPixelmon(pixelmonContract).safeTransferFrom(
        address(this),
        msg.sender,
        tokenId
    );
}

// receive wrapped pixelmon, burn, then return pixelmon back