/*
     This file is part of GNUnet.
     Copyright (C) 2010, 2011, 2012 GNUnet e.V.

     GNUnet 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, or (at your
     option) any later version.

     GNUnet 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 GNUnet; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
*/

/**
 * @file src/fs/gnunet-fs-gtk_download-save-as.c
 * @brief functions for managing the 'save as' dialog
 * @author Christian Grothoff
 */
#include "gnunet-fs-gtk_download-save-as.h"
#include "gnunet-fs-gtk.h"
#include "gnunet-fs-gtk_event-handler.h"

/**
 * State for a 'save as' dialog of a download.
 */
struct DownloadAsDialogContext
{

  /**
   * Download entry for which this dialog determines the
   * filename (and other options).
   */
  struct DownloadEntry *de;

  /**
   * Builder for the dialog.
   */
  GtkBuilder *builder;

  /**
   * Main dialog object.
   */
  GtkWidget *dialog;

  /**
   * Name of the directory that should be unlinked if the operation is aborted,
   * can be NULL.
   */
  char *dirname;

  /**
   * Set to #GNUNET_YES if pressing "Save" button should initiate the download
   * right away, set to #GNUNET_NO if it should only change directory and file
   * names on the download panel.
   * Popup menu sets it to #GNUNET_YES, download panel "Change name" button sets
   * it to #GNUNET_NO.
   */
  int download_directly;
};


/**
 * Clean up the given DLC context, including possibly removing
 * the left-over (empty) directory.
 *
 * @param dlc context to clean up
 */
static void
clean_up_download_as_context (struct DownloadAsDialogContext *dlc)
{
  g_object_unref (G_OBJECT (dlc->builder));
  if (NULL != dlc->dirname)
  {
    if (0 != rmdir (dlc->dirname))
      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
                                "rmdir",
                                dlc->dirname);
    GNUNET_free (dlc->dirname);
  }
  GNUNET_free (dlc);
}


/**
 * Free resources associated with the given download entry.
 *
 * @param de context to free
 */
void
GNUNET_FS_GTK_free_download_entry (struct DownloadEntry *de)
{
  GtkTreePath *path;
  GtkTreeIter iter;

  GNUNET_assert (NULL == de->dc);
  if (NULL != de->sr)
  {
    GNUNET_assert (de->sr->download == de);
    de->sr->download = NULL;
    de->sr = NULL;
  }
  GNUNET_free (de->filename);
  GNUNET_FS_uri_destroy (de->uri);
  if (NULL != de->rr)
  {
    path = gtk_tree_row_reference_get_path (de->rr);
    GNUNET_assert (
      gtk_tree_model_get_iter (GTK_TREE_MODEL (downloads_treestore),
                               &iter,
                               path));
    gtk_tree_path_free (path);
    gtk_tree_row_reference_free (de->rr);
    de->rr = NULL;
    gtk_tree_store_remove (downloads_treestore, &iter);
  }
  GNUNET_free (de);
}


/**
 * The 'save_as' dialog is being deleted.  Clean up.
 *
 * @param widget the dialog object
 * @param event the deletion event
 * @param user_data the 'structDownloadAsDialogContext' of the dialog
 * @return always FALSE (destroy the window)
 */
gboolean
GNUNET_GTK_save_as_dialog_delete_event_cb (GtkWidget *widget,
                                           GdkEvent *event,
                                           gpointer user_data)
{
  struct DownloadAsDialogContext *dlc = user_data;

  GNUNET_FS_GTK_free_download_entry (dlc->de);
  clean_up_download_as_context (dlc);
  return FALSE;
}


/**
 * The user clicked on the 'save as' button of the dialog.
 * Delete the window and start the download.
 *
 * @param dialog the dialog object
 * @param response_id response_id associated with the button
 * @param user_data the 'structDownloadAsDialogContext' of the dialog
 */
void
GNUNET_GTK_save_as_dialog_response_cb (GtkDialog *dialog,
                                       gint response_id,
                                       gpointer user_data)
{
  struct DownloadAsDialogContext *dlc = user_data;
  struct DownloadEntry *de;
  GtkToggleButton *cb;

  de = dlc->de;
  if (GTK_RESPONSE_OK != response_id)
  {
    GNUNET_FS_GTK_free_download_entry (de);
    gtk_widget_destroy (GTK_WIDGET (dialog));
    clean_up_download_as_context (dlc);
    return;
  }
  GNUNET_free (de->filename);
  de->filename =
    GNUNET_GTK_filechooser_get_filename_utf8 (GTK_FILE_CHOOSER (dlc->dialog));
  cb = GTK_TOGGLE_BUTTON (
    gtk_builder_get_object (dlc->builder,
                            "GNUNET_GTK_save_as_recursive_check_button"));
  if (dlc->download_directly)
  {
    de->is_recursive =
      (TRUE == gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cb)))
        ? GNUNET_YES
        : GNUNET_NO;
    de->anonymity = (uint32_t) gtk_spin_button_get_value (GTK_SPIN_BUTTON (
      gtk_builder_get_object (dlc->builder,
                              "GNUNET_GTK_save_as_dialog_anonymity_spin_button")));
  }
  gtk_widget_destroy (GTK_WIDGET (dialog));
  if (dlc->download_directly)
    GNUNET_FS_GTK_download_context_start_download (de);
  else if (de->sr != NULL && de->sr->tab != NULL && de->sr->rr != NULL)
  {
    /* Update download panel */
    GNUNET_FS_GTK_set_item_downloaded_name (de->sr->tab->ts,
                                            de->sr->rr,
                                            de->filename);
    GNUNET_FS_GTK_free_download_entry (de);
  }
  /* we were successful, do not remove the directory (if we created one) */
  GNUNET_free (dlc->dirname);
  dlc->dirname = NULL;
  clean_up_download_as_context (dlc);
}


/*
** FIXME: implement handling for corner cases:
* A) Selection changes in search tab while SaveAs dialog is open.
*    If it's in direct download mode, ignore that; otherwise destroy the dialog.
* B) Download! is pressed in download panel (or the search result is otherwise
*    being downloaded without using saveas dialog) while SaveAs dialog is open.
*    Destroy SaveAs dialog.
*/
static void
open_saveas_dialog (struct DownloadEntry *de, int download_directly)
{
  struct DownloadAsDialogContext *dlc;
  GtkWidget *cb;
  GtkWidget *hb;

  dlc = GNUNET_new (struct DownloadAsDialogContext);
  dlc->de = de;
  dlc->download_directly = download_directly;
  dlc->builder =
    GNUNET_GTK_get_new_builder ("gnunet_fs_gtk_download_as_dialog.glade", dlc);
  if (NULL == dlc->builder)
  {
    GNUNET_break (0);
    GNUNET_FS_GTK_free_download_entry (de);
    GNUNET_free (dlc);
    return;
  }
  dlc->dialog = GTK_WIDGET (
    gtk_builder_get_object (dlc->builder, "GNUNET_GTK_save_as_dialog"));

  /* Enable recursive button for directories and
     set recursive 'default' value based on what the 'dc' tells us */
  cb = GTK_WIDGET (
    gtk_builder_get_object (dlc->builder,
                            "GNUNET_GTK_save_as_recursive_check_button"));
  hb = GTK_WIDGET (
    gtk_builder_get_object (dlc->builder,
                            "GNUNET_GTK_save_as_dialog_options_hbox"));
  if (download_directly)
  {
    if (GNUNET_YES == de->is_directory)
      gtk_widget_set_sensitive (cb, TRUE);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cb), de->is_recursive);
    gtk_widget_show (hb);
  }
  else
  {
    gtk_widget_hide (hb);
  }

  /* initialize filename based on filename from 'dc' */
  if (NULL != de->filename)
  {
    char *dirname;
    char *basename;
    struct stat sbuf;

    dirname = GNUNET_strdup (de->filename);
    basename = (char *) GNUNET_STRINGS_get_short_name (dirname);
    /* basename now points into 'dirname'; cut 'dirname' off at the '/' before
     * basename */
    if (basename > dirname)
      basename[-1] = '\0';

    if (0 != stat (dirname, &sbuf))
    {
      if (GNUNET_OK != GNUNET_DISK_directory_create (dirname))
      {
        GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "mkdir", dirname);
      }
      else
      {
        dlc->dirname = dirname;
      }
    }
    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlc->dialog),
                                         dirname);
    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dlc->dialog),
                                       basename);
    if (NULL == dlc->dirname)
      GNUNET_free (dirname);
  }
  gtk_dialog_set_default_response (GTK_DIALOG (dlc->dialog), GTK_RESPONSE_OK);
  gtk_window_present (GTK_WINDOW (dlc->dialog));
}


/**
 * Open the 'save as' dialog for a download.  Calls the 'dc->cb'
 * continutation when the dialog is complete.  Will release the 'dc'
 * resources if the dialog is cancelled.
 * Pressing 'Save' button will initiate the download.
 *
 * @param de download context for the file/directory
 */
void
GNUNET_FS_GTK_open_download_as_dialog (struct DownloadEntry *de)
{
  open_saveas_dialog (de, GNUNET_YES);
}


/**
 * Open the 'save as' dialog for a download.  Calls the 'dc->cb'
 * continutation when the dialog is complete.  Will release the 'dc'
 * resources if the dialog is cancelled.
 * Pressing 'Save' button will change selected directory and
 * file name in download panel, but will not initiate the download.
 *
 * @param de download context for the file/directory
 */
void
GNUNET_FS_GTK_open_change_download_name_dialog (struct DownloadEntry *de)
{
  open_saveas_dialog (de, GNUNET_NO);
}


/**
 * Actually start the download that is specified by the given download
 * context and its options.  Will add a download entry to the
 * respective tree model and trigger a start of the download using the
 * FS-API.
 *
 * @param de download context of the download to execute
 */
void
GNUNET_FS_GTK_download_context_start_download (struct DownloadEntry *de)
{
  enum GNUNET_FS_DownloadOptions opt;
  struct GNUNET_FS_Handle *fs;
  uint64_t len;

  fs = GNUNET_FS_GTK_get_fs_handle ();
  opt = GNUNET_FS_DOWNLOAD_OPTION_NONE;
  if (de->is_recursive)
    opt |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
  len = GNUNET_FS_uri_chk_get_file_size (de->uri);
  if ((NULL != de->sr) && (NULL != de->sr->result))
  {
    GNUNET_break (NULL !=
                  GNUNET_FS_download_start_from_search (fs,
                                                        de->sr->result,
                                                        de->filename,
                                                        NULL /* tempname */,
                                                        0 /* offset */,
                                                        len,
                                                        de->anonymity,
                                                        opt,
                                                        de));
  }
  else
  {
    GNUNET_break (
      NULL !=
      GNUNET_FS_download_start (fs,
                                de->uri,
                                NULL /* meta data */,
                                de->filename,
                                NULL /* tempname */,
                                0 /* offset */,
                                len,
                                de->anonymity,
                                opt,
                                de,
                                (NULL != de->pde) ? de->pde->dc : NULL));
  }
}


/* end of gnunet-fs-gtk-download.c */
