/*
 * @Author: DI JUNKUN
 * @Date: 2024-05-29
 * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
 */

#ifndef _MAIN_WINDOW_H_
#define _MAIN_WINDOW_H_

#include <SDL3/SDL.h>

#include <atomic>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <mutex>
#include <nlohmann/json.hpp>
#include <optional>
#include <queue>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>

#include "IconsFontAwesome6.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
#include "imgui_internal.h"
#include "minirtc.h"
#include "path_manager.h"
#include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h"
#include "thumbnail.h"

#if _WIN32
#include "win_tray.h"
#endif

namespace crossdesk {
class Render {
 public:
  struct FileTransferState {
    std::atomic<bool> file_sending_ = false;
    std::atomic<uint64_t> file_sent_bytes_ = 0;
    std::atomic<uint64_t> file_total_bytes_ = 0;
    std::atomic<uint32_t> file_send_rate_bps_ = 0;
    std::mutex file_transfer_mutex_;
    std::chrono::steady_clock::time_point file_send_start_time_;
    std::chrono::steady_clock::time_point file_send_last_update_time_;
    uint64_t file_send_last_bytes_ = 0;
    bool file_transfer_window_visible_ = false;
    std::atomic<uint32_t> current_file_id_{0};

    struct QueuedFile {
      std::filesystem::path file_path;
      std::string file_label;
      std::string remote_id;
    };
    std::queue<QueuedFile> file_send_queue_;
    std::mutex file_queue_mutex_;

    enum class FileTransferStatus { Queued, Sending, Completed, Failed };

    struct FileTransferInfo {
      std::string file_name;
      std::filesystem::path file_path;
      uint64_t file_size = 0;
      FileTransferStatus status = FileTransferStatus::Queued;
      uint64_t sent_bytes = 0;
      uint32_t file_id = 0;
      uint32_t rate_bps = 0;
    };
    std::vector<FileTransferInfo> file_transfer_list_;
    std::mutex file_transfer_list_mutex_;
  };

  struct SubStreamWindowProperties {
    Params params_;
    PeerPtr* peer_ = nullptr;
    std::string audio_label_ = "control_audio";
    std::string data_label_ = "data";
    std::string file_label_ = "file";
    std::string control_data_label_ = "control_data";
    std::string file_feedback_label_ = "file_feedback";
    std::string clipboard_label_ = "clipboard";
    std::string local_id_ = "";
    std::string remote_id_ = "";
    bool exit_ = false;
    bool signal_connected_ = false;
    SignalStatus signal_status_ = SignalStatus::SignalClosed;
    bool connection_established_ = false;
    bool rejoin_ = false;
    bool net_traffic_stats_button_pressed_ = false;
    bool mouse_control_button_pressed_ = true;
    bool mouse_controller_is_started_ = false;
    bool audio_capture_button_pressed_ = true;
    bool control_mouse_ = true;
    bool streaming_ = false;
    bool is_control_bar_in_left_ = true;
    bool control_bar_hovered_ = false;
    bool display_selectable_hovered_ = false;
    bool control_bar_expand_ = true;
    bool reset_control_bar_pos_ = false;
    bool control_window_width_is_changing_ = false;
    bool control_window_height_is_changing_ = false;
    bool p2p_mode_ = true;
    bool remember_password_ = false;
    char remote_password_[7] = "";
    float sub_stream_window_width_ = 1280;
    float sub_stream_window_height_ = 720;
    float control_window_min_width_ = 20;
    float control_window_max_width_ = 230;
    float control_window_min_height_ = 38;
    float control_window_max_height_ = 180;
    float control_window_width_ = 230;
    float control_window_height_ = 38;
    float control_bar_pos_x_ = 0;
    float control_bar_pos_y_ = 30;
    float mouse_diff_control_bar_pos_x_ = 0;
    float mouse_diff_control_bar_pos_y_ = 0;
    double control_bar_button_pressed_time_ = 0;
    double net_traffic_stats_button_pressed_time_ = 0;
    // Double-buffered NV12 frame storage. Written by decode callback thread,
    // consumed by SDL main thread.
    std::mutex video_frame_mutex_;
    std::shared_ptr<std::vector<unsigned char>> front_frame_;
    std::shared_ptr<std::vector<unsigned char>> back_frame_;
    bool render_rect_dirty_ = false;
    bool stream_cleanup_pending_ = false;
    float mouse_pos_x_ = 0;
    float mouse_pos_y_ = 0;
    float mouse_pos_x_last_ = 0;
    float mouse_pos_y_last_ = 0;
    int texture_width_ = 1280;
    int texture_height_ = 720;
    int video_width_ = 0;
    int video_height_ = 0;
    int video_width_last_ = 0;
    int video_height_last_ = 0;
    int selected_display_ = 0;
    size_t video_size_ = 0;
    bool tab_selected_ = false;
    bool tab_opened_ = true;
    std::optional<float> pos_x_before_docked_;
    std::optional<float> pos_y_before_docked_;
    float render_window_x_ = 0;
    float render_window_y_ = 0;
    float render_window_width_ = 0;
    float render_window_height_ = 0;
    std::string fullscreen_button_label_ = "Fullscreen";
    std::string net_traffic_stats_button_label_ = "Show Net Traffic Stats";
    std::string mouse_control_button_label_ = "Mouse Control";
    std::string audio_capture_button_label_ = "Audio Capture";
    std::string remote_host_name_ = "";
    std::vector<DisplayInfo> display_info_list_;
    SDL_Texture* stream_texture_ = nullptr;
    uint8_t* argb_buffer_ = nullptr;
    int argb_buffer_size_ = 0;
    SDL_Rect stream_render_rect_;
    SDL_Rect stream_render_rect_last_;
    ImVec2 control_window_pos_;
    ConnectionStatus connection_status_ = ConnectionStatus::Closed;
    TraversalMode traversal_mode_ = TraversalMode::UnknownMode;
    int fps_ = 0;
    int frame_count_ = 0;
    std::chrono::steady_clock::time_point last_time_;
    XNetTrafficStats net_traffic_stats_;

    using QueuedFile = FileTransferState::QueuedFile;
    using FileTransferStatus = FileTransferState::FileTransferStatus;
    using FileTransferInfo = FileTransferState::FileTransferInfo;
    FileTransferState file_transfer_;
  };

 public:
  Render();
  ~Render();

 public:
  int Run();

 private:
  void InitializeLogger();
  void InitializeSettings();
  void InitializeSDL();
  void InitializeModules();
  void InitializeMainWindow();
  void MainLoop();
  void UpdateLabels();
  void UpdateInteractions();
  void HandleRecentConnections();
  void HandleStreamWindow();
  void HandleServerWindow();
  void Cleanup();
  void CleanupFactories();
  void CleanupPeer(std::shared_ptr<SubStreamWindowProperties> props);
  void CleanupPeers();
  void CleanSubStreamWindowProperties(
      std::shared_ptr<SubStreamWindowProperties> props);
  void UpdateRenderRect();
  void ProcessSdlEvent(const SDL_Event& event);

  void ProcessFileDropEvent(const SDL_Event& event);

  void ProcessSelectedFile(
      const std::string& path,
      const std::shared_ptr<SubStreamWindowProperties>& props,
      const std::string& file_label, const std::string& remote_id = "");

  std::shared_ptr<SubStreamWindowProperties>
  GetSubStreamWindowPropertiesByRemoteId(const std::string& remote_id);

 private:
  int CreateStreamRenderWindow();
  int TitleBar(bool main_window);
  int MainWindow();
  int UpdateNotificationWindow();
  int StreamWindow();
  int ServerWindow();
  int RemoteClientInfoWindow();
  int LocalWindow();
  int RemoteWindow();
  int RecentConnectionsWindow();
  int SettingWindow();
  int SelfHostedServerWindow();
  int ShowSimpleFileBrowser();
  int ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props);
  int ControlBar(std::shared_ptr<SubStreamWindowProperties>& props);
  int AboutWindow();
  int StatusBar();
  bool ConnectionStatusWindow(
      std::shared_ptr<SubStreamWindowProperties>& props);
  int ShowRecentConnections();
  void Hyperlink(const std::string& label, const std::string& url,
                 const float window_width);
  int FileTransferWindow(std::shared_ptr<SubStreamWindowProperties>& props);
  std::string OpenFileDialog(std::string title);

 private:
  int ConnectTo(const std::string& remote_id, const char* password,
                bool remember_password);
  int CreateMainWindow();
  int DestroyMainWindow();
  int CreateStreamWindow();
  int DestroyStreamWindow();
  int CreateServerWindow();
  int DestroyServerWindow();
  int SetupFontAndStyle(ImFont** system_chinese_font_out);
  int DestroyMainWindowContext();
  int DestroyStreamWindowContext();
  int DestroyServerWindowContext();
  int DrawMainWindow();
  int DrawStreamWindow();
  int DrawServerWindow();
  int ConfirmDeleteConnection();
  int NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props);
  void DrawConnectionStatusText(
      std::shared_ptr<SubStreamWindowProperties>& props);
#ifdef __APPLE__
  int RequestPermissionWindow();
  bool CheckScreenRecordingPermission();
  bool CheckAccessibilityPermission();
  void OpenScreenRecordingPreferences();
  void OpenAccessibilityPreferences();
  bool DrawToggleSwitch(const char* id, bool active, bool enabled);
#endif

 public:
  static void OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
                                     const char* user_id, size_t user_id_size,
                                     const char* src_id, size_t src_id_size,
                                     void* user_data);

  static void OnReceiveAudioBufferCb(const char* data, size_t size,
                                     const char* user_id, size_t user_id_size,
                                     const char* src_id, size_t src_id_size,
                                     void* user_data);

  static void OnReceiveDataBufferCb(const char* data, size_t size,
                                    const char* user_id, size_t user_id_size,
                                    const char* src_id, size_t src_id_size,
                                    void* user_data);

  static void OnSignalStatusCb(SignalStatus status, const char* user_id,
                               size_t user_id_size, void* user_data);

  static void OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
                                   size_t user_id_size, void* user_data);

  static void OnNetStatusReport(const char* client_id, size_t client_id_size,
                                TraversalMode mode,
                                const XNetTrafficStats* net_traffic_stats,
                                const char* user_id, const size_t user_id_size,
                                void* user_data);

  static SDL_HitTestResult HitTestCallback(SDL_Window* window,
                                           const SDL_Point* area, void* data);

  static std::vector<char> SerializeRemoteAction(const RemoteAction& action);

  static bool DeserializeRemoteAction(const char* data, size_t size,
                                      RemoteAction& out);

  static void FreeRemoteAction(RemoteAction& action);

 private:
  int SendKeyCommand(int key_code, bool is_down);
  int ProcessMouseEvent(const SDL_Event& event);

  static void SdlCaptureAudioIn(void* userdata, Uint8* stream, int len);
  static void SdlCaptureAudioOut(void* userdata, Uint8* stream, int len);

 private:
  int SaveSettingsIntoCacheFile();
  int LoadSettingsFromCacheFile();

  int ScreenCapturerInit();
  int StartScreenCapturer();
  int StopScreenCapturer();

  int StartSpeakerCapturer();
  int StopSpeakerCapturer();

  int StartMouseController();
  int StopMouseController();

  int StartKeyboardCapturer();
  int StopKeyboardCapturer();

  int CreateConnectionPeer();

  // File transfer helper functions
  void StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
                         const std::filesystem::path& file_path,
                         const std::string& file_label,
                         const std::string& remote_id = "");
  void ProcessFileQueue(std::shared_ptr<SubStreamWindowProperties> props);

  int AudioDeviceInit();
  int AudioDeviceDestroy();

 private:
  struct CDCache {
    char client_id_with_password[17];
    int language;
    int video_quality;
    int video_frame_rate;
    int video_encode_format;
    bool enable_hardware_video_codec;
    bool enable_turn;
    bool enable_srtp;

    unsigned char key[16];
    unsigned char iv[16];
  };

  struct CDCacheV2 {
    char client_id_with_password[17];
    int language;
    int video_quality;
    int video_frame_rate;
    int video_encode_format;
    bool enable_hardware_video_codec;
    bool enable_turn;
    bool enable_srtp;

    unsigned char key[16];
    unsigned char iv[16];

    char self_hosted_id[17];
  };

 private:
  CDCache cd_cache_;
  CDCacheV2 cd_cache_v2_;
  std::mutex cd_cache_mutex_;
  std::unique_ptr<ConfigCenter> config_center_;
  ConfigCenter::LANGUAGE localization_language_ =
      ConfigCenter::LANGUAGE::CHINESE;
  std::unique_ptr<PathManager> path_manager_;
  std::string cert_path_;
  std::string exec_log_path_;
  std::string dll_log_path_;
  std::string cache_path_;
  int localization_language_index_ = -1;
  int localization_language_index_last_ = -1;
  bool modules_inited_ = false;
  /* ------ all windows property start ------ */
  float title_bar_width_ = 640;
  float title_bar_height_ = 30;
  float title_bar_button_width_ = 30;
  float title_bar_button_height_ = 30;
  /* ------ all windows property end ------ */

  /* ------ main window property start ------ */
  // thumbnail
  unsigned char aes128_key_[16];
  unsigned char aes128_iv_[16];
  std::shared_ptr<Thumbnail> thumbnail_;

  // recent connections
  std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
      recent_connections_;
  int recent_connection_image_width_ = 160;
  int recent_connection_image_height_ = 90;
  uint32_t recent_connection_image_save_time_ = 0;

  // main window render
  SDL_Window* main_window_ = nullptr;
  SDL_Renderer* main_renderer_ = nullptr;
  ImGuiContext* main_ctx_ = nullptr;
  ImFont* main_windows_system_chinese_font_ = nullptr;
  ImFont* stream_windows_system_chinese_font_ = nullptr;
  ImFont* server_windows_system_chinese_font_ = nullptr;
  bool exit_ = false;
  const int sdl_refresh_ms_ = 16;  // ~60 FPS
#if _WIN32
  std::unique_ptr<WinTray> tray_;
#endif

  // main window properties
  nlohmann::json latest_version_info_ = nlohmann::json{};
  bool update_available_ = false;
  std::string latest_version_ = "";
  std::string release_notes_ = "";
  bool start_mouse_controller_ = false;
  bool mouse_controller_is_started_ = false;
  bool start_screen_capturer_ = false;
  bool screen_capturer_is_started_ = false;
  bool start_speaker_capturer_ = false;
  bool speaker_capturer_is_started_ = false;
  bool start_keyboard_capturer_ = true;
  bool show_cursor_ = false;
  bool keyboard_capturer_is_started_ = false;
  bool foucs_on_main_window_ = false;
  bool foucs_on_stream_window_ = false;
  bool main_window_minimized_ = false;
  uint32_t last_main_minimize_request_tick_ = 0;
  uint32_t last_stream_minimize_request_tick_ = 0;
  bool audio_capture_ = false;
  int main_window_width_real_ = 720;
  int main_window_height_real_ = 540;
  float main_window_dpi_scaling_w_ = 1.0f;
  float main_window_dpi_scaling_h_ = 1.0f;
  float dpi_scale_ = 1.0f;
  float main_window_width_default_ = 640;
  float main_window_height_default_ = 480;
  float main_window_width_ = 640;
  float main_window_height_ = 480;
  float main_window_width_last_ = 640;
  float main_window_height_last_ = 480;
  float local_window_width_ = 320;
  float local_window_height_ = 235;
  float remote_window_width_ = 320;
  float remote_window_height_ = 235;
  float local_child_window_width_ = 266;
  float local_child_window_height_ = 180;
  float remote_child_window_width_ = 266;
  float remote_child_window_height_ = 180;
  float main_window_text_y_padding_ = 10;
  float main_child_window_x_padding_ = 27;
  float main_child_window_y_padding_ = 45;
  float status_bar_height_ = 22;
  float connection_status_window_width_ = 200;
  float connection_status_window_height_ = 150;
  float notification_window_width_ = 200;
  float notification_window_height_ = 80;
  float about_window_width_ = 300;
  float about_window_height_ = 170;
  float update_notification_window_width_ = 400;
  float update_notification_window_height_ = 320;
  int screen_width_ = 1280;
  int screen_height_ = 720;
  int selected_display_ = 0;
  std::string connect_button_label_ = "Connect";
  char input_password_tmp_[7] = "";
  char input_password_[7] = "";
  std::string random_password_ = "";
  char new_password_[7] = "";
  char remote_id_display_[12] = "";
  unsigned char audio_buffer_[720];
  int audio_len_ = 0;
  bool audio_buffer_fresh_ = false;
  bool need_to_rejoin_ = false;
  std::chrono::steady_clock::time_point last_rejoin_check_time_;
  bool just_created_ = false;
  std::string controlled_remote_id_ = "";
  std::string focused_remote_id_ = "";
  std::string remote_client_id_ = "";
  SDL_Event last_mouse_event;
  SDL_AudioStream* output_stream_;
  uint32_t STREAM_REFRESH_EVENT = 0;

  // stream window render
  SDL_Window* stream_window_ = nullptr;
  SDL_Renderer* stream_renderer_ = nullptr;
  ImGuiContext* stream_ctx_ = nullptr;

  // stream window properties
  bool need_to_create_stream_window_ = false;
  bool stream_window_created_ = false;
  bool stream_window_inited_ = false;
  bool window_maximized_ = false;
  bool stream_window_grabbed_ = false;
  bool control_mouse_ = false;
  int stream_window_width_default_ = 1280;
  int stream_window_height_default_ = 720;
  float stream_window_width_ = 1280;
  float stream_window_height_ = 720;
  SDL_PixelFormat stream_pixformat_ = SDL_PIXELFORMAT_NV12;
  int stream_window_width_real_ = 1280;
  int stream_window_height_real_ = 720;
  float stream_window_dpi_scaling_w_ = 1.0f;
  float stream_window_dpi_scaling_h_ = 1.0f;

  // server window render
  SDL_Window* server_window_ = nullptr;
  SDL_Renderer* server_renderer_ = nullptr;
  ImGuiContext* server_ctx_ = nullptr;

  // server window properties
  bool need_to_create_server_window_ = false;
  bool need_to_destroy_server_window_ = false;
  bool server_window_created_ = false;
  bool server_window_inited_ = false;
  int server_window_width_default_ = 250;
  int server_window_height_default_ = 150;
  float server_window_width_ = 250;
  float server_window_height_ = 150;
  float server_window_title_bar_height_ = 30.0f;
  SDL_PixelFormat server_pixformat_ = SDL_PIXELFORMAT_NV12;
  int server_window_normal_width_ = 250;
  int server_window_normal_height_ = 150;
  float server_window_dpi_scaling_w_ = 1.0f;
  float server_window_dpi_scaling_h_ = 1.0f;

  // server window collapsed mode
  bool server_window_collapsed_ = false;
  bool server_window_collapsed_dragging_ = false;
  float server_window_collapsed_drag_start_mouse_x_ = 0.0f;
  float server_window_collapsed_drag_start_mouse_y_ = 0.0f;
  int server_window_collapsed_drag_start_win_x_ = 0;
  int server_window_collapsed_drag_start_win_y_ = 0;

  // server window drag normal mode
  bool server_window_dragging_ = false;
  float server_window_drag_start_mouse_x_ = 0.0f;
  float server_window_drag_start_mouse_y_ = 0.0f;
  int server_window_drag_start_win_x_ = 0;
  int server_window_drag_start_win_y_ = 0;

  bool label_inited_ = false;
  bool connect_button_pressed_ = false;
  bool password_validating_ = false;
  uint32_t password_validating_time_ = 0;
  bool show_settings_window_ = false;
  bool show_self_hosted_server_config_window_ = false;
  bool rejoin_ = false;
  bool local_id_copied_ = false;
  bool show_password_ = true;
  bool show_about_window_ = false;
  bool show_connection_status_window_ = false;
  bool show_reset_password_window_ = false;
  bool show_update_notification_window_ = false;
  bool fullscreen_button_pressed_ = false;
  bool focus_on_input_widget_ = true;
  bool is_client_mode_ = false;
  bool is_server_mode_ = false;
  bool reload_recent_connections_ = true;
  bool show_confirm_delete_connection_ = false;
  bool delete_connection_ = false;
  bool is_tab_bar_hovered_ = false;
  std::string delete_connection_name_ = "";
  bool re_enter_remote_id_ = false;
  double copy_start_time_ = 0;
  SignalStatus signal_status_ = SignalStatus::SignalClosed;
  std::string signal_status_str_ = "";
  bool signal_connected_ = false;
  PeerPtr* peer_ = nullptr;
  PeerPtr* peer_reserved_ = nullptr;
  std::string video_primary_label_ = "primary_display";
  std::string video_secondary_label_ = "secondary_display";
  std::string audio_label_ = "audio";
  std::string data_label_ = "data";
  std::string info_label_ = "info";
  std::string control_data_label_ = "control_data";
  std::string file_label_ = "file";
  std::string file_feedback_label_ = "file_feedback";
  std::string clipboard_label_ = "clipboard";
  Params params_;
  // Map file_id to props for tracking file transfer progress via ACK
  std::unordered_map<uint32_t, std::weak_ptr<SubStreamWindowProperties>>
      file_id_to_props_;
  std::shared_mutex file_id_to_props_mutex_;

  // Map file_id to FileTransferState for global file transfer (props == null)
  std::unordered_map<uint32_t, FileTransferState*> file_id_to_transfer_state_;
  std::shared_mutex file_id_to_transfer_state_mutex_;
  SDL_AudioDeviceID input_dev_;
  SDL_AudioDeviceID output_dev_;
  ScreenCapturerFactory* screen_capturer_factory_ = nullptr;
  ScreenCapturer* screen_capturer_ = nullptr;
  SpeakerCapturerFactory* speaker_capturer_factory_ = nullptr;
  SpeakerCapturer* speaker_capturer_ = nullptr;
  DeviceControllerFactory* device_controller_factory_ = nullptr;
  MouseController* mouse_controller_ = nullptr;
  KeyboardCapturer* keyboard_capturer_ = nullptr;
  std::vector<DisplayInfo> display_info_list_;
  uint64_t last_frame_time_;
  bool show_new_version_icon_ = false;
  bool show_new_version_icon_in_menu_ = true;
  double new_version_icon_last_trigger_time_ = 0.0;
  double new_version_icon_render_start_time_ = 0.0;
#ifdef __APPLE__
  bool show_request_permission_window_ = true;
#endif
  char client_id_[10] = "";
  char client_id_display_[12] = "";
  char client_id_with_password_[17] = "";
  char password_saved_[7] = "";
  char self_hosted_id_[17] = "";
  char self_hosted_user_id_[17] = "";
  int language_button_value_ = 0;
  int video_quality_button_value_ = 0;
  int video_frame_rate_button_value_ = 1;
  int video_encode_format_button_value_ = 0;
  bool enable_hardware_video_codec_ = false;
  bool enable_turn_ = true;
  bool enable_srtp_ = false;
  char signal_server_ip_[256] = "api.crossdesk.cn";
  char signal_server_port_[6] = "9099";
  char coturn_server_port_[6] = "3478";
  char cert_file_path_[256] = "";
  bool enable_self_hosted_ = false;
  int language_button_value_last_ = 0;
  int video_quality_button_value_last_ = 0;
  int video_frame_rate_button_value_last_ = 0;
  int video_encode_format_button_value_last_ = 0;
  bool enable_hardware_video_codec_last_ = false;
  bool enable_turn_last_ = true;
  bool enable_srtp_last_ = false;
  bool enable_self_hosted_last_ = false;
  bool enable_autostart_ = false;
  bool enable_autostart_last_ = false;
  bool enable_daemon_ = false;
  bool enable_daemon_last_ = false;
  bool enable_minimize_to_tray_ = false;
  bool enable_minimize_to_tray_last_ = false;
  char signal_server_ip_self_[256] = "";
  char signal_server_port_self_[6] = "";
  char coturn_server_port_self_[6] = "";
  std::string tls_cert_path_self_ = "";
  bool settings_window_pos_reset_ = true;
  bool self_hosted_server_config_window_pos_reset_ = true;
  std::string selected_current_file_path_ = "";
  bool show_file_browser_ = true;
  /* ------ main window property end ------ */

  /* ------ sub stream window property start ------ */
  std::unordered_map<std::string, std::shared_ptr<SubStreamWindowProperties>>
      client_properties_;
  std::shared_mutex client_properties_mutex_;
  void CloseTab(decltype(client_properties_)::iterator& it);
  /* ------ stream window property end ------ */

  /* ------ async thumbnail save tasks ------ */
  std::vector<std::thread> thumbnail_save_threads_;
  std::mutex thumbnail_save_threads_mutex_;
  void WaitForThumbnailSaveTasks();

  /* ------ server mode ------ */
  std::unordered_map<std::string, ConnectionStatus> connection_status_;
  std::unordered_map<std::string, std::string> connection_host_names_;
  std::string selected_server_remote_id_ = "";
  std::string selected_server_remote_hostname_ = "";
  FileTransferState file_transfer_;
};
}  // namespace crossdesk
#endif