text: // Copyright 2017 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef LIB_APP_DRIVER_CPP_MODULE_DRIVER_H_
  #define LIB_APP_DRIVER_CPP_MODULE_DRIVER_H_
  
  #include <fuchsia/modular/cpp/fidl.h>
  #include <fuchsia/sys/cpp/fidl.h>
  #include <fuchsia/ui/app/cpp/fidl.h>
  #include <lib/async/cpp/task.h>
  #include <lib/async/default.h>
  #include <lib/fidl/cpp/binding.h>
  #include <lib/fidl/cpp/interface_request.h>
  #include <lib/lifecycle/cpp/lifecycle_impl.h>
  #include <lib/sys/cpp/component_context.h>
  #include <src/lib/fxl/logging.h>
  
  #include <memory>
  
  namespace modular {
  
  // This interface is passed to the |Impl| object that ModuleDriver initializes.
  class ModuleHost {
   public:
    virtual sys::ComponentContext* component_context() = 0;
    virtual fuchsia::modular::ModuleContext* module_context() = 0;
  };
  
  // ModuleDriver provides a way to write modules and participate in application
  // lifecycle. The |Impl| class supplied to ModuleDriver is instantiated when the
  // Module and ViewProvider services have both been requested by the framework.
  //
  // Usage:
  //   The |Impl| class must implement:
  //
  //      // A constructor with the following signature:
  //      Constructor(
  //           modular::ModuleHost* module_host,
  //           fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>
  //           view_provider_request);
  //
  //       // Called by ModuleDriver. Call |done| once shutdown sequence is
  //       // complete, at which point |this| will be deleted.
  //       void Terminate(fit::function<void()> done);
  //
  // Example:
  //
  // class HelloWorldModule {
  //  public:
  //   HelloWorldModule(
  //      modular::ModuleHost* module_host,
  //      fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>
  //      view_provider_request) {}
  //
  //   // Called by ModuleDriver.
  //   void Terminate(fit::function<void()> done) { done(); }
  // };
  //
  // int main(int argc, const char** argv) {
  //   async::Loop loop(&kAsyncLoopConfigAttachToThread);
  //   auto context = sys::ComponentContext::Create();
  //   modular::ModuleDriver<HelloWorldApp> driver(context.get(),
  //                                               [&loop] { loop.Quit(); });
  //   loop.Run();
  //   return 0;
  // }
  template <typename Impl>
  class ModuleDriver : LifecycleImpl::Delegate, ModuleHost {
   public:
    ModuleDriver(sys::ComponentContext* const context,
                 fit::function<void()> on_terminated)
        : context_(context),
          lifecycle_impl_(context->outgoing(), this),
          on_terminated_(std::move(on_terminated)) {
      context_->svc()->Connect(module_context_.NewRequest());
  
      context_->outgoing()->AddPublicService<fuchsia::ui::app::ViewProvider>(
          [this](fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider> request) {
            impl_ = std::make_unique<Impl>(static_cast<ModuleHost*>(this),
                                           std::move(request));
          });
    }
  
   private:
    // |ModuleHost|
    sys::ComponentContext* component_context() override { return context_; }
  
    // |ModuleHost|
    fuchsia::modular::ModuleContext* module_context() override {
      FXL_DCHECK(module_context_);
      return module_context_.get();
    }
  
    // |LifecycleImpl::Delegate|
    void Terminate() override {
      // It's possible that we process the |fuchsia::modular::Lifecycle.Terminate|
      // message before the |Module.Initialize| message, even when both messages
      // are ready to be processed at the same time. In this case, because |impl_|
      // hasn't been instantiated yet, we cannot delegate the
      // |fuchsia::modular::Lifecycle.Terminate| message.
      if (impl_) {
        impl_->Terminate([this] {
          // Cf. AppDriver::Terminate().
          async::PostTask(async_get_default_dispatcher(), [this] {
            impl_.reset();
            on_terminated_();
          });
        });
      } else {
        on_terminated_();
      }
    }
  
    sys::ComponentContext* const context_;
    LifecycleImpl lifecycle_impl_;
    fit::function<void()> on_terminated_;
    fuchsia::modular::ModuleContextPtr module_context_;
  
    std::unique_ptr<Impl> impl_;
  
    FXL_DISALLOW_COPY_AND_ASSIGN(ModuleDriver);
  };
  
  }  // namespace modular
  
  #endif  // LIB_APP_DRIVER_CPP_MODULE_DRIVER_H_
  