/*
 * Copyright (C) 2018  Teja Cetinski <teja@cetinski.eu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gtr-actions.h"
#include "gtr-dl-teams.h"
#include "gtr-window.h"
#include "gtr-utils.h"
#include "gtr-profile-manager.h"
#include "gtr-drop-down-option.h"
#include <libsoup/soup.h>

#include <json-glib/json-glib.h>
#include <json-glib/json-gobject.h>

#include <glib/gi18n.h>

typedef struct
{
  GtkWidget *titlebar;
  GtkWidget *main_box;
  GtkWidget *open_button;
  GtkWidget *load_button;
  GtkWidget *reserve_button;
  GtkWidget *stats_label;
  GtkWidget *module_state_label;
  GtkWidget *file_label;

  GtkWidget *langs_combobox;
  GListStore *langs_model;
  GtkWidget *modules_combobox;
  GListStore *modules_model;
  GtkWidget *domains_combobox;
  GtkWidget *branches_combobox;

  GtkStringList *branches_model;
  GListStore *domains_model;

  gchar *selected_lang;
  gchar *selected_module;
  gchar *selected_branch;
  gchar *selected_domain;
  gchar *file_path;
  gchar *module_state;

  GtrWindow *main_window;

  SoupSession *soup_session;
} GtrDlTeamsPrivate;

struct _GtrDlTeams
{
  AdwBin parent_instance;
};

G_DEFINE_TYPE_WITH_PRIVATE (GtrDlTeams, gtr_dl_teams, ADW_TYPE_BIN)

typedef struct
{
  char *name;
  char *description;
} GtrDlTeamsDomainPrivate;

struct _GtrDlTeamsDomain
{
  GObject parent_instance;
};

G_DEFINE_TYPE_WITH_PRIVATE (GtrDlTeamsDomain, gtr_dl_teams_domain, G_TYPE_OBJECT)

static void team_add_cb (GtkButton *btn, GtrDlTeams *self);
static void gtr_dl_teams_save_combo_selected (GtkWidget *widget, GParamSpec *spec, GtrDlTeams *self);
static void gtr_dl_teams_load_po_file (GtkButton *button, GtrDlTeams *self);
static void gtr_dl_teams_get_file_info (GtrDlTeams *self);
static gboolean gtr_dl_teams_reserve_for_translation (GtkWidget *button, GtrDlTeams *self);

struct _StringObject {
  GObject parent_instance;
  const char * string;
};

static void
gtr_dl_teams_combobox_add (JsonArray *array,
                           int index,
                           JsonNode *element,
                           GtkDropDown *combo)
{
  JsonObject *object = json_node_get_object (element);
  GListStore *model = G_LIST_STORE (gtk_drop_down_get_model (GTK_DROP_DOWN (combo)));
  GtrDropDownOption *option = NULL;

  const char *name = json_object_get_string_member (object, "locale");
  const char *desc = json_object_get_string_member (object, "name");

  option = gtr_drop_down_option_new (name, desc);
  g_list_store_append (model, option);
  g_object_unref (option);
}

static void
gtr_dl_modules_combobox_add (JsonArray *array,
                             int index,
                             JsonNode *element,
                             GtkDropDown *combo)
{
  JsonObject *object = json_node_get_object (element);
  GListStore *model = G_LIST_STORE (gtk_drop_down_get_model (GTK_DROP_DOWN (combo)));
  GtrDropDownOption *option = NULL;

  const char *name = json_object_get_string_member (object, "name");
  option = gtr_drop_down_option_new (name, NULL);
  g_list_store_append (model, option);
  g_object_unref (option);
}

static void
gtr_dl_teams_parse_teams_json (GObject *object,
                               GAsyncResult *result,
                               gpointer user_data)
{
  g_autoptr(JsonParser) parser = json_parser_new ();
  g_autoptr(GInputStream) stream = NULL;
  g_autoptr(GtrDropDownOption) option = NULL;
  GtrProfileManager * pmanager = NULL;
  const char *def_lang = NULL;
  unsigned int def_lang_pos = 0;
  GtrProfile *profile = NULL;
  GError *error = NULL;
  JsonNode *node = NULL;
  JsonArray *array = NULL;

  GtrDlTeams *widget = GTR_DL_TEAMS (user_data);
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (widget);

  GtkAlertDialog *dialog;

  /* Parse JSON */
  stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);

  if (error)
    {
      dialog = gtk_alert_dialog_new ("%s", error->message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      return;
    }

  json_parser_load_from_stream (parser, stream, NULL, NULL);

  node = json_parser_get_root (parser);
  array = json_node_get_array (node);

  pmanager = gtr_profile_manager_get_default ();
  profile = gtr_profile_manager_get_active_profile (pmanager);
  if (profile)
    def_lang = gtr_profile_get_language_code (profile);

  json_array_foreach_element (
    array,
    (JsonArrayForeach)gtr_dl_teams_combobox_add,
    priv->langs_combobox
  );

  option = gtr_drop_down_option_new (def_lang, NULL);
  g_list_store_find_with_equal_func (
    priv->langs_model,
    option,
    (GEqualFunc)gtr_drop_down_option_equal,
    &def_lang_pos
  );
  gtk_drop_down_set_selected (GTK_DROP_DOWN (priv->langs_combobox), def_lang_pos);

  /* Enable selection */
  gtk_widget_set_sensitive (priv->langs_combobox, TRUE);
  g_object_unref (pmanager);
}

static void
gtr_dl_teams_load_module_details_json (GtkWidget  *widget,
                                       GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  g_autoptr(SoupMessage) msg = NULL;
  g_autofree gchar *module_endpoint;
  g_autoptr(JsonParser) parser = NULL;
  gint i;
  GError *error = NULL;
  JsonNode *node = NULL;
  JsonObject *object;
  JsonNode *branchesNode;
  JsonNode *domainsNode;
  GtkAlertDialog *dialog;
  SoupStatus status_code;
  g_autoptr(GInputStream) stream = NULL;

  gtk_widget_set_visible (priv->file_label, FALSE);
  gtk_widget_set_visible (priv->module_state_label, FALSE);
  gtk_label_set_text (GTK_LABEL (priv->stats_label), "");

  /* Disable (down)load button */
  gtk_widget_set_sensitive (priv->branches_combobox, FALSE);
  gtk_widget_set_sensitive (priv->domains_combobox, FALSE);
  gtk_widget_set_sensitive (priv->load_button, FALSE);

  gtk_string_list_splice (
    priv->branches_model,
    0,
    g_list_model_get_n_items (G_LIST_MODEL (priv->branches_model)),
    NULL
  );

  g_list_store_remove_all (priv->domains_model);

  /* Get module details JSON from DL API */
  module_endpoint = g_strconcat ((const gchar *)API_URL, "modules/", priv->selected_module, NULL);
  msg = soup_message_new ("GET", module_endpoint);
  stream = soup_session_send (priv->soup_session, msg, NULL, &error);
  status_code = soup_message_get_status (msg);

  if (error || !SOUP_STATUS_IS_SUCCESSFUL (status_code))
    {
      g_autofree gchar *message = NULL;

      if (error)
        {
          message = error->message;
          g_clear_error (&error);
        }
      else
        {
          message = g_strdup (soup_message_get_reason_phrase (msg));
        }

      dialog = gtk_alert_dialog_new (_("Error loading module info: %s"), message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      return;
    }

  parser = json_parser_new ();

  /* Load response body and fill branches and domains, then show widgets */
  json_parser_load_from_stream (parser, stream, NULL, &error);
  node = json_parser_get_root (parser);

  object = json_node_get_object(node);

  /* branches */
  branchesNode = json_object_get_member (object, "branches");

  if (branchesNode != NULL)
    {
      JsonArray *branchesArray = json_node_get_array (branchesNode);
      JsonNode *branch_element;
      JsonObject *branch_object;

      for (i=0; i < json_array_get_length (branchesArray); i++)
        {
          const char *name = NULL;
          branch_element = json_array_get_element (branchesArray, i);
          branch_object = json_node_get_object (branch_element);
          name = json_object_get_string_member (branch_object, "name"),
          gtk_string_list_append (priv->branches_model, name);
        }
      gtk_drop_down_set_selected (GTK_DROP_DOWN (priv->branches_combobox), 0);
      gtk_widget_set_sensitive (priv->branches_combobox, TRUE);
    }
  // TODO: check why there are no branches, display notification to user

  /* domains */
  domainsNode = json_object_get_member (object, "domains");

  if (domainsNode != NULL)
    {
      JsonArray *domains_array = json_node_get_array (domainsNode);
      JsonNode *domain_element;
      JsonObject *domain_object;

      for (i=0; i < json_array_get_length (domains_array); i++)
        {
          domain_element = json_array_get_element (domains_array, i);
          domain_object = json_node_get_object (domain_element);
          g_list_store_append (
            priv->domains_model,
            gtr_dl_teams_domain_new (
              json_object_get_string_member (domain_object, "name"),
              json_object_get_string_member (domain_object, "description")
            )
          );
        }

      gtk_drop_down_set_selected (GTK_DROP_DOWN (priv->domains_combobox), 0);
      gtk_widget_set_sensitive (priv->domains_combobox, TRUE);
    }
  // TODO: check why there are no domains and display notification to user
}

static void
gtr_dl_teams_parse_modules_json (GObject *object,
                                 GAsyncResult *result,
                                 gpointer user_data)
{
  g_autoptr(JsonParser) parser = json_parser_new ();
  g_autoptr(GInputStream) stream = NULL;
  GError *error = NULL;

  JsonNode *node = NULL;
  JsonArray *array = NULL;

  GtrDlTeams *widget = GTR_DL_TEAMS (user_data);
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (widget);

  GtkAlertDialog *dialog;

  /* Parse JSON */
  stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
  if (error)
    {
      dialog = gtk_alert_dialog_new ("%s", error->message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      return;
    }

  json_parser_load_from_stream (parser, stream, NULL, NULL);

  node = json_parser_get_root (parser);
  array = json_node_get_array (node);

  json_array_foreach_element (
    array,
    (JsonArrayForeach)gtr_dl_modules_combobox_add,
    priv->modules_combobox
  );

  gtk_widget_set_sensitive (priv->modules_combobox, TRUE);
}

void
gtr_dl_teams_load_json (GtrDlTeams *self)
{
  /* Get team list JSON from DL */
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  SoupMessage *message = NULL;
  char *url = NULL;

  url = g_strconcat ((const gchar *)API_URL, "languages", NULL);
  message = soup_message_new ("GET", url);
  soup_session_send_async (priv->soup_session, message, G_PRIORITY_DEFAULT, NULL, gtr_dl_teams_parse_teams_json, self);


  g_object_unref (message);
  g_free (url);

  /* Get module list JSON from DL */
  url = g_strconcat ((const gchar *)API_URL, "modules", NULL);
  message = soup_message_new ("GET", url);
  soup_session_send_async (priv->soup_session, message, G_PRIORITY_DEFAULT, NULL, gtr_dl_teams_parse_modules_json, self);

  g_object_unref (message);
  g_free (url);
}

void gtr_dl_teams_verify_and_load (GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);

  if (priv->selected_lang != NULL &&
      priv->selected_module != NULL &&
      priv->selected_branch != NULL &&
      priv->selected_domain != NULL)
    {
      // get stats and path from DL API and enable (down)load button
      gtr_dl_teams_get_file_info (self);
    }
}

static void
gtr_dl_teams_get_file_info (GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  gchar *stats_endpoint;
  JsonNode *node = NULL;
  g_autoptr(JsonParser) parser = NULL;
  JsonObject *object;
  g_autoptr(SoupMessage) msg = NULL;
  GError *error = NULL;
  JsonNode *stats_node;
  JsonObject *stats_object;
  const char *format;
  char *markup;
  GtkAlertDialog *dialog;
  SoupStatus status_code;
  g_autoptr(GInputStream) stream = NULL;

  /* API endpoint: modules/[module]/branches/[branch]/domains/[domain]/languages/[team] */
  stats_endpoint = g_strconcat ((const gchar *)API_URL,
                                 "modules/",
                                 priv->selected_module,
                                 "/branches/",
                                 priv->selected_branch,
                                 "/domains/",
                                 priv->selected_domain,
                                 "/languages/",
                                 priv->selected_lang,
                                 NULL);

  msg = soup_message_new ("GET", stats_endpoint);
  stream = soup_session_send (priv->soup_session, msg, NULL, &error);
  status_code = soup_message_get_status (msg);

  if (error || !SOUP_STATUS_IS_SUCCESSFUL (status_code))
    {
      g_autofree gchar *message = NULL;

      if (error)
        {
          message = error->message;
          g_clear_error (&error);
        }
      else
        {
          message = g_strdup (soup_message_get_reason_phrase (msg));
        }

      dialog = gtk_alert_dialog_new (_("Error loading file info: %s"), message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      return;
    }

  parser = json_parser_new ();

  /* Load response body and get path to PO file */
  json_parser_load_from_stream (parser, stream, NULL, &error);
  node = json_parser_get_root (parser);

  object = json_node_get_object(node);

  /* Save file path; escape the string - slashes inside! */
  priv->file_path = g_strescape (json_object_get_string_member (object, "po_file"), "");

  if (!priv->file_path)
    {
      gtk_label_set_text (GTK_LABEL (priv->file_label), "No file found.");
      gtk_widget_set_visible (priv->file_label, TRUE);
      return;
    }

  if (priv->module_state)
    g_free (priv->module_state);
  priv->module_state = g_strdup (json_object_get_string_member (object, "state"));

  if (!priv->module_state)
    {
      gtk_label_set_text (GTK_LABEL (priv->module_state_label), _("No module state found."));
      gtk_widget_set_visible (priv->module_state_label, TRUE);
    }

  /* Get file statistics and show them to the user */
  stats_node = json_object_get_member (object, "statistics");
  stats_object = json_node_get_object (stats_node);

  format = "<span color=\"green\">\%d translated</span>, <span color=\"orange\">\%d fuzzy</span>, <span color=\"red\">\%d untranslated</span>";
  markup = g_markup_printf_escaped (format,
                                    json_object_get_int_member (stats_object, "trans"),
                                    json_object_get_int_member (stats_object, "fuzzy"),
                                    json_object_get_int_member (stats_object, "untrans"));

  gtk_label_set_markup (GTK_LABEL (priv->stats_label), markup);
  gtk_label_set_text (GTK_LABEL (priv->file_label), strrchr (priv->file_path, '/') + 1);
  gtk_widget_set_visible (priv->file_label, TRUE);
  gtk_label_set_text (GTK_LABEL (priv->module_state_label), priv->module_state);
  gtk_widget_set_visible (priv->module_state_label, TRUE);
  /* Enable (down)load button */
  gtk_widget_set_sensitive (priv->load_button, TRUE);

  /* Enable the reserve button if a module's state is either None or Translated or ToReview */
  if (strcmp(priv->module_state, "None") == 0 ||
      strcmp(priv->module_state, "Translated") == 0 ||
      strcmp(priv->module_state, "ToReview") == 0)
    {
      gtk_widget_set_sensitive (priv->reserve_button, TRUE);
    }
  else
    {
      gtk_widget_set_sensitive (priv->reserve_button, FALSE);
    }

  g_free (markup);
}

static void
gtr_dl_teams_load_po_file (GtkButton *button, GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  g_autoptr(SoupMessage) msg = NULL;
  GError *error = NULL;
  GFile *tmp_file = NULL;
  GFileIOStream *iostream = NULL;
  GOutputStream *output = NULL;
  GtkAlertDialog *dialog;
  gboolean ret = FALSE;
  int file_index = 0;
  const char *dest_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
  g_autofree char *basename = NULL;
  g_autofree char *file_path = NULL;
  g_autoptr(GFile) dest_file = NULL;
  gboolean reserve_first = FALSE;
  SoupStatus status_code;
  GBytes *bytes;

  // reserve for translation first
  reserve_first = gtk_switch_get_active (GTK_SWITCH (priv->reserve_button));
  if (reserve_first)
    {
      ret = gtr_dl_teams_reserve_for_translation (priv->reserve_button, self);
      if (!ret)
        return;

      if (priv->module_state)
        g_free (priv->module_state);

      // The reserve was successful, so we should change the module_state to
      // "Translating"
      priv->module_state = g_strdup ("Translating");
    }

  /* Load the file, save as temp; path to file is https://l10n.gnome.org/[priv->file_path] */
  msg = soup_message_new ("GET", g_strconcat (DL_SERVER, g_strcompress(priv->file_path), NULL));
  bytes = soup_session_send_and_read (priv->soup_session, msg, NULL, &error);
  status_code = soup_message_get_status (msg);

  if (error || !SOUP_STATUS_IS_SUCCESSFUL (status_code))
    {
      g_autofree gchar *message = NULL;

      if (error)
        {
          message = error->message;
          g_clear_error (&error);
        }
      else
        {
          message = g_strdup (soup_message_get_reason_phrase (msg));
        }

      dialog = gtk_alert_dialog_new (_("Error loading file: %s"), message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      return;
    }

  tmp_file = g_file_new_tmp (NULL, &iostream, &error);

  if (error != NULL)
    {
      dialog = gtk_alert_dialog_new (_("Error creating tmp file: %s"), error->message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      g_error_free (error);
      return;
    }

  output = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
  g_output_stream_write_bytes (output,
                               bytes,
                               NULL,
                               &error);
  g_bytes_unref (bytes);

  if (error != NULL)
    {
      dialog = gtk_alert_dialog_new (_("Error writing stream: %s"), error->message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      g_error_free (error);
      return;
    }

  /* Save file to Downloads; file basename is the part from last / character on */
  basename = g_path_get_basename (priv->file_path);
  // Remove the extension
  file_path = g_strconcat ("file://", dest_dir, "/", basename, NULL);
  dest_file = g_file_new_for_uri (file_path);

  ret = g_file_copy (tmp_file, dest_file, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
  while (!ret && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
    {
      g_autofree char *tmpname = gtr_utils_get_filename (basename);
      g_free (basename);
      g_free (file_path);
      g_object_unref (dest_file);
      g_clear_error (&error);

      basename = g_strdup_printf ("%s (%d).po", tmpname, ++file_index);
      file_path = g_strconcat ("file://", dest_dir, "/", basename, NULL);
      dest_file = g_file_new_for_uri (file_path);
      ret = g_file_copy (tmp_file, dest_file, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
    }

  if (error != NULL)
    {
      dialog = gtk_alert_dialog_new (_("Error creating tmp file: %s"), error->message);
      gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
      g_error_free (error);
      return;
    }

  if (gtr_open (dest_file, priv->main_window, &error)) {
    GtrTab *tab = gtr_window_get_active_tab (priv->main_window);
    g_autofree char *info_msg = NULL;
    info_msg = g_strdup_printf (_("The file '%s' has been saved in %s"),
                                basename, dest_dir);
    gtr_tab_set_info (tab, info_msg, NULL);

    GtrPo *po = gtr_tab_get_po(tab);
    GError *po_error = NULL;
    gtr_po_set_dl_info(po,
                       priv->selected_lang,
                       priv->selected_module,
                       priv->selected_branch,
                       priv->selected_domain,
                       priv->module_state);
    // Save to update the headers
    gtr_po_save_file(po, &po_error);
  }

  g_object_unref (tmp_file);
}

/* Reserve for translation */
static gboolean
gtr_dl_teams_reserve_for_translation (GtkWidget *button, GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  GtrProfileManager *pmanager = NULL;
  GtrProfile *profile = NULL;
  GtkAlertDialog *dialog;
  const char *auth_token = NULL;
  SoupStatus status_code;
  GError *error = NULL;

  g_autoptr (SoupMessage) msg = NULL;
  g_autoptr(GInputStream) stream = NULL;
  g_autofree gchar *message = NULL;
  g_autofree char *auth = NULL;
  g_autofree gchar *reserve_endpoint = NULL;

  pmanager = gtr_profile_manager_get_default ();
  profile = gtr_profile_manager_get_active_profile (pmanager);
  auth_token = gtr_profile_get_auth_token (profile);
  auth = g_strconcat ("Bearer ", auth_token, NULL);

  /* API endpoint: modules/[module]/branches/[branch]/domains/[domain]/languages/[team]/reserve */
  reserve_endpoint = g_strconcat ((const gchar *)API_URL,
                                  "modules/", priv->selected_module,
                                  "/branches/", priv->selected_branch,
                                  "/domains/", priv->selected_domain,
                                  "/languages/", priv->selected_lang,
                                  "/reserve", NULL);

  msg = soup_message_new ("POST", reserve_endpoint);
  soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
  soup_message_headers_append (soup_message_get_request_headers (msg),
                               "Authentication", auth);
  stream = soup_session_send (priv->soup_session, msg, NULL, &error);
  status_code = soup_message_get_status (msg);

  if (error || !SOUP_STATUS_IS_SUCCESSFUL (status_code))
  {
    if (error)
      {
        message = error->message;
        g_clear_error (&error);
      }
    else
      {
        message = g_strdup (soup_message_get_reason_phrase (msg));
      }

    dialog = gtk_alert_dialog_new (
      _(
        "An error occurred while reserving this module: %s\n"
        "Maybe you've not configured your l10n.gnome.org "
        "token correctly in your profile or you don't have "
        "permissions to reserve this module."
      ),
      message);
    gtk_alert_dialog_show (GTK_ALERT_DIALOG (dialog), GTK_WINDOW (priv->main_window));
    return FALSE;
  }

  /* Display a message if the reserve for translation operation was successful */
  message = g_strdup_printf (
    _("The file '%s.%s.%s.%s' has been successfully reserved"),
    priv->selected_module,
    priv->selected_branch,
    priv->selected_lang,
    priv->selected_domain);

  gtk_widget_set_sensitive (priv->reserve_button, FALSE);
  gtr_window_add_toast_msg (GTR_WINDOW (priv->main_window), message);

  return TRUE;
}

static void
gtr_dl_teams_save_combo_selected (GtkWidget  *widget,
                                  GParamSpec *spec,
                                  GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  const gchar *name;

  /* Save selected combo option */
  name = gtk_widget_get_name (widget);

  if (strcmp(name, "combo_modules") == 0)
    {
      GtrDropDownOption *opt = GTR_DROP_DOWN_OPTION (
        gtk_drop_down_get_selected_item (GTK_DROP_DOWN (priv->modules_combobox))
      );
      if (priv->selected_module)
        g_free (priv->selected_module);

      if (opt)
        priv->selected_module = g_strdup (gtr_drop_down_option_get_name (opt));
      else
        priv->selected_module = NULL;

      /* Reload module details on module change */
      gtr_dl_teams_load_module_details_json (widget, self);
    }
  else if (strcmp(name, "combo_langs") == 0)
    {
      GtrDropDownOption *opt = GTR_DROP_DOWN_OPTION (
        gtk_drop_down_get_selected_item (GTK_DROP_DOWN (priv->langs_combobox))
      );
      if (priv->selected_lang)
        g_free (priv->selected_lang);
      if (opt)
        priv->selected_lang = g_strdup (gtr_drop_down_option_get_name (opt));
      else
        priv->selected_lang = NULL;
    }
  else if (strcmp(name, "combo_branches") == 0)
    {
      int selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (widget));
      if (priv->selected_branch)
        g_free (priv->selected_branch);
      if (selected != GTK_INVALID_LIST_POSITION)
        priv->selected_branch = g_strdup (gtk_string_list_get_string (priv->branches_model, selected));
      else
        priv->selected_branch = NULL;
    }
  else if (strcmp(name, "combo_domains") == 0)
    {
      GObject *domain = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (priv->domains_combobox));
      if (priv->selected_domain)
        g_free (priv->selected_domain);
      if (domain)
        priv->selected_domain = g_strdup (gtr_dl_teams_domain_name (GTR_DL_TEAMS_DOMAIN (domain)));
      else
        priv->selected_domain = NULL;
    }

  /* Check if all four required values have been selected to proceed with loading PO file */
  gtr_dl_teams_verify_and_load (self);
}

static void
gtr_dl_teams_dispose (GObject *object)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (GTR_DL_TEAMS (object));

  if (priv->selected_lang)
    {
      g_free (priv->selected_lang);
      priv->selected_lang = NULL;
    }
  if (priv->selected_module)
    {
      g_free (priv->selected_module);
      priv->selected_module = NULL;
    }
  if (priv->selected_branch)
    {
      g_free (priv->selected_branch);
      priv->selected_branch = NULL;
    }
  if (priv->selected_domain)
    {
      g_free (priv->selected_domain);
      priv->selected_domain = NULL;
    }
  if (priv->module_state)
    {
      g_free (priv->module_state);
      priv->module_state = NULL;
    }
  if (priv->file_path)
    {
      g_free (priv->file_path);
      priv->file_path = NULL;
    }
  if (priv->soup_session)
    {
      g_object_unref (priv->soup_session);
      priv->soup_session = NULL;
    }

  G_OBJECT_CLASS (gtr_dl_teams_parent_class)->dispose (object);
}

static void
gtr_dl_teams_finalize (GObject *object)
{
  G_OBJECT_CLASS (gtr_dl_teams_parent_class)->finalize (object);
}

static void
gtr_dl_teams_class_init (GtrDlTeamsClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->finalize = gtr_dl_teams_finalize;
  object_class->dispose = gtr_dl_teams_dispose;

  gtk_widget_class_set_template_from_resource (widget_class,
                                               "/org/gnome/translator/gtr-dl-teams.ui");

  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, titlebar);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, main_box);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, file_label);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, stats_label);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, module_state_label);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, load_button);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, reserve_button);

  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, open_button);

  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, langs_combobox);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, modules_combobox);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, domains_combobox);
  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, branches_combobox);

  gtk_widget_class_bind_template_child_private (widget_class, GtrDlTeams, branches_model);
}

static char *
domains_expression (GtrDlTeamsDomain *domain)
{
  g_assert (GTR_IS_DL_TEAMS_DOMAIN (domain));
  return g_strdup (gtr_dl_teams_domain_description (domain));
}

static void
gtr_dl_teams_init (GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  gtk_widget_init_template (GTK_WIDGET (self));
  GtkExpression *expression = NULL;

  priv->soup_session = soup_session_new ();

  priv->main_window = NULL;
  priv->selected_lang = NULL;
  priv->selected_module = NULL;
  priv->selected_branch = NULL;
  priv->selected_domain = NULL;
  priv->file_path = NULL;
  priv->module_state = NULL;

  priv->domains_model = g_list_store_new (GTR_TYPE_DL_TEAMS_DOMAIN);
  gtk_drop_down_set_model (GTK_DROP_DOWN (priv->domains_combobox),
                           G_LIST_MODEL (priv->domains_model));

  gtk_widget_set_sensitive (priv->load_button, FALSE);
  gtk_widget_set_sensitive (priv->reserve_button, FALSE);

  /* Add combo boxes for DL teams and modules */
  expression = gtk_property_expression_new (GTR_TYPE_DROP_DOWN_OPTION, NULL, "description");
  priv->langs_model = g_list_store_new (GTR_TYPE_DROP_DOWN_OPTION);
  gtk_drop_down_set_model (
    GTK_DROP_DOWN (priv->langs_combobox),
    G_LIST_MODEL (priv->langs_model)
  );
  gtk_drop_down_set_expression (GTK_DROP_DOWN (priv->langs_combobox), expression);
  gtk_drop_down_set_enable_search (GTK_DROP_DOWN (priv->langs_combobox), TRUE);
  gtk_widget_set_sensitive (priv->langs_combobox, FALSE);

  expression = gtk_property_expression_new (GTR_TYPE_DROP_DOWN_OPTION, NULL, "name");
  priv->modules_model = g_list_store_new (GTR_TYPE_DROP_DOWN_OPTION);
  gtk_drop_down_set_model (
    GTK_DROP_DOWN (priv->modules_combobox),
    G_LIST_MODEL (priv->modules_model)
  );
  gtk_drop_down_set_expression (GTK_DROP_DOWN (priv->modules_combobox), expression);
  gtk_drop_down_set_enable_search (GTK_DROP_DOWN (priv->modules_combobox), TRUE);
  gtk_widget_set_sensitive (priv->modules_combobox, FALSE);

  g_signal_connect (priv->open_button,
                    "clicked",
                    G_CALLBACK (team_add_cb),
                    self);

  /* Connect "changed" to all combo boxes */
  g_signal_connect (priv->langs_combobox,
                    "notify::selected",
                    G_CALLBACK (gtr_dl_teams_save_combo_selected),
                    self);
  g_signal_connect (priv->modules_combobox,
                    "notify::selected",
                    G_CALLBACK (gtr_dl_teams_save_combo_selected),
                    self);
  g_signal_connect (priv->domains_combobox,
                    "notify::selected-item",
                    G_CALLBACK (gtr_dl_teams_save_combo_selected),
                    self);
  g_signal_connect (priv->branches_combobox,
                    "notify::selected-item",
                    G_CALLBACK (gtr_dl_teams_save_combo_selected),
                    self);

  /* Connect "click" on file button to load PO file */
  g_signal_connect (priv->load_button,
                    "clicked",
                    G_CALLBACK (gtr_dl_teams_load_po_file),
                    self);

  expression = gtk_cclosure_expression_new (G_TYPE_STRING,
                                            NULL,
                                            0, NULL,
                                            (GCallback)domains_expression,
                                            NULL, NULL);
  gtk_drop_down_set_expression (GTK_DROP_DOWN (priv->domains_combobox), expression);
}

GtrDlTeams*
gtr_dl_teams_new (GtrWindow *window)
{
  GtrDlTeams *self = g_object_new (GTR_TYPE_DL_TEAMS, NULL);
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);

  priv->main_window = window;
  return self;
}

GtkWidget *
gtr_dl_teams_get_header (GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  return priv->titlebar;
}

// static functions
static void
team_add_cb (GtkButton   *btn,
             GtrDlTeams *self)
{
  GtrDlTeamsPrivate *priv = gtr_dl_teams_get_instance_private (self);
  GtrWindow *window = GTR_WINDOW (priv->main_window);
  gtr_open_file_dialog (window);
}

// Domains GObject
GtrDlTeamsDomain*
gtr_dl_teams_domain_new (const char *name, const char *description)
{
  GtrDlTeamsDomain *self = g_object_new (GTR_TYPE_DL_TEAMS_DOMAIN, NULL);
  GtrDlTeamsDomainPrivate *priv = gtr_dl_teams_domain_get_instance_private (self);

  priv->name = g_strdup (name);
  priv->description = g_strdup (description);

  return self;
}

const char*
gtr_dl_teams_domain_name (GtrDlTeamsDomain *domain)
{
  GtrDlTeamsDomainPrivate *priv = gtr_dl_teams_domain_get_instance_private (domain);
  return (const char*) priv->name;
}

const char*
gtr_dl_teams_domain_description (GtrDlTeamsDomain *domain)
{
  GtrDlTeamsDomainPrivate *priv = gtr_dl_teams_domain_get_instance_private (domain);
  return (const char*) priv->description;
}

static void
gtr_dl_teams_domain_dispose (GObject *object)
{
  GtrDlTeamsDomainPrivate *priv = gtr_dl_teams_domain_get_instance_private (GTR_DL_TEAMS_DOMAIN (object));
  if (priv->name)
    g_free (priv->name);
  if (priv->description)
    g_free (priv->description);

  G_OBJECT_CLASS (gtr_dl_teams_domain_parent_class)->dispose (object);
}

static void
gtr_dl_teams_domain_class_init (GtrDlTeamsDomainClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  object_class->dispose = gtr_dl_teams_domain_dispose;
}

static void
gtr_dl_teams_domain_init (GtrDlTeamsDomain *self)
{
  GtrDlTeamsDomainPrivate *priv = gtr_dl_teams_domain_get_instance_private (self);

  priv->name = NULL;
  priv->description = NULL;
}
