/* Jungle Monkey
 * Copyright (C) 1999-2001  The Regents of the University of Michigan
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */




#include "ggui_download.h"
#include "ggui.h"
#include "ggui_debug.h"
#include "ggui_files.h"
#include "ggui_properties.h"
#include "ggui_search.h"
#include "pixmaps.h"

#include "util/util.h"
#include "util/jmgnet.h"
#include "mtp/mtp_client.h"

#include <sys/types.h>
#include <errno.h>
#include <string.h>



/* ******************** */

typedef struct _Download
{
  GURL*	     url;
  gchar*     path;
  GSHA*	     sha;
  MtpClient* mtp_client;
  gboolean   have_file;

} Download;

typedef enum 
{
  IT_CANCEL_DOWNLOAD,
  IT_SEPARATOR1,
  IT_PROPERTIES,
} Item;


static GladeXML*    menu_xml;
static GtkCList*    download_clist;
static GtkWidget*   download_popup;

static Download*    selected_dl;
static GHashTable*  url_to_dl;


static void shutdown_hfunc (gpointer key, gpointer value, gpointer user_data);

static Download* download_new (const GURL* url, const gchar* p, const GSHA* sha);
#define          download_get(U) ((Download*) g_hash_table_lookup(url_to_dl, (U)))

static gchar* get_path (const gchar* p, const gchar* name);
static void file_func (MtpClient* client, MtpClientStatus status, gpointer user_data);
static void download_update (Download* dl);
static void download_delete (Download* dl);

void on_download_select_row(GtkCList* list, gint row, gint column, GdkEvent* event);
void on_download_unselect_row(GtkCList* list, gint row, gint column, GdkEvent* event);
gint on_download_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data);

static void toolbar_append (Item item, GdkPixmap* gpixmap, GdkBitmap* gbitmap,
			    const gchar* tooltip, void* func);
static void update_toolbar (void);

static void update_items (void* widget);
static void show_item (void* widget, Item it);



#define POPUPFUNC(F) void F(gpointer callback_data, guint callback_action, GtkWidget* widget)

POPUPFUNC(on_download_cancel_download);
POPUPFUNC(on_download_properties);



void
ggui_download_init (void)
{
  menu_xml = ggui_get_glade_xml("download_popup", "master_popup.glade");
  g_assert (menu_xml != NULL);
  glade_xml_signal_autoconnect(menu_xml);

  download_clist = GTK_CLIST(glade_xml_get_widget(jmw_xml, "jmdownloadclist"));
  download_popup = GTK_WIDGET(glade_xml_get_widget(menu_xml, "download_popup"));

  g_assert (download_clist != NULL);
  g_assert (download_popup != NULL);

  gtk_clist_column_title_passive (download_clist, 0);
  gtk_clist_column_title_passive (download_clist, 1);

  ggui_set_tooltip (GTK_WIDGET(download_clist), _("Files you are downloading."));

  url_to_dl   = g_hash_table_new (gnet_url_hash, gnet_url_equal);
  selected_dl = NULL;

  /* TODO: Restore incomplete downloads */
}


void
ggui_download_shutdown (void)
{
  /* TODO: Serialize incomplete downloads */

  g_hash_table_foreach (url_to_dl, shutdown_hfunc, NULL);
  g_hash_table_destroy (url_to_dl);
  url_to_dl = NULL;
  selected_dl = NULL;
}


static void
shutdown_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Download* dl = (Download*) value;
  download_delete (dl);
}


/* ******************** */

void
ggui_download_get (const GURL* url, const gchar* path, const GSHA* sha)
{
  Download* dl;
  
  g_return_if_fail (url);

  /* Ignore non-MTP */
  if (SAFESTRCMP (url->protocol, "mtp"))
    return;

  /* Download if we don't have it */
  dl = download_get (url);
  if (!dl)
    {
      dl = download_new (url, path, sha);
    }
  /* Redownload if we don't have it (there was an error before) */
  else if (!dl->mtp_client && !dl->have_file)
    {
      /* Redownload the file */
      if (dl->path)
	g_free (dl->path);
      dl->path = get_path (path, gnet_url_basename(url));
      g_return_if_fail (dl->path);

      dl->mtp_client = mtp_client_get_file (url, dl->path, file_func, dl);
    }
  /* Launch if we have it */
  else if (dl->path && dl->have_file)
    {
      GURL* url;

      url = gnet_url_new_fields ("file", "", 0, dl->path);
      ggui_activate (url);
      gnet_url_delete (url);
    }
  /* Otherwise, we are in the process of downloading - ignore */

  download_update (dl);
}


void
ggui_download_cancel (const GURL* url)
{
  Download* dl;
  int row;

  g_return_if_fail (url);

  /* Get download */
  dl = download_get (url);
  if (!dl)
    return;

  /* Remove from list */
  row = gtk_clist_find_row_from_data (download_clist, dl);
  gtk_clist_remove (download_clist, row);

  /* Delete */
  mtp_client_delete (dl->mtp_client);
  dl->mtp_client = NULL;
}


gint /* -1 not trans, -2 = down */
ggui_download_get_percent_complete (const GURL* url)
{
  Download* dl;

  g_return_val_if_fail (url, -1);

  dl = download_get (url);
  if (!dl)
    return -1;
  if (dl->have_file)
    return 100;
  if (!dl->mtp_client) /* !dl->have_file */
    return -2;
  /* Otherwise, we are downloading the file */

  if (dl->mtp_client->length == 0)
    return 100;

  return 100.0 * (((gdouble) dl->mtp_client->offset) / 
	  ((gdouble) dl->mtp_client->length));
}



/* ******************** */

static Download*
download_new (const GURL* url, const gchar* p, const GSHA* sha)
{
  gchar*     path;
  MtpClient* mtp_client;
  Download*  dl;
  gchar*     texts[2] = {NULL, NULL};
  gint	     row;

  g_return_val_if_fail (url, NULL);

  /* Get path */
  path = get_path (p, gnet_url_basename(url));
  g_return_val_if_fail (path, NULL);

  /* Create MTP client */
  mtp_client = mtp_client_get_file (url, path, file_func, NULL);
  g_return_val_if_fail (mtp_client, NULL);

  /* Create Download */
  dl       	 = g_new0(Download, 1);
  dl->url  	 = gnet_url_clone (url);
  dl->path 	 = path;
  if (sha) dl->sha = gnet_sha_clone (sha);
  dl->mtp_client = mtp_client;
  mtp_client->user_data = dl;
  g_hash_table_insert (url_to_dl, dl->url, dl);

  /* Add to clist */
  row = gtk_clist_prepend (download_clist, texts);
  gtk_clist_set_row_data (download_clist, row, dl);

  return dl;
}


static gchar*
get_path (const gchar* p, const gchar* name)
{
  gchar* dir;
  gchar* path;

  g_return_val_if_fail (name && *name,       NULL);
  g_return_val_if_fail (jm_home,       	     NULL);
  g_return_val_if_fail (is_okfilename(name), NULL);

  if (p && *p == '/' && G_DIR_SEPARATOR == '/' && is_okpathname(p))
    dir = g_strconcat (jm_home, p, NULL);
  else 
    dir = g_strdup (jm_home);

  if (!mkdirr(dir, 0700))
    {
      g_warning ("Could not create directory %s: %s\n", dir, strerror(errno));
      g_free (dir);
      return NULL;
    }

  /* Create the final path name.  Add directory separator if
     needed. */
  if (*dir && dir[strlen(dir) - 1] == G_DIR_SEPARATOR)
    path = g_strconcat (dir, name, NULL);
  else
    path = g_strconcat (dir, G_DIR_SEPARATOR_S, name, NULL);
  g_free (dir);

  return path;
}



static void 
file_func (MtpClient* client, MtpClientStatus status, gpointer user_data)
{
  Download* dl = (Download*) user_data;
  g_return_if_fail (dl);

  if (status == MTP_CLIENT_STATUS_OK)
    {
      gint row;

      if (dl->sha && client->sha)
	{
	  gchar shastr1[2 * GNET_SHA_HASH_LENGTH + 1];
	  gchar shastr2[2 * GNET_SHA_HASH_LENGTH + 1];

	  gnet_sha_copy_string (dl->sha, shastr1);
	  shastr1[2 * GNET_SHA_HASH_LENGTH] = '\0';
	  GGUIP (8, "SHA CHECK1 %s\n", shastr1);
	  gnet_sha_copy_string (client->sha, shastr2);
	  shastr2[2 * GNET_SHA_HASH_LENGTH] = '\0';
	  GGUIP (8, "SHA CHECK2 %s\n", shastr2);
	}

      /* Check SHA */
      if (dl->sha && client->sha && !gnet_sha_equal (dl->sha, client->sha))
	{
	  ggui_show_message ("warning", _("SHA mismatch: %s may be corrupted."),
			     dl->path);
	}

      /* Remove from clist */
      row = gtk_clist_find_row_from_data (download_clist, dl);
      gtk_clist_remove (download_clist, row);

      /* Delete client */
      mtp_client_delete (client);
      dl->mtp_client = NULL;
      dl->have_file = TRUE;

      download_update (dl);
    }
  else if (status == MTP_CLIENT_STATUS_UPDATE)
    {
      download_update (dl);
    }
  else
    {
      mtp_client_delete (client);
      dl->mtp_client = NULL;

      download_update (dl);
    }
}


static void
download_update (Download* dl)
{
  gint       percent;
  gint       row;
  gchar*     name;
  gchar*     str = NULL;
  GdkPixmap* pixmap;
  GdkBitmap* mask;

  g_return_if_fail (dl);

  /* Update file part */
  ggui_file_update (dl->url);
  ggui_search_update (dl->url);

  row = gtk_clist_find_row_from_data (download_clist, dl);
  if (row < 0)  /* Ignore if no longer in clist */
    return;

  name = gnet_url_basename (dl->url);
  g_return_if_fail (name);

  percent = ggui_download_get_percent_complete (dl->url);
  if (percent >= 0 && dl->mtp_client)
    {
      gchar* fraction;

      fraction = filesize_fraction_to_string(dl->mtp_client->offset,
					     dl->mtp_client->length);
        
      str = g_strdup_printf("%d%% (%s)", percent, fraction);
      g_free (fraction);
    }

  ggui_get_pixmaps (dl->url, &pixmap, &mask);
  ggui_clist_update_row2 (download_clist, row, name, str, pixmap, mask);
  g_free (str);
}


void
download_delete (Download* dl)
{
  g_return_if_fail (dl);

  gnet_url_delete (dl->url);
  g_free (dl->path);
  gnet_sha_delete (dl->sha);
  mtp_client_delete (dl->mtp_client);
  g_free (dl);
}



/* **************************************** */


void 
on_download_select_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
  selected_dl = (Download*) gtk_clist_get_row_data(list, row);
}


void 
on_download_unselect_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
  selected_dl = NULL;
}



/* Return TRUE if we handle event; FALSE to pass on */
gint
on_download_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data)
{
  gint row, column;
  gboolean rv = FALSE;

  /* We don't handle double-clicks */

  /* User right-clicked - show context menu */
  if (event->type == GDK_BUTTON_PRESS && event->button == 3)
    {
      GList* i;

      /* Select the clicked row, if there is one */
      if (gtk_clist_get_selection_info (list, event->x, event->y, &row, &column))
	gtk_clist_select_row (list, row, 0);
      else
	gtk_clist_unselect_all (list);

      /* Hide everything */
      for (i = GTK_MENU_SHELL(download_popup)->children; i != NULL; i = i->next)
	{
	  GtkWidget* menu_item = GTK_WIDGET(i->data);
	  g_return_val_if_fail (GTK_MENU_ITEM(menu_item) != NULL, FALSE);
	  gtk_widget_hide(menu_item);
	}

      /* Build the menu */
      if (selected_dl)
	{
	  update_items (NULL);

	  gtk_menu_popup (GTK_MENU(download_popup), NULL, NULL, 
			  NULL, NULL, 3, event->time);
	}

      rv = TRUE;
    }

  return rv;
}


void
ggui_download_show_toolbar (void)
{
  /* Add everything to the toolbar */
  toolbar_append (IT_CANCEL_DOWNLOAD, down_page_pixmap, down_page_mask, 
		  _("Cancel download"), on_download_cancel_download);

  /* Update the toolbar */
  update_toolbar();
}


static void
toolbar_append (Item item, GdkPixmap* gpixmap, GdkBitmap* gbitmap,
	       const gchar* tooltip, void* func)
{
  GtkPixmap* pixmap;				     	 
  GtkWidget* widget;				     	 

  pixmap = (GtkPixmap*) gtk_pixmap_new (gpixmap, gbitmap);
  g_return_if_fail (pixmap);                         	 
  widget = gtk_toolbar_append_item (jmw_toolbar, NULL,   
				    tooltip, "private",                 
				    GTK_WIDGET(pixmap),           
				    (GtkSignalFunc) func, NULL);     
  g_return_if_fail (widget);                             
  gtk_object_set_user_data (GTK_OBJECT(widget), (gpointer) item);
}


static void
update_toolbar (void)
{
  GList* kids;
  GList* i;

  /* Hide all */
  kids = gtk_container_children (GTK_CONTAINER(jmw_toolbar));
  for (i = kids; i != NULL; i = i->next)
    gtk_widget_set_sensitive (GTK_WIDGET(i->data), FALSE);

  /* Update toolbar */
  update_items (kids);

  g_list_free (kids);
}



/* **************************************** */

static const char* item_mi_name[] =
{
  "download_cancel_download_mi",
  "separator1",
  "properties_mi",
};

static void
update_items (void* widget)
{
  if (selected_dl)
    {
      if (selected_dl->mtp_client)
	show_item (widget, IT_CANCEL_DOWNLOAD);

      show_item (widget, IT_SEPARATOR1);
      show_item (widget, IT_PROPERTIES);
    }
}


static void
show_item (void* widget, Item it)
{
  if (!widget)	/* Popup menu */
    {
      GtkWidget* menu_item = glade_xml_get_widget(menu_xml, item_mi_name[it]);
      g_return_if_fail (menu_item != NULL);
      gtk_widget_show(menu_item);
    }
  else		/* Toolbar    */
    {
      GList* kids = (GList*) widget;
      GList* i;

      for (i = kids; i != NULL; i = i->next)		 
	{
	  if (it == (guint) gtk_object_get_user_data(GTK_OBJECT(i->data)))
	    {                                                  
	      gtk_widget_set_sensitive (GTK_WIDGET(i->data), TRUE); 
	      break;                                           
	    }                                                  
	}
    }
}


POPUPFUNC(on_download_cancel_download)
{
  g_return_if_fail (selected_dl);
  ggui_download_cancel (selected_dl->url);
}


POPUPFUNC(on_download_properties)
{
  g_return_if_fail (selected_dl);
  ggui_properties_show (selected_dl->url);
}


syntax highlighted by Code2HTML, v. 0.9.1