/* $Id: e2p_track.c 2064 2010-03-12 13:15:36Z tpgww $

Copyright (C) 2006-2010 tooar <tooar@emelfm2.net>

This file is part of emelFM2.
emelFM2 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.

emelFM2 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 emelFM2; see the file GPL. If not, see http://www.gnu.org/licenses.
*/

#include "emelfm2.h"
#include <string.h>
#include "e2_plugins.h"
#include "e2_dialog.h"

typedef struct _E2_TrackDlgData
{
	GtkWidget *dialog;
	GtkWidget *service_combo;
	GtkWidget *query_combo;
	GtkWidget *type_radio;	//1st member of search-type radio group
	GtkWidget *type_radio2;	//2nd member of search-type radio group
} E2_TrackDlgData;

//tracker stuff, from various tracker 0.6.0 headers
/*
enum {
	SERVICE_FILES,
	SERVICE_FOLDERS,
	SERVICE_DOCUMENTS,
	SERVICE_IMAGES,
	SERVICE_MUSIC,
	SERVICE_VIDEOS,
	SERVICE_TEXT_FILES,
	SERVICE_DEVELOPMENT_FILES,
	SERVICE_OTHER_FILES,
	SERVICE_VFS_FILES,
	SERVICE_VFS_FOLDERS,
	SERVICE_VFS_DOCUMENTS,
	SERVICE_VFS_IMAGES,
	SERVICE_VFS_MUSIC,
	SERVICE_VFS_VIDEOS,
	SERVICE_VFS_TEXT_FILES,
	SERVICE_VFS_DEVELOPMENT_FILES,
	SERVICE_VFS_OTHER_FILES,
	SERVICE_CONVERSATIONS,
	SERVICE_PLAYLISTS,
	SERVICE_APPLICATIONS,
	SERVICE_CONTACTS,
	SERVICE_EMAILS,
	SERVICE_EMAILATTACHMENTS,
	SERVICE_APPOINTMENTS,
	SERVICE_TASKS,
	SERVICE_BOOKMARKS,
	SERVICE_HISTORY,
	SERVICE_PROJECTS,
	MAXSERVICES
};
*/

#define ACTIVE_TRACKER_SERVICES 11

static gchar *cmd_str [ACTIVE_TRACKER_SERVICES] =
{	//these service names are hardcoded in tracker-files, and not translated
	//same order here as enum
//	NULL,	//not interested in Files
//	NULL,	//not interested in "Folders",
	"Documents",
	"Images",
	"Music",
	"Videos",
	"Text",
	"Development",
	"Other",
//	NULL,	//SERVICE_VFS_FILES,
//	NULL,	//SERVICE_VFS_FOLDERS,
//	NULL,	//SERVICE_VFS_DOCUMENTS,
//	NULL,	//SERVICE_VFS_IMAGES,
//	NULL,	//SERVICE_VFS_MUSIC,
//	NULL,	//SERVICE_VFS_VIDEOS,
//	NULL,	//SERVICE_VFS_TEXT_FILES,
//	NULL,	//SERVICE_VFS_DEVELOPMENT_FILES,
//	NULL,	//SERVICE_VFS_OTHER_FILES,
	"Conversations",	//"GaimConversations",
//	NULL,	//SERVICE_PLAYLISTS,
	"Applications",
//	NULL,	//SERVICE_CONTACTS,
	"Emails",	//"EvolutionEmails" "KMailEmails",
	"EmailAttachments",	//	"EvolutionAttachments" 	"KMailAttachments",
//	NULL,	//SERVICE_APPOINTMENTS,
//	NULL,	//SERVICE_TASKS,
//	NULL,	//SERVICE_BOOKMARKS,
//	NULL,	//SERVICE_HISTORY,
//	NULL,	//SERVICE_PROJECTS,
};

static gchar *object_names [ACTIVE_TRACKER_SERVICES] =
{ //these service strings are in same order as enum
//	N_("all files"),	//not actually interested in "Files", this is a proxy
//	NULL,	//not interested in "Folders",
	N_("office documents"),
	N_("images"),
	N_("music"),
	N_("videos"),
	N_("text files"),
	N_("development files"),
	N_("other files"),
//	NULL,	//SERVICE_VFS_FILES,
//	NULL,	//SERVICE_VFS_FOLDERS,
//	NULL,	//SERVICE_VFS_DOCUMENTS,
//	NULL,	//SERVICE_VFS_IMAGES,
//	NULL,	//SERVICE_VFS_MUSIC,
//	NULL,	//SERVICE_VFS_VIDEOS,
//	NULL,	//SERVICE_VFS_TEXT_FILES,
//	NULL,	//SERVICE_VFS_DEVELOPMENT_FILES,
//	NULL,	//SERVICE_VFS_OTHER_FILES,
	N_("conversations"),
//	NULL,	//N_("playlists") SERVICE_PLAYLISTS,
	N_("applications"),
//	NULL,	//N_("people") SERVICE_CONTACTS,
	N_("emails"),	//"EvolutionEmails" "KMailEmails",
	N_("email attachments"),	//"EvolutionAttachments" 	"KMailAttachments",
//	NULL,	//N_("appointments") SERVICE_APPOINTMENTS,
//	NULL,	//N_("tasks") SERVICE_TASKS,
//	NULL,	//N_("bookmarks") SERVICE_BOOKMARKS,
//	NULL,	//N_("notes")SERVICE_HISTORY,
//	NULL,	//N_("projects") SERVICE_PROJECTS,
};

static gint service_index = -1;	//undefined selection
static GList *query_history = NULL;

/**
@brief response callback for plugin selection dialog

@param dialog the dialog where the response was triggered
@param response the response assigned to the activated button widget
@param data ptr to plugins option data struct

@return
*/
static void _e2p_track_choose_response_cb (GtkDialog *dialog, gint response,
	E2_TrackDlgData *rt)
{
	if (response == E2_RESPONSE_USER1)
		return;	//ignore toggles of hidden-items
	if (response == GTK_RESPONSE_OK)
	{
		//returns localized string
		gchar *local = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
		if (local != NULL)
		{	//no conversion error
			gchar *utf = F_FILENAME_FROM_LOCALE (local);
			gtk_entry_set_text (
#ifdef USE_GTK2_14
				GTK_ENTRY (gtk_bin_get_child (GTK_BIN (rt->query_combo))),
#else
				GTK_ENTRY (GTK_BIN (rt->query_combo)->child),
#endif
				utf);
			g_free (local);
			F_FREE (utf, local);
		}
	}
	gtk_widget_destroy (GTK_WIDGET (dialog));
	gtk_widget_grab_focus (
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->query_combo))
#else
		GTK_BIN (rt->query_combo)->child
#endif
	);
}
/**
@brief bring up a system find-file window to choose a query

@param button UNUSED activated widget
@param rt ptr to dialog data struct

@return
*/
static void _e2p_track_choose_rdf_cb (GtkButton *button, E2_TrackDlgData *rt)
{
	//no need for vfs support here
	GtkWidget *dialog = gtk_file_chooser_dialog_new (NULL,
		GTK_WINDOW (rt->dialog), GTK_FILE_CHOOSER_ACTION_OPEN,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		GTK_STOCK_OPEN, GTK_RESPONSE_OK,
		NULL);

	e2_dialog_setup_chooser (dialog,
		_("choose query"),
		NULL,	//CWD
		GTK_FILE_CHOOSER_ACTION_OPEN,
		FALSE,	//hide-hidden
		FALSE,	//single-selection
		GTK_RESPONSE_OK);	//default response

	GtkFileFilter *filter = gtk_file_filter_new ();
	gtk_file_filter_set_name (GTK_FILE_FILTER (filter), _("rdf"));
	gtk_file_filter_add_pattern (filter, "*.rdf");
	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);

/*	//hide the dialog's standard 'open' button
	GtkContainer *bbox = GTK_CONTAINER (
#ifdef USE_GTK2_14
		gtk_dialog_get_action_area (GTK_DIALOG (dialog));
#else
		GTK_DIALOG (dialog)->action_area);
#endif
	GList* children = gtk_container_get_children (bbox);
	GtkWidget *btn = children->data;
	gtk_widget_hide (btn);
	g_list_free (children);
	//add 2 buttons that we want
	E2_Button no_btn;
	e2_button_derive (&no_btn, &E2_BUTTON_NO, BTN_NO_DEFAULT);

	e2_dialog_add_defined_button (dialog, &E2_BUTTON_?APPLY);
	e2_dialog_add_defined_button (dialog, &no_btn);
	e2_dialog_add_defined_button (dialog, &E2_BUTTON_APPLY);
*/
	g_signal_connect (G_OBJECT (dialog), "response",
		G_CALLBACK (_e2p_track_choose_response_cb), rt);

	e2_dialog_setup (dialog, rt->dialog);
	gtk_widget_show_all (dialog);
}
/*
 //get files with service (Documents, Music, Images, Videos, Text, Development, Other)
tracker-files -s service
 //get files with mime type(s)
tracker-files -m Mime1 [Mime2...]

 //search files for certain terms (ANDed)
tracker-search [OPTION...] search terms ... -
  -l, --limit=limit         limit the number of results showed
  -s, --service=service     search from a specific service

 //perform an rdf query and return results with specified metadata fields
tracker-query [OPTION...] RDFQueryFile [MetaDataField...] ...
  -s, --service=service             search from a specific service
  -t, --search-term=search-term     adds a fulltext search filter
  -k, --keyword=keyword             adds a keyword filter
*/
/**
@brief find and list matching items in output pane
This is called from a callback, BGL closed
@param rt pointer to data for dialog

@return
*/
static void _e2p_track_list_results (E2_TrackDlgData *rt)
{
	GtkWidget *entry =
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->query_combo));
#else
		GTK_BIN (rt->query_combo)->child;
#endif
	const gchar *find = gtk_entry_get_text (GTK_ENTRY (entry));
	gchar *command;
	gint result;

	if (
#ifdef USE_GTK2_14
		gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->type_radio))
#else
		(GTK_TOGGLE_BUTTON (rt->type_radio))->active
#endif
	)	//search for service
	{
		gint type = gtk_combo_box_get_active (GTK_COMBO_BOX (rt->service_combo));
		if (type == -1)
			return;
		if (*find == '\0'
			|| !strcmp (find, "*")
			|| !strcmp (find, _("all")))
			command = g_strdup_printf ("tracker-files -s %s", cmd_str[service_index]);
		else
			command = g_strdup_printf ("tracker-search -s %s %s",
				cmd_str[service_index], find);
	}
	else if (
#ifdef USE_GTK2_14
		gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->type_radio2))
#else
		(GTK_TOGGLE_BUTTON (rt->type_radio2))->active
#endif
	)	//search for mimetype
	{
		if (*find == '\0')
		{
			e2_output_print_end (&app.tab, FALSE);
			return;
		}
		command = g_strdup_printf ("tracker-files -m %s", find);
	}
	else
	{
		if (*find == '\0')
		{
			e2_output_print_end (&app.tab, FALSE);
			return;
		}
		command = g_strdup_printf ("tracker-query %s", find);
	}

	result = e2_command_run (command, E2_COMMAND_RANGE_DEFAULT, rt->dialog
#ifdef E2_COMMANDQ
	, TRUE
#endif
	);
	if (result == 0)
		e2_output_print_end (&app.tab, FALSE);

	g_free (command);
}
/**
@brief callback for dialog's "response" signal

@param dialog the dialog where the response was triggered
@param response the response for the clicked button
@param rt pointer to data for dialog

@return
*/
static void _e2p_track_response_cb (GtkDialog *dialog, gint response,
	E2_TrackDlgData *rt)
{
	switch (response)
	{
		case E2_RESPONSE_USER1:	//show help on using tracker
			e2_utils_show_help ("tracker plugin"); //no translation unless help doc is translated
			gtk_widget_grab_focus (
#ifdef USE_GTK2_14
				gtk_bin_get_child (GTK_BIN (rt->query_combo))
#else
				GTK_BIN (rt->query_combo)->child
#endif
			);
			break;
		case E2_RESPONSE_USER2:	//clear query entry
		{
			GtkWidget *entry =
#ifdef USE_GTK2_14
				gtk_bin_get_child (GTK_BIN (rt->query_combo));
#else
				GTK_BIN (rt->query_combo)->child;
#endif
			gtk_entry_set_text (GTK_ENTRY (entry), "");
			gtk_widget_grab_focus (entry);
		}
			break;
		case E2_RESPONSE_FIND:
			//in case the user aborts during the search, update history stuff now
			service_index = gtk_combo_box_get_active (GTK_COMBO_BOX (rt->service_combo));
			GtkWidget *entry =
#ifdef USE_GTK2_14
				gtk_bin_get_child (GTK_BIN (rt->query_combo));
#else
				GTK_BIN (rt->query_combo)->child;
#endif
			const gchar *find = gtk_entry_get_text (GTK_ENTRY (entry));
			if (*find != '\0')
				e2_list_update_history (find, &query_history, NULL, 30, FALSE);
			_e2p_track_list_results (rt);
			break;
		default:
			gtk_widget_destroy (rt->dialog);
			DEALLOCATE (E2_TrackDlgData, rt);
			break;
	}
}
/**
@brief handle activation (<Return> keypresses) in the query entry

@param entry UNUSED the entry widget for the combo box
@param rt pointer to dialog data struct
@return
*/
static void _e2p_track_query_activated_cb (GtkEntry *entry, E2_TrackDlgData *rt)
{
	_e2p_track_response_cb (GTK_DIALOG (rt->dialog), E2_RESPONSE_FIND, rt);
}
/**
@brief create and run dialog

@param from the button, menu item etc which was activated
@param art action runtime data

@return TRUE if action completed successfully, else FALSE
*/
static gboolean _e2p_track (gpointer from, E2_ActionRuntime *art)
{
	E2_TrackDlgData *rt = ALLOCATE (E2_TrackDlgData);
	CHECKALLOCATEDWARN (rt, return FALSE; );

	rt->dialog = e2_dialog_create (NULL, NULL, _("tracker query"),
		(ResponseFunc)_e2p_track_response_cb, rt);

	GtkWidget *dialog_vbox =
#ifdef USE_GTK2_14
		gtk_dialog_get_content_area (GTK_DIALOG (rt->dialog));
#else
		(GTK_DIALOG (rt->dialog))->vbox;
#endif
	GtkWidget *hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING,
		FALSE, FALSE, E2_PADDING_SMALL);
//	rt->type_radio = e2_button_add_radio (hbox, NULL, NULL, FALSE, 0, NULL, NULL);
//	e2_widget_add_label (hbox, _("Search for"), 0.0, 0.5, FALSE, 0);
	rt->type_radio = e2_button_add_radio (hbox, _("_Search for"), NULL,
		TRUE, FALSE, 0, NULL, NULL);

	rt->service_combo = e2_combobox_add (hbox, FALSE, E2_PADDING,
		NULL, NULL, NULL, E2_COMBOBOX_MENU_STYLE);
	guint i;
	for (i = 0; i < ACTIVE_TRACKER_SERVICES; i++)
		gtk_combo_box_append_text (GTK_COMBO_BOX (rt->service_combo),
			gettext (object_names[i]));
	gtk_combo_box_set_active (GTK_COMBO_BOX (rt->service_combo), service_index);
//USELESS? 	e2_widget_set_safetip (rt->service_combo, _("Select information service"));
	e2_widget_add_label (hbox, _("which match:"), 0.0, 0.5, FALSE, 0);

	hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE,
		E2_PADDING_SMALL);
	GSList *group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rt->type_radio));
//	e2_button_add_radio (hbox, NULL, group, FALSE, 0, NULL, NULL);
//	e2_widget_add_label (hbox, _("Search for items whose _mimetype is any of:"), 0.0, 0.5, FALSE, 0);
	rt->type_radio2 = e2_button_add_radio (hbox,
		_("Search for items whose _mimetype is any of:"), group,
		FALSE, FALSE, 0, NULL, NULL);

	hbox = e2_widget_add_box (dialog_vbox, TRUE, E2_PADDING, FALSE, FALSE,
		E2_PADDING_SMALL);
//	group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rt->type_radio));
	e2_button_add_radio (hbox, _("Search for items using this rdf query:"), group,
		FALSE, FALSE, 0, NULL, NULL);
	e2_button_add (hbox, FALSE, E2_PADDING_LARGE, _("_Select"), GTK_STOCK_OPEN,
			_("Open query-selection dialog"), _e2p_track_choose_rdf_cb, rt);

	rt->query_combo = e2_combobox_add (dialog_vbox, FALSE, E2_PADDING,
		(ActivateFunc)_e2p_track_query_activated_cb, rt, &query_history,
		E2_COMBOBOX_HAS_ENTRY | E2_COMBOBOX_FOCUS_ON_CHANGE | E2_COMBOBOX_CYCLE_HISTORY);

	GtkWidget *btn = e2_dialog_add_simple_button (rt->dialog, GTK_STOCK_HELP,
		_("_Help"), E2_RESPONSE_USER1);
	e2_widget_set_safetip (btn, _("Get help on constructing queries"));

	btn = e2_dialog_add_simple_button (rt->dialog, GTK_STOCK_CLEAR,
		_("C_lear"), E2_RESPONSE_USER2);
	e2_widget_set_safetip (btn, _("Clear the current query"));

	btn = e2_dialog_add_simple_button (rt->dialog, GTK_STOCK_FIND,
		_("_Find"), E2_RESPONSE_FIND);
	e2_widget_set_safetip (btn,
		_("Initiate a query using currently-specified criteria"));

	e2_dialog_set_negative_response (rt->dialog, GTK_RESPONSE_CLOSE);
	e2_dialog_show (rt->dialog, app.main_window, 0, &E2_BUTTON_CLOSE, NULL);
	gtk_widget_grab_focus (
#ifdef USE_GTK2_14
		gtk_bin_get_child (GTK_BIN (rt->query_combo))
#else
		GTK_BIN (rt->query_combo)->child
#endif
	);

	return TRUE;
}

//aname must be confined to this module
static gchar *aname;
/**
@brief plugin initialization function, called by main program

@param p ptr to plugin data struct

@return TRUE if the initialization succeeds, else FALSE
*/
gboolean init_plugin (Plugin *p)
{
#define ANAME "track"
	aname = _("track");

	p->signature = ANAME VERSION;
	p->menu_name = _("_Track..");
	p->description = _("Find items in the tracker database");
	p->icon = "plugin_"ANAME""E2ICONTB;  //use icon file pathname if appropriate

	if (p->action == NULL)
	{
		//don't free name string here
		E2_Action plugact =
		{g_strconcat (_A(1),".",aname,NULL),_e2p_track,FALSE,E2_ACTION_TYPE_ITEM,0,NULL,NULL};
		p->action = e2_plugins_action_register (&plugact);
		if G_LIKELY((p->action != NULL))
		{
			e2_cache_int_register ("track-plugin-type", &service_index, 0);
			e2_cache_list_register ("track-plugin-history", &query_history);
			return TRUE;
		}
		else
			g_free (plugact.name);
	}
	return FALSE;
}
/**
@brief cleanup transient things for this plugin

@param p pointer to data struct for the plugin

@return TRUE if all cleanups were completed
*/
gboolean clean_plugin (Plugin *p)
{
	gchar *action_name = g_strconcat (_A(1),".",aname,NULL);
	gboolean ret = e2_plugins_action_unregister (action_name);
	g_free (action_name);
	if (ret)
	{
		//backup the cache data
		e2_cache_unregister ("track-plugin-type");
		e2_cache_unregister ("track-plugin-history");
		//cleanup ?
	}
	return ret;
}
