#include "config_center.h"

#include "autostart.h"
#include "rd_log.h"

namespace crossdesk {

namespace {

bool IsValidTurnModeValue(long value) {
  return value >= static_cast<long>(ConfigCenter::TURN_MODE::DISABLED) &&
         value <= static_cast<long>(ConfigCenter::TURN_MODE::FORCE_TCP);
}

}  // namespace

ConfigCenter::ConfigCenter(const std::string& config_path)
    : config_path_(config_path) {
  ini_.SetUnicode(true);
  Load();
}

ConfigCenter::~ConfigCenter() {}

int ConfigCenter::Load() {
  SI_Error rc = ini_.LoadFile(config_path_.c_str());
  if (rc < 0) {
    Save();
    return -1;
  }

  bool persist_config_migration = false;
  persist_config_migration |= ini_.Delete(section_, "video_content_type");
  persist_config_migration |= ini_.Delete(section_, "screen_content");
  persist_config_migration |=
      ini_.Delete(section_, "enable_desktop_quality_optimization");
  persist_config_migration |=
      ini_.Delete(section_, "enable_minimize_to_tray");

  const long language_value =
      ini_.GetLongValue(section_, "language", static_cast<long>(language_));
  if (language_value < static_cast<long>(LANGUAGE::CHINESE) ||
      language_value > static_cast<long>(LANGUAGE::RUSSIAN)) {
    language_ = LANGUAGE::ENGLISH;
  } else {
    language_ = static_cast<LANGUAGE>(language_value);
  }

  video_quality_ = static_cast<VIDEO_QUALITY>(ini_.GetLongValue(
      section_, "video_quality", static_cast<long>(video_quality_)));

  video_frame_rate_ = static_cast<VIDEO_FRAME_RATE>(ini_.GetLongValue(
      section_, "video_frame_rate", static_cast<long>(video_frame_rate_)));

  video_encode_format_ = static_cast<VIDEO_ENCODE_FORMAT>(
      ini_.GetLongValue(section_, "video_encode_format",
                        static_cast<long>(video_encode_format_)));

  hardware_video_codec_ = ini_.GetBoolValue(section_, "hardware_video_codec",
                                            hardware_video_codec_);
  const char* turn_mode_value = ini_.GetValue(section_, "turn_mode", nullptr);
  if (turn_mode_value != nullptr && strlen(turn_mode_value) > 0) {
    const long parsed_turn_mode = ini_.GetLongValue(
        section_, "turn_mode", static_cast<long>(turn_mode_));
    if (IsValidTurnModeValue(parsed_turn_mode)) {
      turn_mode_ = static_cast<TURN_MODE>(parsed_turn_mode);
    } else {
      LOG_WARN("Invalid TURN mode [{}], using auto UDP/TCP",
               parsed_turn_mode);
      turn_mode_ = TURN_MODE::AUTO_UDP_TCP;
    }
  } else {
    const bool legacy_enable_turn = ini_.GetBoolValue(
        section_, "enable_turn", turn_mode_ != TURN_MODE::DISABLED);
    turn_mode_ = legacy_enable_turn ? TURN_MODE::AUTO_UDP_TCP
                                    : TURN_MODE::DISABLED;
    ini_.SetLongValue(section_, "turn_mode", static_cast<long>(turn_mode_));
    persist_config_migration = true;
  }
  enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_);
  enable_self_hosted_ =
      ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);

  const char* signal_server_host_value =
      ini_.GetValue(section_, "signal_server_host", nullptr);
  if (signal_server_host_value != nullptr &&
      strlen(signal_server_host_value) > 0) {
    signal_server_host_ = signal_server_host_value;
  } else {
    signal_server_host_ = "";
  }
  const char* signal_server_port_value =
      ini_.GetValue(section_, "signal_server_port", nullptr);
  if (signal_server_port_value != nullptr &&
      strlen(signal_server_port_value) > 0) {
    signal_server_port_ =
        static_cast<int>(ini_.GetLongValue(section_, "signal_server_port", 0));
  } else {
    signal_server_port_ = 0;
  }
  const char* coturn_server_port_value =
      ini_.GetValue(section_, "coturn_server_port", nullptr);
  if (coturn_server_port_value != nullptr &&
      strlen(coturn_server_port_value) > 0) {
    coturn_server_port_ =
        static_cast<int>(ini_.GetLongValue(section_, "coturn_server_port", 0));
  } else {
    coturn_server_port_ = 0;
  }

  enable_autostart_ =
      ini_.GetBoolValue(section_, "enable_autostart", enable_autostart_);
  enable_daemon_ = ini_.GetBoolValue(section_, "enable_daemon", enable_daemon_);
  portable_service_prompt_suppressed_ =
      ini_.GetBoolValue(section_, "portable_service_prompt_suppressed",
                        portable_service_prompt_suppressed_);

  const char* file_transfer_save_path_value =
      ini_.GetValue(section_, "file_transfer_save_path", nullptr);
  if (file_transfer_save_path_value != nullptr &&
      strlen(file_transfer_save_path_value) > 0) {
    file_transfer_save_path_ = file_transfer_save_path_value;
  } else {
    file_transfer_save_path_ = "";
  }

  if (persist_config_migration && ini_.SaveFile(config_path_.c_str()) < 0) {
    return -1;
  }

  return 0;
}

int ConfigCenter::Save() {
  ini_.SetLongValue(section_, "language", static_cast<long>(language_));
  ini_.SetLongValue(section_, "video_quality",
                    static_cast<long>(video_quality_));
  ini_.SetLongValue(section_, "video_frame_rate",
                    static_cast<long>(video_frame_rate_));
  ini_.SetLongValue(section_, "video_encode_format",
                    static_cast<long>(video_encode_format_));
  ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
  ini_.SetLongValue(section_, "turn_mode", static_cast<long>(turn_mode_));
  ini_.SetBoolValue(section_, "enable_turn",
                    turn_mode_ != TURN_MODE::DISABLED);
  ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
  ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);

  // only save when self hosted
  if (enable_self_hosted_) {
    ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
    ini_.SetLongValue(section_, "signal_server_port",
                      static_cast<long>(signal_server_port_));
    ini_.SetLongValue(section_, "coturn_server_port",
                      static_cast<long>(coturn_server_port_));
  }

  ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
  ini_.SetBoolValue(section_, "enable_daemon", enable_daemon_);
  ini_.SetBoolValue(section_, "portable_service_prompt_suppressed",
                    portable_service_prompt_suppressed_);

  ini_.SetValue(section_, "file_transfer_save_path",
                file_transfer_save_path_.c_str());

  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }

  return 0;
}

// setters

int ConfigCenter::SetLanguage(LANGUAGE language) {
  language_ = language;
  ini_.SetLongValue(section_, "language", static_cast<long>(language_));
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
  video_quality_ = video_quality;
  ini_.SetLongValue(section_, "video_quality",
                    static_cast<long>(video_quality_));
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate) {
  video_frame_rate_ = video_frame_rate;
  ini_.SetLongValue(section_, "video_frame_rate",
                    static_cast<long>(video_frame_rate_));
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetVideoEncodeFormat(
    VIDEO_ENCODE_FORMAT video_encode_format) {
  video_encode_format_ = video_encode_format;
  ini_.SetLongValue(section_, "video_encode_format",
                    static_cast<long>(video_encode_format_));
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
  hardware_video_codec_ = hardware_video_codec;
  ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetTurnMode(TURN_MODE turn_mode) {
  if (!IsValidTurnModeValue(static_cast<long>(turn_mode))) {
    return -1;
  }

  turn_mode_ = turn_mode;
  ini_.SetLongValue(section_, "turn_mode", static_cast<long>(turn_mode_));
  ini_.SetBoolValue(section_, "enable_turn",
                    turn_mode_ != TURN_MODE::DISABLED);
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetTurn(bool enable_turn) {
  if (!enable_turn) {
    return SetTurnMode(TURN_MODE::DISABLED);
  }
  if (turn_mode_ == TURN_MODE::DISABLED) {
    return SetTurnMode(TURN_MODE::AUTO_UDP_TCP);
  }
  return SetTurnMode(turn_mode_);
}

int ConfigCenter::SetSrtp(bool enable_srtp) {
  enable_srtp_ = enable_srtp;
  ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetServerHost(const std::string& signal_server_host) {
  signal_server_host_ = signal_server_host;
  ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetServerPort(int signal_server_port) {
  signal_server_port_ = signal_server_port;
  ini_.SetLongValue(section_, "signal_server_port",
                    static_cast<long>(signal_server_port_));
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetCoturnServerPort(int coturn_server_port) {
  coturn_server_port_ = coturn_server_port;
  ini_.SetLongValue(section_, "coturn_server_port",
                    static_cast<long>(coturn_server_port_));
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
  enable_self_hosted_ = enable_self_hosted;
  ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);

  // load from config if self hosted is enabled
  if (enable_self_hosted_) {
    const char* signal_server_host_value =
        ini_.GetValue(section_, "signal_server_host", nullptr);
    if (signal_server_host_value != nullptr &&
        strlen(signal_server_host_value) > 0) {
      signal_server_host_ = signal_server_host_value;
    }
    const char* signal_server_port_value =
        ini_.GetValue(section_, "signal_server_port", nullptr);
    if (signal_server_port_value != nullptr &&
        strlen(signal_server_port_value) > 0) {
      signal_server_port_ = static_cast<int>(
          ini_.GetLongValue(section_, "signal_server_port", 0));
    }
    const char* coturn_server_port_value =
        ini_.GetValue(section_, "coturn_server_port", nullptr);
    if (coturn_server_port_value != nullptr &&
        strlen(coturn_server_port_value) > 0) {
      coturn_server_port_ = static_cast<int>(
          ini_.GetLongValue(section_, "coturn_server_port", 0));
    }

    ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
    ini_.SetLongValue(section_, "signal_server_port",
                      static_cast<long>(signal_server_port_));
    ini_.SetLongValue(section_, "coturn_server_port",
                      static_cast<long>(coturn_server_port_));
  }

  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

int ConfigCenter::SetAutostart(bool enable_autostart) {
  enable_autostart_ = enable_autostart;
  bool success = false;
  if (enable_autostart) {
    success = EnableAutostart("CrossDesk");
  } else {
    success = DisableAutostart("CrossDesk");
  }

  ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }

  if (!success) {
    LOG_ERROR("SetAutostart failed");
    return -1;
  }

  return 0;
}

int ConfigCenter::SetDaemon(bool enable_daemon) {
  enable_daemon_ = enable_daemon;

  ini_.SetBoolValue(section_, "enable_daemon", enable_daemon_);
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }

  return 0;
}

int ConfigCenter::SetPortableServicePromptSuppressed(bool suppressed) {
  portable_service_prompt_suppressed_ = suppressed;
  ini_.SetBoolValue(section_, "portable_service_prompt_suppressed",
                    portable_service_prompt_suppressed_);
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }

  return 0;
}

// getters

ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }

ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() const {
  return video_quality_;
}

ConfigCenter::VIDEO_FRAME_RATE ConfigCenter::GetVideoFrameRate() const {
  return video_frame_rate_;
}

ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() const {
  return video_encode_format_;
}

bool ConfigCenter::IsHardwareVideoCodec() const {
  return hardware_video_codec_;
}

ConfigCenter::TURN_MODE ConfigCenter::GetTurnMode() const {
  return turn_mode_;
}

bool ConfigCenter::IsEnableTurn() const {
  return turn_mode_ != TURN_MODE::DISABLED;
}

bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; }

std::string ConfigCenter::GetSignalServerHost() const {
  return signal_server_host_;
}

int ConfigCenter::GetSignalServerPort() const { return signal_server_port_; }

int ConfigCenter::GetCoturnServerPort() const { return coturn_server_port_; }

std::string ConfigCenter::GetDefaultServerHost() const {
  return signal_server_host_default_;
}

int ConfigCenter::GetDefaultSignalServerPort() const {
  return server_port_default_;
}

int ConfigCenter::GetDefaultCoturnServerPort() const {
  return coturn_server_port_default_;
}

bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }

bool ConfigCenter::IsEnableAutostart() const { return enable_autostart_; }

bool ConfigCenter::IsEnableDaemon() const { return enable_daemon_; }

bool ConfigCenter::IsPortableServicePromptSuppressed() const {
  return portable_service_prompt_suppressed_;
}

int ConfigCenter::SetFileTransferSavePath(const std::string& path) {
  file_transfer_save_path_ = path;
  ini_.SetValue(section_, "file_transfer_save_path",
                file_transfer_save_path_.c_str());
  SI_Error rc = ini_.SaveFile(config_path_.c_str());
  if (rc < 0) {
    return -1;
  }
  return 0;
}

std::string ConfigCenter::GetFileTransferSavePath() const {
  return file_transfer_save_path_;
}
}  // namespace crossdesk
