// Composition root: builds the dependency graph from the bottom up and hands // it to the wxWidgets UI layer. This is the only place where concrete adapter // types are mentioned - everything downstream depends on interfaces. #include "ccm/domain/MagicCard.hpp" #include "ccm/domain/PokemonCard.hpp" #include "ccm/games/magic/MagicGameModule.hpp" #include "ccm/games/pokemon/PokemonGameModule.hpp" #include "ccm/infra/CprHttpClient.hpp" #include "ccm/infra/JsonCollectionRepository.hpp" #include "ccm/infra/JsonSetRepository.hpp" #include "ccm/infra/LocalImageStore.hpp" #include "ccm/infra/StdFileSystem.hpp" #include "ccm/services/CardPreviewService.hpp" #include "ccm/services/CollectionService.hpp" #include "ccm/services/ConfigService.hpp" #include "ccm/services/ImageService.hpp" #include "ccm/services/SetService.hpp" #include "ccm/ui/AppContext.hpp" #include "ccm/ui/MagicGameView.hpp" #include "ccm/ui/MainFrame.hpp" #include "ccm/ui/PokemonGameView.hpp" #include #include #include #include #include #include #include #include #include namespace { // All Game::X -> directory string mappings live in one place. Eliminates the // need for the repositories to know about concrete game module classes. std::string dirNameForGame(ccm::Game g) { switch (g) { case ccm::Game::Magic: return "magic"; case ccm::Game::Pokemon: return "pokemon"; } return "magic"; } } // namespace class CcmApp : public wxApp { public: bool OnInit() override { // wxImage knows about PNG/JPEG once these handlers are registered. wxImage::AddHandler(new wxPNGHandler); wxImage::AddHandler(new wxJPEGHandler); // --- locate config next to exe, data in user home ---------------- const std::filesystem::path exeDir = std::filesystem::path(wxStandardPaths::Get().GetExecutablePath().ToStdString()) .parent_path(); const auto configPath = exeDir / "config.json"; const auto defaultDataDir = std::filesystem::path(wxGetHomeDir().ToStdString()) / "ccm3-data"; // --- build the dependency graph ----------------------------------- fs_ = std::make_unique(); config_ = std::make_unique(*fs_, configPath, defaultDataDir); if (auto init = config_->initialize(); !init) { wxMessageBox("Failed to load configuration: " + init.error(), "Startup error", wxOK | wxICON_ERROR); return false; } http_ = std::make_unique(); magicMod_ = std::make_unique(*http_); pokeMod_ = std::make_unique(*http_); magicRepo_ = std::make_unique>( *fs_, *config_, &dirNameForGame); pokeRepo_ = std::make_unique>( *fs_, *config_, &dirNameForGame); setRepo_ = std::make_unique(*fs_, *config_, &dirNameForGame); imgStore_ = std::make_unique(*fs_, *config_, &dirNameForGame); imgSvc_ = std::make_unique(*imgStore_); magicCollSvc_ = std::make_unique>( *magicRepo_, *imgStore_); pokeCollSvc_ = std::make_unique>( *pokeRepo_, *imgStore_); setSvc_ = std::make_unique(*setRepo_); setSvc_->registerModule(magicMod_.get()); setSvc_->registerModule(pokeMod_.get()); previewSvc_ = std::make_unique(*http_); previewSvc_->registerModule(*magicMod_); previewSvc_->registerModule(*pokeMod_); // Per-game UI bundles. Order here is the order shown in the Game menu. magicView_ = std::make_unique( *config_, *magicCollSvc_, *setSvc_, *imgSvc_, *previewSvc_, *magicMod_); pokeView_ = std::make_unique( *config_, *pokeCollSvc_, *setSvc_, *imgSvc_, *previewSvc_, *pokeMod_); ctx_ = std::make_unique(ccm::ui::AppContext{ *config_, *setSvc_, *imgSvc_, *previewSvc_, *magicMod_, *pokeMod_, { magicView_.get(), pokeView_.get() }, }); auto* frame = new ccm::ui::MainFrame(*ctx_); #ifdef __WXMSW__ frame->SetIcon(wxICON(ccm_main_icon)); #endif frame->Show(true); return true; } private: // Order matters - destruction is reverse, so put services that depend on // others *after* their deps in the member list. Game views are torn down // first so their panels release any references to the typed services. std::unique_ptr fs_; std::unique_ptr config_; std::unique_ptr http_; std::unique_ptr magicMod_; std::unique_ptr pokeMod_; std::unique_ptr> magicRepo_; std::unique_ptr> pokeRepo_; std::unique_ptr setRepo_; std::unique_ptr imgStore_; std::unique_ptr imgSvc_; std::unique_ptr> magicCollSvc_; std::unique_ptr> pokeCollSvc_; std::unique_ptr setSvc_; std::unique_ptr previewSvc_; std::unique_ptr magicView_; std::unique_ptr pokeView_; std::unique_ptr ctx_; }; wxIMPLEMENT_APP(CcmApp);