#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>

#include "guiutils.h"
#include "desktop_icon.h"

#include "cfg.h"
#include "edv_types.h"
#include "recbin_dnd.h"
#include "recbin_desktop_icon.h"
#include "endeavour2.h"
#include "edv_cfg_list.h"
#include "edv_cb.h"
#include "edv_op.h"
#include "config.h"

#include "images/icon_trash_empty_48x48.xpm"
#include "images/icon_trash_48x48.xpm"
#include "images/icon_purge_all_20x20.xpm"


static void EDVRecBinDeskIconMapCB(gpointer di, gpointer data);
static void EDVRecBinDeskIconMovedCB(
	gpointer di, gint x, gint y, gpointer data
);

edv_recbin_desktop_icon_struct *EDVRecBinDeskIconNew(
	edv_core_struct *core
);
void EDVRecBinDeskIconUpdate(edv_recbin_desktop_icon_struct *rbdi);
void EDVRecBinDeskIconMap(edv_recbin_desktop_icon_struct *rbdi);
void EDVRecBinDeskIconUnmap(edv_recbin_desktop_icon_struct *rbdi);
void EDVRecBinDeskIconDelete(edv_recbin_desktop_icon_struct *rbdi);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


#if defined(PROG_LANGUAGE_SPANISH)
# define EDV_RECBIN_DESKICON_LABEL	"Cajón De Recirculación"
#elif defined(PROG_LANGUAGE_FRENCH)
# define EDV_RECBIN_DESKICON_LABEL	"Corbeille"
#elif defined(PROG_LANGUAGE_GERMAN)
# define EDV_RECBIN_DESKICON_LABEL	"Verwerten Sie Behälter"
#elif defined(PROG_LANGUAGE_ITALIAN)
# define EDV_RECBIN_DESKICON_LABEL	"Contenitore Per Raccolta"
#elif defined(PROG_LANGUAGE_DUTCH)
# define EDV_RECBIN_DESKICON_LABEL	"Recycl Bak"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
# define EDV_RECBIN_DESKICON_LABEL	"Caixa De Recycle"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
# define EDV_RECBIN_DESKICON_LABEL	"Resirkulasjons Bin"
#else
# define EDV_RECBIN_DESKICON_LABEL	"Recycle Bin"
#endif


/*
 *	Desktop icon map reference window callback.
 */
static void EDVRecBinDeskIconMapCB(gpointer di, gpointer data)
{
	edv_core_struct *core;
	edv_recbin_desktop_icon_struct *rbdi = EDV_RECBIN_DESKTOP_ICON(data);
	if(rbdi == NULL)
	    return;

	core = rbdi->core;
	if(core == NULL)
	    return;

	EDVMapRecBin(core);
}

/*
 *	Desktop icon moved callback.
 */
static void EDVRecBinDeskIconMovedCB(
	gpointer di, gint x, gint y, gpointer data
)
{
	edv_core_struct *core;
	edv_recbin_desktop_icon_struct *rbdi = EDV_RECBIN_DESKTOP_ICON(data);
	if(rbdi == NULL)
	    return;

	core = rbdi->core;
	if(core == NULL)
	    return;

	CFGItemListSetValueI(
	    core->cfg_list, EDV_CFG_PARM_RECBIN_DESKTOP_ICON_X,
	    x, FALSE
	);
	CFGItemListSetValueI(
	    core->cfg_list, EDV_CFG_PARM_RECBIN_DESKTOP_ICON_Y,
	    y, FALSE
	);
}


/*
 *	Creates a new recycle bin desktop icon.
 */
edv_recbin_desktop_icon_struct *EDVRecBinDeskIconNew(
	edv_core_struct *core
)
{
	gint x = 0, y = 0;
	GtkWidget *w;
	desktop_icon_struct *di;
	const GtkTargetEntry dnd_tar_types[] = {
{"text/plain",				0,	EDV_DND_TYPE_INFO_TEXT_PLAIN},
{"text/uri-list",			0,	EDV_DND_TYPE_INFO_TEXT_URI_LIST},
{"STRING",				0,	EDV_DND_TYPE_INFO_STRING}
	};
	const cfg_item_struct *cfg_list;
	edv_recbin_desktop_icon_struct *rbdi = EDV_RECBIN_DESKTOP_ICON(
	    g_malloc0(sizeof(edv_recbin_desktop_icon_struct))
	);
	if((rbdi == NULL) || (core == NULL))
	{
	    g_free(rbdi);
	    return(NULL);
	}

	cfg_list = core->cfg_list;
	if(cfg_list != NULL)
	{
	    x = CFGItemListGetValueI(
		cfg_list, EDV_CFG_PARM_RECBIN_DESKTOP_ICON_X
	    );
	    y = CFGItemListGetValueI(
		cfg_list, EDV_CFG_PARM_RECBIN_DESKTOP_ICON_Y
	    );
	}

	rbdi->core = core;
	rbdi->last_recbin_items = -1;


	/* Create the desktop icon */
	rbdi->deskicon = di = DeskIconNew(
	    x, y,
	    (guint8 **)icon_trash_empty_48x48_xpm,
	    EDV_RECBIN_DESKICON_LABEL
	);
	DeskIconSetFlags(
	    di,
	    DESKICON_CAN_SELECT | DESKICON_CAN_MOVE
	);
	DeskIconSetSensitive(di, TRUE);
	DeskIconSetMapRefCB(
	    di,
	    EDVRecBinDeskIconMapCB,
	    rbdi
	);
	DeskIconSetMovedCB(
	    di,
	    EDVRecBinDeskIconMovedCB,
	    rbdi
	);

	/* Set up the desktop icon's toplevel icon and label as
	 * as dnd target widgets
	 */
	/* Icon toplevel */
	w = (di != NULL) ? di->icon_toplevel : NULL;
	GUIDNDSetTar(
	    w,
	    dnd_tar_types,
	    sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
	    GDK_ACTION_MOVE,                    /* Actions */
	    GDK_ACTION_MOVE,                    /* Default action if same */
	    GDK_ACTION_MOVE,                    /* Default action */
	    EDVRecBinDeskIconDragDataReceivedCB,
	    rbdi
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "drag_motion",
	    GTK_SIGNAL_FUNC(EDVRecBinDeskIconDragMotionCB), rbdi
	);
	/* Label toplevel */
#if 0
/* Don't set the label as a dnd target widget, only allow user to
 * drop on the icon
 */
	w = (di != NULL) ? di->label_toplevel : NULL;
	GUIDNDSetTar(
	    w,
	    dnd_tar_types,
	    sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
	    GDK_ACTION_MOVE,                    /* Actions */
	    GDK_ACTION_MOVE,                    /* Default action if same */
	    GDK_ACTION_MOVE,                    /* Default action */
	    EDVRecBinDeskIconDragDataReceivedCB,
	    rbdi
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "drag_motion",
	    GTK_SIGNAL_FUNC(EDVRecBinDeskIconDragMotionCB), rbdi
	);
#endif

	/* Create menu */
	if(TRUE)
	{
	    GtkAccelGroup *accelgrp = NULL;
	    guint8 **icon_data;
	    const gchar *label;
	    gint accel_key;
	    guint accel_mods;
	    gpointer mclient_data;
	    void (*func_cb)(GtkWidget *, gpointer);
	    GtkWidget *menu = GUIMenuCreate();
	    GtkWidget **fw_rtn;

#define DO_ADD_MENU_ITEM_LABEL	\
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp, \
  icon_data, label, accel_key, accel_mods, \
  (gpointer *)fw_rtn, \
  mclient_data, func_cb \
 ); \
}
#define DO_ADD_MENU_SEP		\
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL, \
  NULL, NULL, 0, 0, NULL, \
  NULL, NULL \
 ); \
}
	    icon_data = NULL;
	    label = "Restore";
	    accel_key = 0;
	    accel_mods = 0;
	    fw_rtn = NULL;
	    mclient_data = core;
	    func_cb = EDVMapRecBinCB;
	    DO_ADD_MENU_ITEM_LABEL

	    DO_ADD_MENU_SEP

	    icon_data = (guint8 **)icon_purge_all_20x20_xpm;
	    label = "Purge All";
	    accel_key = 0;
	    accel_mods = 0;
	    fw_rtn = NULL;
	    mclient_data = core;
	    func_cb = EDVPurgeAllRecycledObjectsCB;
	    DO_ADD_MENU_ITEM_LABEL

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_SEP

	    DeskIconSetMenu(di, menu);
	}

	return(rbdi);
}

/*
 *	Updates the icon on the recycle bin desktop icon to reflect the
 *	current unmber of contents in the recycle bin.
 */
void EDVRecBinDeskIconUpdate(edv_recbin_desktop_icon_struct *rbdi)
{
	gint cur_recbin_items;
	gchar *label;
	desktop_icon_struct *di;
	edv_core_struct *core;
	guint8 **icon_data;


	if(rbdi == NULL)
	    return;

	core = rbdi->core;
	di = rbdi->deskicon;
	if((core == NULL) || (di == NULL))
	    return;

	/* Get current number of recycled objects from core structure */
	cur_recbin_items = core->last_recbin_items;

	/* Update recycle bin icon data, setting it to not NULL if it
	 * needs to be changed and update the recycle bin's
	 * last_recbin_items to match the value on the core structure
	 */
	icon_data = NULL;
	label = NULL;
	if(rbdi->last_recbin_items < 0)
	{
	    /* Unknown if there were contents before, so definately
	     * set a new icon
	     */
	    if(cur_recbin_items > 0)
	    {
		icon_data = (guint8 **)icon_trash_48x48_xpm;
		label = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
EDV_RECBIN_DESKICON_LABEL "\n%i Objeto%s"
#elif defined(PROG_LANGUAGE_FRENCH)
EDV_RECBIN_DESKICON_LABEL "\n%i Objet%s"
#elif defined(PROG_LANGUAGE_GERMAN)
EDV_RECBIN_DESKICON_LABEL "\n%i Objekt%s"
#elif defined(PROG_LANGUAGE_ITALIAN)
EDV_RECBIN_DESKICON_LABEL "\n%i L'Oggetto%s"
#elif defined(PROG_LANGUAGE_DUTCH)
EDV_RECBIN_DESKICON_LABEL "\n%i Voorwerp%s"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
EDV_RECBIN_DESKICON_LABEL "\n%i Objeto%s"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
EDV_RECBIN_DESKICON_LABEL "\n%i Objekt%s"
#else
EDV_RECBIN_DESKICON_LABEL "\n%i Object%s"
#endif
		    , cur_recbin_items,
		    (cur_recbin_items == 1) ? "" : "s"
		);
	    }
	    else
	    {
		icon_data = (guint8 **)icon_trash_empty_48x48_xpm;
		label = STRDUP(EDV_RECBIN_DESKICON_LABEL);
	    }
	}
	else
	{
	    /* Change in number of contents? */
	    if(rbdi->last_recbin_items != cur_recbin_items)
	    {
		if(cur_recbin_items > 0)
		{
		    icon_data = (guint8 **)icon_trash_48x48_xpm;
		    label = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
EDV_RECBIN_DESKICON_LABEL "\n%i Objeto%s"
#elif defined(PROG_LANGUAGE_FRENCH)
EDV_RECBIN_DESKICON_LABEL "\n%i Objet%s"
#elif defined(PROG_LANGUAGE_GERMAN)
EDV_RECBIN_DESKICON_LABEL "\n%i Objekt%s"
#elif defined(PROG_LANGUAGE_ITALIAN)
EDV_RECBIN_DESKICON_LABEL "\n%i L'Oggetto%s"
#elif defined(PROG_LANGUAGE_DUTCH)
EDV_RECBIN_DESKICON_LABEL "\n%i Voorwerp%s"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
EDV_RECBIN_DESKICON_LABEL "\n%i Objeto%s"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
EDV_RECBIN_DESKICON_LABEL "\n%i Objekt%s"
#else
EDV_RECBIN_DESKICON_LABEL "\n%i Object%s"
#endif
			, cur_recbin_items,
			(cur_recbin_items == 1) ? "" : "s"
		    );
		}
		else
		{
		    icon_data = (guint8 **)icon_trash_empty_48x48_xpm;
		    label = STRDUP(EDV_RECBIN_DESKICON_LABEL);
		}
	    }
	}
/*
printf("EDVRecBinDeskIconUpdate(): 0x%.8x %i\n", (guint)icon_data,
cur_recbin_items);
 */

	/* Update desktop icon and label */
	DeskIconSet(di, icon_data, label);

	/* Update number of recycled objects on the recycled desktop
	 * icon
	 */
	rbdi->last_recbin_items = cur_recbin_items;

	g_free(label);
}

/*
 *	Maps the recycle bin desktop icon.
 */
void EDVRecBinDeskIconMap(edv_recbin_desktop_icon_struct *rbdi)
{
	if(rbdi == NULL)
	    return;

	DeskIconMap(rbdi->deskicon);
}

/*
 *	Unmaps the recycle bin desktop icon.
 */
void EDVRecBinDeskIconUnmap(edv_recbin_desktop_icon_struct *rbdi)
{
	if(rbdi == NULL)
	    return;

	DeskIconUnmap(rbdi->deskicon);
}

/*
 *	Deallocates all resources of the given recycle bin desktop icon
 *	and deallocates the structure itself.
 */
void EDVRecBinDeskIconDelete(edv_recbin_desktop_icon_struct *rbdi)
{
	if(rbdi == NULL)
	    return;

	/* Delete the desktop icon */
	DeskIconDelete(rbdi->deskicon);
	rbdi->deskicon = NULL;

	/* Deallocate the structure itself */
	g_free(rbdi);
}
