Update MoQT chat client to use InteractiveCli when there is no output file.

Not in production.

PiperOrigin-RevId: 609148698
diff --git a/quiche/quic/moqt/tools/chat_client_bin.cc b/quiche/quic/moqt/tools/chat_client_bin.cc
index 9bc100c..920fc15 100644
--- a/quiche/quic/moqt/tools/chat_client_bin.cc
+++ b/quiche/quic/moqt/tools/chat_client_bin.cc
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "absl/container/flat_hash_map.h"
+#include "absl/functional/bind_front.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "quiche/quic/core/crypto/proof_verifier.h"
@@ -31,6 +32,7 @@
 #include "quiche/quic/platform/api/quic_default_proof_providers.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
 #include "quiche/quic/tools/fake_proof_verifier.h"
+#include "quiche/quic/tools/interactive_cli.h"
 #include "quiche/quic/tools/quic_name_lookup.h"
 #include "quiche/quic/tools/quic_url.h"
 #include "quiche/common/platform/api/quiche_command_line_flags.h"
@@ -65,6 +67,7 @@
     if (!output_filename_.empty()) {
       output_file_.open(output_filename_);
       output_file_ << "Chat transcript:\n";
+      output_file_.flush();
     }
     if (ignore_certificate) {
       verifier = std::make_unique<quic::FakeProofVerifier>();
@@ -76,6 +79,12 @@
     session_callbacks_.session_established_callback = [this]() {
       std::cout << "Session established\n";
       session_is_open_ = true;
+      if (output_filename_.empty()) {  // Use the CLI.
+        cli_ = std::make_unique<quic::InteractiveCli>(
+            event_loop_.get(),
+            absl::bind_front(&ChatClient::OnTerminalLineInput, this));
+        cli_->PrintLine("Fully connected. Enter '/exit' to exit the chat.\n");
+      }
     };
     session_callbacks_.session_terminated_callback =
         [this](absl::string_view error_message) {
@@ -88,6 +97,20 @@
     client_->Connect(path, std::move(session_callbacks_));
   }
 
+  void OnTerminalLineInput(absl::string_view input_message) {
+    if (input_message.empty()) {
+      return;
+    }
+    if (input_message == "/exit") {
+      session_is_open_ = false;
+      return;
+    }
+    session_->PublishObject(my_track_name_, next_sequence_.group++,
+                            next_sequence_.object, /*object_send_order=*/0,
+                            moqt::MoqtForwardingPreference::kObject,
+                            input_message, true);
+  }
+
   bool session_is_open() const { return session_is_open_; }
   bool is_syncing() const {
     return !catalog_group_.has_value() || subscribes_to_make_ > 0 ||
@@ -98,12 +121,6 @@
     event_loop_->RunEventLoopOnce(quic::QuicTime::Delta::FromMilliseconds(500));
   }
 
-  const moqt::FullTrackName& my_track_name() { return my_track_name_; }
-
-  moqt::MoqtSession* session() { return session_; }
-
-  moqt::FullSequence& next_sequence() { return next_sequence_; }
-
   bool has_output_file() { return !output_filename_.empty(); }
 
   void WriteToFile(absl::string_view user, absl::string_view message) {
@@ -148,7 +165,6 @@
         client_->ProcessCatalog(object, this, group_sequence, object_sequence);
         return;
       }
-      // TODO: Message is from another chat participant
       std::string username = full_track_name.track_namespace;
       username = username.substr(username.find_last_of('/') + 1);
       if (!client_->other_users_.contains(username)) {
@@ -159,11 +175,17 @@
         client_->WriteToFile(username, object);
         return;
       }
-      std::cout << username << ": " << object << "\n";
+      if (cli_ != nullptr) {
+        std::string full_output = absl::StrCat(username, ": ", object);
+        cli_->PrintLine(full_output);
+      }
     }
 
+    void set_cli(quic::InteractiveCli* cli) { cli_ = cli; }
+
    private:
     ChatClient* client_;
+    quic::InteractiveCli* cli_;
   };
 
   // returns false on error
@@ -326,8 +348,13 @@
   // Handling incoming and outgoing messages
   moqt::FullSequence next_sequence_ = {0, 0};
 
+  // Used when chat output goes to file.
   std::ofstream output_file_;
   std::string output_filename_;
+
+  // Used when there is no output file, and both input and output are in the
+  // terminal.
+  std::unique_ptr<quic::InteractiveCli> cli_;
 };
 
 // A client for MoQT over chat, used for interop testing. See
@@ -357,47 +384,31 @@
   while (client.is_syncing()) {
     client.RunEventLoop();
   }
-  if (client.session_is_open()) {
-    if (client.has_output_file()) {
-      std::cout << "Fully connected. Messages are in the output file. Exit the "
-                << "session by entering :exit\n";
-    } else {
-      std::cout << "Fully connected. Press ENTER to begin input of message, "
-                << "ENTER when done. Exit session if the message is ':exit'\n";
-    }
+  if (!client.session_is_open()) {
+    return 1;  // Something went wrong in connecting.
   }
-  bool session_is_open = client.session_is_open();
+  if (!client.has_output_file()) {
+    while (client.session_is_open()) {
+      client.RunEventLoop();
+    }
+    return 0;
+  }
+  // There is an output file.
+  std::cout << "Fully connected. Messages are in the output file. Exit the "
+            << "session by entering /exit\n";
   struct pollfd poll_settings = {
       0,
       POLLIN,
       POLLIN,
   };
-  while (session_is_open) {
+  while (client.session_is_open()) {
     std::string message_to_send;
     while (poll(&poll_settings, 1, 0) <= 0) {
       client.RunEventLoop();
     }
-    if (!client.has_output_file()) {
-      std::cin.ignore(10, '\n');
-      std::cout << username << ": ";
-    }
     std::getline(std::cin, message_to_send);
-    if (message_to_send.empty()) {
-      continue;
-    }
-    if (message_to_send == ":exit") {
-      std::cout << "Exiting the app.\n";
-      // TODO: Close the session.
-      session_is_open = false;
-      break;
-    }
-    if (client.has_output_file()) {
-      client.WriteToFile(username, message_to_send);
-    }
-    client.session()->PublishObject(
-        client.my_track_name(), client.next_sequence().group++,
-        client.next_sequence().object, /*object_send_order=*/0,
-        moqt::MoqtForwardingPreference::kObject, message_to_send, true);
+    client.OnTerminalLineInput(message_to_send);
+    client.WriteToFile(username, message_to_send);
   }
   return 0;
 }