#include <config.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkkeysyms-compat.h>

#include "elist.h"
#include "gtk_util.h"
#include "mru.h"
#include "prefs.h"
#include "file_list.h"
#include "message_box.h"
#include "cursor.h"
#include "str_util.h"
#include "vorbis_file.h"
#include "vorbis_edit_field.h"

#include "vorbis_edit.h"


/* widgets */
static GtkWindow *w_main = NULL;
static GtkNotebook *nb_edit = NULL;
static GtkNotebook *nb_vorbis = NULL;
static GtkMenuItem *m_vorbis = NULL;
static GtkCheckMenuItem *m_vor_view_simple = NULL;
static GtkCheckMenuItem *m_vor_view_advanced = NULL;

static GtkEntry *ent_title = NULL;
static GtkEntry *ent_artist = NULL;
static GtkEntry *ent_album = NULL;
static GtkEntry *ent_year = NULL;
static GtkEntry *ent_comment = NULL;
static GtkEntry *ent_track = NULL;
static GtkComboBoxText *combo_genre = NULL;
static GtkLabel *lab_title = NULL;
static GtkLabel *lab_artist = NULL;
static GtkLabel *lab_album = NULL;
static GtkLabel *lab_year = NULL;
static GtkLabel *lab_comment = NULL;
static GtkLabel *lab_track = NULL;
static GtkLabel *lab_genre = NULL;
static GtkLabel *lab_advanced = NULL;
static GtkButton *b_advanced = NULL;
static GtkButton *b_clear = NULL;
static GtkButton *b_write = NULL;

static GtkTreeView *tv_comments = NULL;
static GtkListStore *store_comments = NULL;
static GtkToolButton *b_add = NULL;
static GtkToolButton *b_edit = NULL;
static GtkToolButton *b_remove = NULL;
static GtkButton *b_simple = NULL;

/* the various notebook tabs */
#define TAB_SIMPLE 0
#define TAB_ADVANCED 1

/* private data */
static int tab_edit_vorbis;
static gboolean changed_flag = TRUE;
static gboolean editable_flag = TRUE;
static gboolean ignore_changed_signals = FALSE;
static int extra_fields = 0;
static vorbis_file *file = NULL;

/* preferences */
static MRUList *genre_mru;
static int *current_tab;


/*** private functions ******************************************************/

static void tree_view_setup()
{
	GtkTreeViewColumn *col;
	GtkCellRenderer *renderer;

	/* model */
	store_comments = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);

	/* columns and renderers */
	col = gtk_tree_view_column_new();
	gtk_tree_view_column_set_title(col, _("Field"));
	gtk_tree_view_append_column(tv_comments, col);

	renderer = gtk_cell_renderer_text_new();
	g_object_set(renderer, "ypad", 0, NULL);
	gtk_tree_view_column_pack_start(col, renderer, FALSE);
	gtk_tree_view_column_add_attribute(col, renderer, "text", 0);

	col = gtk_tree_view_column_new();
	gtk_tree_view_column_set_title(col, _("Text"));
	gtk_tree_view_append_column(tv_comments, col);

	renderer = gtk_cell_renderer_text_new();
	g_object_set(renderer, "ypad", 0, NULL);
	gtk_tree_view_column_pack_start(col, renderer, FALSE);
	gtk_tree_view_column_add_attribute(col, renderer, "text", 1);

	/* selection mode */
	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(tv_comments),
				    GTK_SELECTION_SINGLE);
}

static void set_changed_flag(gboolean value)
{
	if (changed_flag == value)
		return;

	changed_flag = value;
	gtk_widget_set_sensitive(GTK_WIDGET(b_write), value);
}

static void set_editable_flag(gboolean value)
{
	if (editable_flag == value)
		return;

	editable_flag = value;

	/* simple edit controls */
	gtk_widget_set_sensitive(GTK_WIDGET(ent_title), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_title), value);
	gtk_widget_set_sensitive(GTK_WIDGET(ent_artist), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_artist), value);
	gtk_widget_set_sensitive(GTK_WIDGET(ent_album), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_album), value);
	gtk_widget_set_sensitive(GTK_WIDGET(ent_year), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_year), value);
	gtk_widget_set_sensitive(GTK_WIDGET(ent_comment), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_comment), value);
	gtk_widget_set_sensitive(GTK_WIDGET(combo_genre), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_genre), value);
	gtk_widget_set_sensitive(GTK_WIDGET(ent_track), value);
	gtk_widget_set_sensitive(GTK_WIDGET(lab_track), value);
	gtk_widget_set_sensitive(GTK_WIDGET(b_clear), value);

	/* advanced edit controls */
	gtk_widget_set_sensitive(GTK_WIDGET(tv_comments), value);
	gtk_widget_set_sensitive(GTK_WIDGET(b_add), value);
	gtk_widget_set_sensitive(GTK_WIDGET(b_edit), value);
	gtk_widget_set_sensitive(GTK_WIDGET(b_remove), value);
}


static gboolean is_simple_field(char *name)
{
	static char *fields[] = {
		"title",
		"artist",
		"album",
		"date",
		"genre",
		"comment",
		"tracknumber",
		NULL
	};
	int i;

	for (i = 0; fields[i] != NULL; i++)
		if (strcmp(name, fields[i]) == 0)
			return TRUE;

	return FALSE;
}


static void count_extra_fields_callback(gchar *name, gchar *text, gpointer data)
{
	if (!is_simple_field(name))
		extra_fields++;
}

static void update_form_simple()
{
	const gchar *str;

	ignore_changed_signals = TRUE;

	if (g_elist_length(genre_mru->list) > 0) {
		gtk_combo_box_text_remove_all(combo_genre);
		g_list_foreach(GLIST(genre_mru->list), glist_2_combo, combo_genre);
	}

	vorbis_file_get_field(file, AF_TITLE, &str);   gtk_entry_set_text(ent_title, str);
	vorbis_file_get_field(file, AF_ARTIST, &str);  gtk_entry_set_text(ent_artist, str);
	vorbis_file_get_field(file, AF_ALBUM, &str);   gtk_entry_set_text(ent_album, str);
	vorbis_file_get_field(file, AF_YEAR, &str);    gtk_entry_set_text(ent_year, str);
	vorbis_file_get_field(file, AF_COMMENT, &str); gtk_entry_set_text(ent_comment, str);
	vorbis_file_get_field(file, AF_TRACK, &str);   gtk_entry_set_text(ent_track, str);
	vorbis_file_get_field(file, AF_GENRE, &str);   gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_genre))), str);
	ignore_changed_signals = FALSE;

	extra_fields = 0;
	g_hash_table_foreach(file->comments, (GHFunc)count_extra_fields_callback, NULL);

	if (extra_fields > 0) {
		char str[16];
		snprintf(str, 16, _("Advanced (%i)"), extra_fields);
		gtk_label_set_text(lab_advanced, str);
	}
	else {
		gtk_label_set_text(lab_advanced, _("Advanced"));
	}
}

static void append_row_callback(gchar *name, gchar *text, GtkListStore *store)
{
	GtkTreeIter iter;

	gtk_list_store_append(store, &iter);
	gtk_list_store_set(store, &iter, 0, name, 1, text, -1);
}

static void update_form_advanced()
{
	/* fill in the list */
	gtk_tree_view_set_model(tv_comments, NULL);
	gtk_list_store_clear(store_comments);

	g_hash_table_foreach(file->comments, (GHFunc)append_row_callback, store_comments);
	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store_comments), 0, GTK_SORT_ASCENDING);

	gtk_tree_view_set_model(tv_comments, GTK_TREE_MODEL(store_comments));

	gtk_widget_set_sensitive(GTK_WIDGET(b_edit), FALSE);
	gtk_widget_set_sensitive(GTK_WIDGET(b_remove), FALSE);
}

static void update_form()
{
	if (!file)
		return;

	if (*current_tab == TAB_SIMPLE)
		update_form_simple();
	else 
		update_form_advanced();
}


static void update_tag()
{
	if (!file || !changed_flag)
		return;

	if (*current_tab == TAB_SIMPLE) {
		vorbis_file_set_field(file, AF_TITLE, gtk_entry_get_text(ent_title));
		vorbis_file_set_field(file, AF_ARTIST, gtk_entry_get_text(ent_artist));
		vorbis_file_set_field(file, AF_ALBUM, gtk_entry_get_text(ent_album));
		vorbis_file_set_field(file, AF_YEAR, gtk_entry_get_text(ent_year));
		vorbis_file_set_field(file, AF_COMMENT, gtk_entry_get_text(ent_comment));
		vorbis_file_set_field(file, AF_TRACK, gtk_entry_get_text(ent_track));
		vorbis_file_set_field(file, AF_GENRE, gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_genre)))));
	}
	else {
		/* Nothing to do here, advanced mode alters tag directly */
	}
}


static void write_to_file()
{
	int res;
	const char *genre;

	cursor_set_wait();

	genre = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_genre))));
	if (strcmp(genre, "") != 0)
		mru_add(genre_mru, genre);

	res = vorbis_file_write_changes(file);
	if (res != AF_OK) {
		char msg[512];
		if (errno == EACCES || errno == EPERM) {
			snprintf(msg, sizeof(msg), 
				 _("Error saving file \"%s\":\n%s (%d)\n\n"
				   "Note:\n"
				   "In order to save changes to an Ogg Vorbis file, you must have\n"
				   "write permission for the directory where it is located."), 
				 file->name, strerror(errno), errno);
		}
		else {
			snprintf(msg, sizeof(msg),
				 _("Error saving file \"%s\":\n%s (%d)"), 
				 file->name, strerror(errno), errno);
		}

		message_box(w_main, _("Error Saving File"), msg, GTK_BUTTONS_OK, 0);
	}
	else {
		set_changed_flag(FALSE);
	}

	cursor_set_normal();
}



/* UI callbacks */
void cb_vor_clear(GtkButton *button, gpointer user_data)
{
	vorbis_file_remove_tag(file);
	update_form();

	set_changed_flag(TRUE);
}

void cb_vor_write(GtkButton *button, gpointer user_data)
{
	if (!file) {
		g_warning("file is not open!");
		return;
	}

	update_tag();
	write_to_file();
}

void cb_vor_view_simple(GtkWidget *widget, GdkEvent *event)
{
	if (*current_tab != TAB_SIMPLE) {
		update_tag();
		*current_tab = TAB_SIMPLE;
		update_form();
		gtk_notebook_set_current_page(nb_vorbis, *current_tab);
	}
}

void cb_vor_view_advanced(GtkWidget *widget, GdkEvent *event)
{
	if (*current_tab != TAB_ADVANCED) {
		update_tag();
		*current_tab = TAB_ADVANCED;
		update_form();
		gtk_notebook_set_current_page(nb_vorbis, *current_tab);
	}
}

void cb_vor_view_button(GtkButton *button, gpointer user_data)
{
	if (*current_tab == TAB_SIMPLE)
		gtk_check_menu_item_set_active(m_vor_view_advanced, TRUE);
	else if (*current_tab == TAB_ADVANCED)
		gtk_check_menu_item_set_active(m_vor_view_simple, TRUE);
}


/* callbacks for the simple editor */
void cb_vor_tag_changed(GObject *obj, gpointer user_data)
{
	if (!ignore_changed_signals)
		set_changed_flag(TRUE);
}

gboolean cb_vor_keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
	if (event->keyval == GDK_Return) {
		if (file && changed_flag) {
			update_tag();
			write_to_file();
		}
		fl_select_next_file();
		return TRUE;
	}

	return FALSE;
}

/* callbacks for the advanced editor */
void cb_vor_comment_selection_changed(GtkTreeSelection *selection, gpointer data)
{
	if (gtk_tree_selection_count_selected_rows(selection) == 0) {
		gtk_widget_set_sensitive(GTK_WIDGET(b_edit), FALSE);
		gtk_widget_set_sensitive(GTK_WIDGET(b_remove), FALSE);
	}
	else {
		gtk_widget_set_sensitive(GTK_WIDGET(b_edit), TRUE);
		gtk_widget_set_sensitive(GTK_WIDGET(b_remove), TRUE);
	}
}

void cb_vor_add(GtkButton *button, gpointer user_data)
{
	if ( vorbis_editfld_create_comment(file) ) {
		set_changed_flag(TRUE);
		update_form();
	}
}

void cb_vor_edit(GtkButton *button, gpointer user_data)
{
	GtkTreeModel *model;
	GtkTreeIter iter;

	if (gtk_tree_view_get_first_selected(tv_comments, &model, &iter)) {
		char *name;
		gtk_tree_model_get(model, &iter, 0, &name, -1);

		if ( vorbis_editfld_edit_comment(file, name) ) {
			set_changed_flag(TRUE);
			update_form();
		}
	}
}

void cb_vor_comment_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data)
{
	/* same as clicking the edit button */
	cb_vor_edit(NULL, NULL);
}

void cb_vor_remove(GtkButton *button, gpointer user_data)
{
	GtkTreeModel *model;
	GtkTreeIter iter;

	if (gtk_tree_view_get_first_selected(tv_comments, &model, &iter)) {
		char *name;
		gtk_tree_model_get(model, &iter, 0, &name, -1);

		/* Setting a comment to empty removes it */
		vorbis_file_set_field_by_name(file, name, "");
		set_changed_flag(TRUE);
		update_form();
	}
}


/*** public functions *******************************************************/

void vorbis_edit_load(vorbis_file *f)
{
	gtk_widget_show(GTK_WIDGET(m_vorbis));

	file = f;
	update_form();

	set_editable_flag(audio_file_is_editable((audio_file *)file));
	set_changed_flag(FALSE);

	gtk_notebook_set_current_page(nb_edit, tab_edit_vorbis);
	gtk_notebook_set_current_page(nb_vorbis, *current_tab);
}


void vorbis_edit_unload()
{
	if (changed_flag) {
		int button;

		button = message_box(w_main, _("Save Changes"), 
				     _("Vorbis Tag has been modified. Save changes?"),
				     GTK_BUTTONS_YES_NO, GTK_RESPONSE_YES);
		if (button == GTK_RESPONSE_YES) {
			/* save button was pressed */
			update_tag();
			write_to_file();
		}
	}

	file = NULL;

	if (GTK_IS_WIDGET(m_vorbis))
		gtk_widget_hide(GTK_WIDGET(m_vorbis));
}


void vorbis_edit_init(GtkBuilder *builder)
{
	GEList *temp_list;
	GEList *genre_list;
	int default_tab;

	/*
	 * get the widgets from glade
	 */

	w_main = GTK_WINDOW(gtk_builder_get_object(builder, "w_main"));
	nb_edit = GTK_NOTEBOOK(gtk_builder_get_object(builder, "nb_edit"));
	nb_vorbis = GTK_NOTEBOOK(gtk_builder_get_object(builder, "nb_vorbis"));
	m_vorbis = GTK_MENU_ITEM(gtk_builder_get_object(builder, "m_vorbis"));
	m_vor_view_simple = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(builder, "m_vor_view_simple"));
	m_vor_view_advanced = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(builder, "m_vor_view_advanced"));

	ent_title = GTK_ENTRY(gtk_builder_get_object(builder, "ent_vor_title"));
	ent_artist = GTK_ENTRY(gtk_builder_get_object(builder, "ent_vor_artist"));
	ent_album = GTK_ENTRY(gtk_builder_get_object(builder, "ent_vor_album"));
	ent_year = GTK_ENTRY(gtk_builder_get_object(builder, "ent_vor_year"));
	ent_comment = GTK_ENTRY(gtk_builder_get_object(builder, "ent_vor_comment"));
	ent_track = GTK_ENTRY(gtk_builder_get_object(builder, "ent_vor_track"));
	combo_genre = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(builder, "combo_vor_genre"));
	lab_title = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_title"));
	lab_artist = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_artist"));
	lab_album = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_album"));
	lab_year = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_year"));
	lab_comment = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_comment"));
	lab_track = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_track"));
	lab_genre = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_genre"));
	lab_advanced = GTK_LABEL(gtk_builder_get_object(builder, "lab_vor_advanced"));
	b_advanced = GTK_BUTTON(gtk_builder_get_object(builder, "b_vor_advanced"));
	b_clear = GTK_BUTTON(gtk_builder_get_object(builder, "b_vor_clear"));
	b_write = GTK_BUTTON(gtk_builder_get_object(builder, "b_vor_write"));

	tv_comments = GTK_TREE_VIEW(gtk_builder_get_object(builder, "tv_vor_comments"));
	b_add = GTK_TOOL_BUTTON(gtk_builder_get_object(builder, "b_vor_add"));
	b_edit = GTK_TOOL_BUTTON(gtk_builder_get_object(builder, "b_vor_edit"));
	b_remove = GTK_TOOL_BUTTON(gtk_builder_get_object(builder, "b_vor_remove"));
	b_simple = GTK_BUTTON(gtk_builder_get_object(builder, "b_vor_simple"));

	/* set up the tree view */
	tree_view_setup();
	g_signal_connect(gtk_tree_view_get_selection(tv_comments), "changed", 
			 G_CALLBACK(cb_vor_comment_selection_changed), NULL);

	/* find out which tab has the Vorbis interface */
	//tab_edit_vorbis = gtk_notebook_page_num(nb_edit, GTK_WIDGET(gtk_builder_get_object(builder, "nb_vorbis")));
	tab_edit_vorbis = 1; // FIXME: hardcoded since in Gtk3 the old method failed

	/*
	 * get the preference values, or set them to defaults
	 */

	/* genre_mru */
	temp_list = g_elist_new();
	genre_list = pref_get_or_set("common_edit:genre_mru", PREF_STRING | PREF_LIST, temp_list);
	g_elist_free_data(temp_list);
	genre_mru = mru_new_from_list(50, genre_list);

	/* current_tab */
	default_tab = TAB_SIMPLE;
	current_tab = pref_get_or_set("vorbis_edit:current_tab", PREF_INT, &default_tab);
	if (*current_tab != TAB_SIMPLE && *current_tab != TAB_ADVANCED)
		*current_tab = default_tab;

	if (*current_tab == TAB_SIMPLE)
		gtk_check_menu_item_set_active(m_vor_view_simple, TRUE);
	else
		gtk_check_menu_item_set_active(m_vor_view_advanced, TRUE);
}

