#pragma once // ICardPreviewSource - resolves the preview image URL for a single card via // the active game's external API. Implementations stay HTTP-bound, no UI deps. // // Modeled after per-game `getImage` helpers in // `src/components/{magic,pokemon}/Selected*Panel.tsx`. The resulting URL is // then fetched as raw image bytes by `CardPreviewService` and decoded by the // UI layer (wxImage in our wx adapter). #include "ccm/util/Result.hpp" #include #include #include namespace ccm { struct AutoDetectedPrint { std::string setNo; std::string rarity; // Optional fields used by games that resolve set/name/language during // auto-detect (e.g. Yu-Gi-Oh! Bandai). Existing games leave them empty. std::string name; std::string setId; std::string setName; std::string language; // Language enum spelling when known ("Japanese" / "English") }; // Classified error returned by ICardPreviewSource::fetchImageUrl. The kind // drives caching policy in CardPreviewService: // // NotFound -- the upstream answered cleanly that the card has no image // (or no matching record at all). Safe to remember: the // answer will not change until the user edits the card // record itself, which automatically invalidates the cache // key. Negative-cached so subsequent selections show the // fallback card-back instantly without another HTTP call. // // Transient -- the upstream did not answer cleanly (HTTP / network / // timeout failure, malformed response, parse error). The // record may well have an image; we just couldn't see it // this time. NOT cached, so the next selection retries. // // The `message` is opaque to the service and is forwarded to the UI as // the existing free-form `Result::error()` string. struct PreviewLookupError { enum class Kind { NotFound, Transient }; Kind kind{Kind::Transient}; std::string message; }; class ICardPreviewSource { public: virtual ~ICardPreviewSource() = default; // Resolve the preview image URL for a single card. `setNo` is optional // (empty string is fine); some game APIs (e.g. Pokemon TCG) can use it as // a more precise lookup key, others (Magic/Scryfall) ignore it. // // Errors carry a classification (`PreviewLookupError::Kind`) so // CardPreviewService can decide whether to remember the miss // (`NotFound`) or retry on the next call (`Transient`). See the doc // comment on PreviewLookupError above for the exact contract. virtual Result fetchImageUrl(std::string_view name, std::string_view setId, std::string_view setNo) = 0; // Opt-in switch for per-game print metadata detection. [[nodiscard]] virtual bool supportsAutoDetectPrint() const noexcept { return false; } // Optional metadata lookup used by game-specific edit dialogs. The default // implementation returns an explicit "unsupported" error so games without // print metadata APIs do not need to override it. virtual Result detectFirstPrint(std::string_view /*name*/, std::string_view /*setId*/) { return Result::err("Auto-detect not supported by this game."); } // Optional listing of every distinct `(set_code, rarity)` print returned by // the upstream for an exact card name inside the chosen display set. virtual Result> detectPrintVariants(std::string_view /*name*/, std::string_view /*setId*/) { return Result>::err( "Print variant listing not supported by this game."); } // Optional lookup by collector / Bandai number (fills name + set + rarity). virtual Result detectBySetNo(std::string_view /*setNo*/) { return Result::err( "Detect-by-number not supported by this game."); } virtual Result> detectVariantsBySetNo(std::string_view /*setNo*/) { return Result>::err( "Detect-by-number variants not supported by this game."); } }; } // namespace ccm