text: #include "pch.h"
  #include "common.h"
  #include "options.h"
  #include "platformplugin.h"
  #include "graphicsplugin.h"
  #include "openxr_program.h"
  
  namespace {
  
  void ShowHelp() {
      // TODO: Improve/update when things are more settled.
      Log::Write(Log::Level::Info,
                 "HelloXr --graphics|-g <Graphics API> [--formfactor|-ff <Form factor>] [--viewconfig|-vc <View config>] "
                 "[--blendmode|-bm <Blend mode>] [--space|-s <Space>] [--verbose|-v]");
      Log::Write(Log::Level::Info, "Graphics APIs:            D3D11, D3D12, OpenGLES, OpenGL, Vulkan");
      Log::Write(Log::Level::Info, "Form factors:             Hmd, Handheld");
      Log::Write(Log::Level::Info, "View configurations:      Mono, Stereo");
      Log::Write(Log::Level::Info, "Environment blend modes:  Opaque, Additive, AlphaBlend");
      Log::Write(Log::Level::Info, "Spaces:                   View, Local, Stage");
  }
  
  bool UpdateOptionsFromCommandLine(Options& options, int argc, char* argv[]) {
      int i = 1;  // Index 0 is the program name and is skipped.
  
      auto getNextArg = [&] {
          if (i >= argc) {
              throw std::invalid_argument("Argument parameter missing");
          }
  
          return std::string(argv[i++]);
      };
  
      while (i < argc) {
          const std::string arg = getNextArg();
          if (EqualsIgnoreCase(arg, "--graphics") || EqualsIgnoreCase(arg, "-g")) {
              options.GraphicsPlugin = getNextArg();
          } else if (EqualsIgnoreCase(arg, "--formfactor") || EqualsIgnoreCase(arg, "-ff")) {
              options.FormFactor = getNextArg();
          } else if (EqualsIgnoreCase(arg, "--viewconfig") || EqualsIgnoreCase(arg, "-vc")) {
              options.ViewConfiguration = getNextArg();
          } else if (EqualsIgnoreCase(arg, "--blendmode") || EqualsIgnoreCase(arg, "-bm")) {
              options.EnvironmentBlendMode = getNextArg();
          } else if (EqualsIgnoreCase(arg, "--space") || EqualsIgnoreCase(arg, "-s")) {
              options.AppSpace = getNextArg();
          } else if (EqualsIgnoreCase(arg, "--verbose") || EqualsIgnoreCase(arg, "-v")) {
              Log::SetLevel(Log::Level::Verbose);
          } else if (EqualsIgnoreCase(arg, "--help") || EqualsIgnoreCase(arg, "-h")) {
              ShowHelp();
              return false;
          } else {
              throw std::invalid_argument(Fmt("Unknown argument: %s", arg.c_str()));
          }
      }
  
      // Check for required parameters.
      if (options.GraphicsPlugin.empty()) {
          Log::Write(Log::Level::Error, "GraphicsPlugin parameter is required");
          ShowHelp();
          return false;
      }
  
      return true;
  }
  }  // namespace
  
  int main(int argc, char* argv[]) {
      try {
          // Parse command-line arguments into Options.
          std::shared_ptr<Options> options = std::make_shared<Options>();
          if (!UpdateOptionsFromCommandLine(*options, argc, argv)) {
              return 1;
          }
  
          // Spawn a thread to wait for a keypress
          static bool quitKeyPressed = false;
          auto exitPollingThread = std::thread{[] {
              Log::Write(Log::Level::Info, "Press any key to shutdown...");
              getchar();
              quitKeyPressed = true;
          }};
          exitPollingThread.detach();
  
          bool requestRestart = false;
          do {
              // Create platform-specific implementation.
              std::shared_ptr<IPlatformPlugin> platformPlugin = CreatePlatformPlugin(options);
  
              // Create graphics API implementation.
              std::shared_ptr<IGraphicsPlugin> graphicsPlugin = CreateGraphicsPlugin(options, platformPlugin);
  
              // Initialize the OpenXR program.
              std::shared_ptr<IOpenXrProgram> program = CreateOpenXrProgram(options, platformPlugin, graphicsPlugin);
  
              program->CreateInstance();
              program->InitializeSystem();
              program->InitializeSession();
              program->CreateSwapchains();
  
              while (!quitKeyPressed) {
                  bool exitRenderLoop = false;
                  program->PollEvents(&exitRenderLoop, &requestRestart);
                  if (exitRenderLoop) {
                      break;
                  }
  
                  if (program->IsSessionRunning()) {
                      program->PollActions();
                      program->RenderFrame();
                  } else {
                      // Throttle loop since xrWaitFrame won't be called.
                      std::this_thread::sleep_for(std::chrono::milliseconds(250));
                  }
              }
  
          } while (!quitKeyPressed && requestRestart);
  
          return 0;
      } catch (const std::exception& ex) {
          Log::Write(Log::Level::Error, ex.what());
          return 1;
      } catch (...) {
          Log::Write(Log::Level::Error, "Unknown Error");
          return 1;
      }
  }
  