text: // Copyright 2018 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.
  
  #include <lib/async-loop/cpp/loop.h>
  #include <lib/async-loop/default.h>
  #include <lib/fidl/cpp/binding_set.h>
  #include <lib/fidl/cpp/interface_request.h>
  #include <lib/sys/cpp/component_context.h>
  #include <lib/syslog/cpp/macros.h>
  #include <lib/zx/channel.h>
  #include <zircon/types.h>
  
  #include <cstdlib>
  #include <string>
  
  #include <fidl/test/compatibility/cpp/fidl.h>
  
  #include "src/tests/fidl/compatibility/hlcpp_client_app.h"
  
  namespace fidl {
  namespace test {
  namespace compatibility {
  
  class EchoServerApp : public Echo {
   public:
    explicit EchoServerApp(async::Loop* loop)
        : loop_(loop), context_(sys::ComponentContext::CreateAndServeOutgoingDirectory()) {
      context_->outgoing()->AddPublicService(bindings_.GetHandler(this));
    }
  
    ~EchoServerApp() {}
  
    void EchoStruct(Struct value, std::string forward_to_server,
                    EchoStructCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoStruct(std::move(value), "", [this, &called_back, &callback](Struct resp) {
          called_back = true;
          callback(std::move(resp));
          loop_->Quit();
        });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        callback(std::move(value));
      }
    }
  
    void EchoStructWithError(Struct value, default_enum err, std::string forward_to_server,
                             RespondWith result_variant,
                             EchoStructWithErrorCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoStructWithError(
            std::move(value), std::move(err), "", result_variant,
            [this, &called_back, &callback](Echo_EchoStructWithError_Result result) {
              called_back = true;
              callback(std::move(result));
              loop_->Quit();
            });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        Echo_EchoStructWithError_Result result;
        if (result_variant == RespondWith::ERR) {
          result.set_err(err);
        } else {
          result.set_response(Echo_EchoStructWithError_Response(std::move(value)));
        }
        callback(std::move(result));
      }
    }
  
    void EchoStructNoRetVal(Struct value, std::string forward_to_server) override {
      if (!forward_to_server.empty()) {
        std::unique_ptr<EchoClientApp> app(new EchoClientApp);
        app->echo().set_error_handler([this, &forward_to_server](zx_status_t status) {
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app->Start(forward_to_server);
        app->echo().events().EchoEvent = [this](Struct resp) {
          this->HandleEchoEvent(std::move(resp));
        };
        app->echo()->EchoStructNoRetVal(std::move(value), "");
        client_apps_.push_back(std::move(app));
      } else {
        for (const auto& binding : bindings_.bindings()) {
          Struct to_send;
          value.Clone(&to_send);
          binding->events().EchoEvent(std::move(to_send));
        }
      }
    }
  
    void EchoArrays(ArraysStruct value, std::string forward_to_server,
                    EchoArraysCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoArrays(std::move(value), "",
                               [this, &called_back, &callback](ArraysStruct resp) {
                                 called_back = true;
                                 callback(std::move(resp));
                                 loop_->Quit();
                               });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        callback(std::move(value));
      }
    }
  
    void EchoArraysWithError(ArraysStruct value, default_enum err, std::string forward_to_server,
                             RespondWith result_variant,
                             EchoArraysWithErrorCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoArraysWithError(
            std::move(value), std::move(err), "", result_variant,
            [this, &called_back, &callback](Echo_EchoArraysWithError_Result result) {
              called_back = true;
              callback(std::move(result));
              loop_->Quit();
            });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        Echo_EchoArraysWithError_Result result;
        if (result_variant == RespondWith::ERR) {
          result.set_err(err);
        } else {
          result.set_response(Echo_EchoArraysWithError_Response(std::move(value)));
        }
        callback(std::move(result));
      }
    }
  
    void EchoVectors(VectorsStruct value, std::string forward_to_server,
                     EchoVectorsCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoVectors(std::move(value), "",
                                [this, &called_back, &callback](VectorsStruct resp) {
                                  called_back = true;
                                  callback(std::move(resp));
                                  loop_->Quit();
                                });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        callback(std::move(value));
      }
    }
  
    void EchoVectorsWithError(VectorsStruct value, default_enum err, std::string forward_to_server,
                              RespondWith result_variant,
                              EchoVectorsWithErrorCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoVectorsWithError(
            std::move(value), std::move(err), "", result_variant,
            [this, &called_back, &callback](Echo_EchoVectorsWithError_Result result) {
              called_back = true;
              callback(std::move(result));
              loop_->Quit();
            });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        Echo_EchoVectorsWithError_Result result;
        if (result_variant == RespondWith::ERR) {
          result.set_err(err);
        } else {
          result.set_response(Echo_EchoVectorsWithError_Response(std::move(value)));
        }
        callback(std::move(result));
      }
    }
  
    void EchoTable(AllTypesTable value, std::string forward_to_server,
                   EchoTableCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoTable(std::move(value), "",
                              [this, &called_back, &callback](AllTypesTable resp) {
                                called_back = true;
                                callback(std::move(resp));
                                loop_->Quit();
                              });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        callback(std::move(value));
      }
    }
  
    void EchoTableWithError(AllTypesTable value, default_enum err, std::string forward_to_server,
                            RespondWith result_variant,
                            EchoTableWithErrorCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoTableWithError(
            std::move(value), std::move(err), "", result_variant,
            [this, &called_back, &callback](Echo_EchoTableWithError_Result result) {
              called_back = true;
              callback(std::move(result));
              loop_->Quit();
            });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        Echo_EchoTableWithError_Result result;
        if (result_variant == RespondWith::ERR) {
          result.set_err(err);
        } else {
          result.set_response(Echo_EchoTableWithError_Response(std::move(value)));
        }
        callback(std::move(result));
      }
    }
  
    void EchoXunions(std::vector<AllTypesXunion> value, std::string forward_to_server,
                     EchoXunionsCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoXunions(std::move(value), "",
                                [this, &called_back, &callback](std::vector<AllTypesXunion> resp) {
                                  called_back = true;
                                  callback(std::move(resp));
                                  loop_->Quit();
                                });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        callback(std::move(value));
      }
    }
  
    void EchoXunionsWithError(std::vector<AllTypesXunion> value, default_enum err,
                              std::string forward_to_server, RespondWith result_variant,
                              EchoXunionsWithErrorCallback callback) override {
      if (!forward_to_server.empty()) {
        EchoClientApp app;
        bool failed = false;
        app.echo().set_error_handler([this, &forward_to_server, &failed](zx_status_t status) {
          failed = true;
          loop_->Quit();
          FX_LOGS(ERROR) << "error communicating with " << forward_to_server << ": " << status;
        });
        app.Start(forward_to_server);
        bool called_back = false;
        app.echo()->EchoXunionsWithError(
            std::move(value), std::move(err), "", result_variant,
            [this, &called_back, &callback](Echo_EchoXunionsWithError_Result result) {
              called_back = true;
              callback(std::move(result));
              loop_->Quit();
            });
        while (!called_back && !failed) {
          loop_->Run();
        }
        loop_->ResetQuit();
      } else {
        Echo_EchoXunionsWithError_Result result;
        if (result_variant == RespondWith::ERR) {
          result.set_err(err);
        } else {
          result.set_response(Echo_EchoXunionsWithError_Response(std::move(value)));
        }
        callback(std::move(result));
      }
    }
  
   private:
    void HandleEchoEvent(Struct value) {
      for (const auto& binding : bindings_.bindings()) {
        Struct to_send;
        value.Clone(&to_send);
        binding->events().EchoEvent(std::move(to_send));
      }
    }
  
    EchoPtr server_ptr;
    EchoServerApp(const EchoServerApp&) = delete;
    EchoServerApp& operator=(const EchoServerApp&) = delete;
  
    async::Loop* loop_;
    std::unique_ptr<sys::ComponentContext> context_;
    fidl::BindingSet<Echo> bindings_;
    std::vector<std::unique_ptr<EchoClientApp>> client_apps_;
  };
  
  }  // namespace compatibility
  }  // namespace test
  }  // namespace fidl
  
  int main(int argc, const char** argv) {
    // The FIDL support lib requires async_get_default_dispatcher() to return
    // non-null.
    async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
  
    fidl::test::compatibility::EchoServerApp app(&loop);
    loop.Run();
    return EXIT_SUCCESS;
  }
  