/*
 * Remmina - The GTK+ Remote Desktop Client
 * Copyright (C) 2009 - Vic Lee
 * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
 * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
 *
 * 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., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU General Public License in all respects
 *  for all of the code used other than OpenSSL. *  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so. *  If you
 *  do not wish to do so, delete this exception statement from your
 *  version. *  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <string.h>
#include "config.h"
#include "remmina_public.h"
#include "remmina_string_list.h"
#include "remmina/remmina_trace_calls.h"

static RemminaStringList *string_list;

#define COLUMN_DESCRIPTION 0
#define COLUMN_VALUE 1
#define GET_OBJECT(object_name) gtk_builder_get_object(string_list->builder, object_name)

/* Update the buttons state on the items in the TreeModel */
void remmina_string_list_update_buttons_state(void)
{
	gint items_count = gtk_tree_model_iter_n_children(
		GTK_TREE_MODEL(string_list->liststore_items), NULL);

	gtk_widget_set_sensitive(GTK_WIDGET(string_list->button_remove), items_count > 0);
	gtk_widget_set_sensitive(GTK_WIDGET(string_list->button_up), items_count > 1);
	gtk_widget_set_sensitive(GTK_WIDGET(string_list->button_down), items_count > 1);
}

/* Check the text inserted in the list */
void remmina_string_list_on_cell_edited(GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text)
{
	TRACE_CALL(__func__);
	gchar *text;
	gchar *error;
	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
	GtkTreeIter iter;

	gtk_tree_model_get_iter(GTK_TREE_MODEL(string_list->liststore_items), &iter, path);
	/* Remove delimitors from the string */
	text = remmina_public_str_replace(new_text, STRING_DELIMITOR, " ");
	if (cell == string_list->cellrenderertext_item1) {
		gtk_list_store_set(string_list->liststore_items, &iter, COLUMN_DESCRIPTION, text, -1);
	}else  {
		/* Check for validation only in second field */
		if (string_list->priv->validation_func) {
			if (!((*string_list->priv->validation_func)(text, &error))) {
				gtk_label_set_text(string_list->label_status, error);
				gtk_widget_show(GTK_WIDGET(string_list->label_status));
				g_free(error);
			}else  {
				gtk_widget_hide(GTK_WIDGET(string_list->label_status));
			}
		}
		gtk_list_store_set(string_list->liststore_items, &iter, COLUMN_VALUE, text, -1);
	}
	gtk_tree_path_free(path);
	g_free(text);
}

/* Move a TreeIter position */
static void remmina_string_list_move_iter(GtkTreeIter *from, GtkTreeIter *to)
{
	TRACE_CALL(__func__);
	GtkTreePath *path;

	gtk_list_store_swap(string_list->liststore_items, from, to);
	path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), from);
	gtk_tree_view_scroll_to_cell(string_list->treeview_items, path, NULL, 0, 0, 0);
	gtk_tree_path_free(path);
}

/* Move down the selected TreeRow */
void remmina_string_list_on_action_down(GtkWidget *widget, gpointer user_data)
{
	TRACE_CALL(__func__);
	GtkTreeIter iter;
	GtkTreeIter target_iter;

	if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
		gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &target_iter);
		if (gtk_tree_model_iter_next(GTK_TREE_MODEL(string_list->liststore_items), &target_iter)) {
			remmina_string_list_move_iter(&iter, &target_iter);
		}
	}
}

/* Move up the selected TreeRow */
void remmina_string_list_on_action_up(GtkWidget *widget, gpointer user_data)
{
	TRACE_CALL(__func__);
	GtkTreeIter iter;
	GtkTreeIter target_iter;
	GtkTreePath *path;

	if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
		gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &target_iter);
		path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), &target_iter);
		/* Before moving the TreeRow check if there’s a previous item */
		if (gtk_tree_path_prev(path)) {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(string_list->liststore_items), &target_iter, path);
			gtk_tree_path_free(path);
			remmina_string_list_move_iter(&iter, &target_iter);
		}
	}
}

/* Add a new TreeRow to the list */
void remmina_string_list_on_action_add(GtkWidget *widget, gpointer user_data)
{
	TRACE_CALL(__func__);
	GtkTreeIter iter;
	GtkTreePath *path;

	gtk_list_store_append(string_list->liststore_items, &iter);
	gtk_tree_selection_select_iter(string_list->treeview_selection, &iter);

	path = gtk_tree_model_get_path(GTK_TREE_MODEL(string_list->liststore_items), &iter);
	gtk_tree_view_set_cursor_on_cell(string_list->treeview_items, path,
		string_list->treeviewcolumn_item,
		GTK_CELL_RENDERER(string_list->priv->two_columns ? string_list->cellrenderertext_item1 : string_list->cellrenderertext_item2),
		TRUE);
	gtk_tree_path_free(path);
	remmina_string_list_update_buttons_state();
}

/* Remove the selected TreeRow from the list */
void remmina_string_list_on_action_remove(GtkWidget *widget, gpointer user_data)
{
	TRACE_CALL(__func__);
	GtkTreeIter iter;

	if (gtk_tree_selection_get_selected(string_list->treeview_selection, NULL, &iter)) {
		gtk_list_store_remove(string_list->liststore_items, &iter);
	}
	gtk_widget_hide(GTK_WIDGET(string_list->label_status));
	remmina_string_list_update_buttons_state();
}

/* Load a string list by splitting a string value */
void remmina_string_list_set_text(const gchar *text, const gboolean clear_data)
{
	TRACE_CALL(__func__);
	GtkTreeIter iter;
	gchar **items;
	gchar **values;
	gint i;
	/* Clear the data before to load new items */
	if (clear_data)
		gtk_list_store_clear(string_list->liststore_items);
	/* Split the string and insert each snippet in the string list */
	items = g_strsplit(text, STRING_DELIMITOR, -1);
	for (i = 0; i < g_strv_length(items); i++) {
		values = g_strsplit(items[i], string_list->priv->fields_separator, -1);
		gtk_list_store_append(string_list->liststore_items, &iter);
		if (g_strv_length(values) > 1) {
			/* Two columns data */
			gtk_list_store_set(string_list->liststore_items, &iter,
				COLUMN_DESCRIPTION, values[0],
				COLUMN_VALUE, values[1],
				-1);
		}else  {
			/* Single column data */
			gtk_list_store_set(string_list->liststore_items, &iter,
				COLUMN_VALUE, values[0],
				-1);
		}
		g_strfreev(values);
	}
	g_strfreev(items);
	remmina_string_list_update_buttons_state();
}

/* Get a string value representing the string list */
gchar* remmina_string_list_get_text(void)
{
	TRACE_CALL(__func__);
	GString *str;
	GtkTreeIter iter;
	gboolean first;
	gboolean ret;
	const gchar *item_description;
	const gchar *item_value;

	str = g_string_new(NULL);
	first = TRUE;
	/* Cycle each GtkTreeIter in the ListStore */
	ret = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(string_list->liststore_items), &iter);
	while (ret) {
		gtk_tree_model_get(GTK_TREE_MODEL(string_list->liststore_items), &iter,
			COLUMN_DESCRIPTION, &item_description,
			COLUMN_VALUE, &item_value,
			-1);
		if (!item_description)
			item_description = "";
		if (item_value && strlen(item_value) > 0) {
			/* Add a delimitor after the first element */
			if (!first) {
				g_string_append(str, STRING_DELIMITOR);
			}else  {
				first = FALSE;
			}
			/* Add the description for two columns list */
			if (string_list->priv->two_columns) {
				g_string_append(str, item_description);
				g_string_append(str, string_list->priv->fields_separator);
			}
			/* Add the element to the string */
			g_string_append(str, item_value);
		}
		ret = gtk_tree_model_iter_next(GTK_TREE_MODEL(string_list->liststore_items), &iter);
	}
	return g_string_free(str, FALSE);
}

/* Set a function that will be used to validate the new rows */
void remmina_string_list_set_validation_func(RemminaStringListValidationFunc func)
{
	TRACE_CALL(__func__);
	string_list->priv->validation_func = func;
}

/* Set the dialog titles */
void remmina_string_list_set_titles(gchar *title1, gchar *title2)
{
	/* Set dialog titlebar */
	gtk_window_set_title(GTK_WINDOW(string_list->dialog),
		(title1 && strlen(title1) > 0) ? title1 : "");
	/* Set title label */
	if (title2 && strlen(title2) > 0) {
		gtk_label_set_text(string_list->label_title, title2);
		gtk_widget_show(GTK_WIDGET(string_list->label_title));
	}else  {
		gtk_widget_hide(GTK_WIDGET(string_list->label_title));
	}
}

/* RemminaStringList initialization */
static void remmina_string_list_init(void)
{
	TRACE_CALL(__func__);
	string_list->priv->validation_func = NULL;
	/* When two columns are requested, show also the first column */
	if (string_list->priv->two_columns)
		gtk_cell_renderer_set_visible(GTK_CELL_RENDERER(string_list->cellrenderertext_item1), TRUE);
	remmina_string_list_update_buttons_state();
}

/* RemminaStringList instance */
GtkDialog* remmina_string_list_new(gboolean two_columns, const gchar *fields_separator)
{
	TRACE_CALL(__func__);
	string_list = g_new0(RemminaStringList, 1);
	string_list->priv = g_new0(RemminaStringListPriv, 1);

	string_list->builder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_string_list.glade");
	string_list->dialog = GTK_DIALOG(gtk_builder_get_object(string_list->builder, "DialogStringList"));

	string_list->liststore_items = GTK_LIST_STORE(GET_OBJECT("liststore_items"));
	string_list->treeview_items = GTK_TREE_VIEW(GET_OBJECT("treeview_items"));
	string_list->treeviewcolumn_item = GTK_TREE_VIEW_COLUMN(GET_OBJECT("treeviewcolumn_item"));
	string_list->treeview_selection = GTK_TREE_SELECTION(GET_OBJECT("treeview_selection"));
	string_list->cellrenderertext_item1 = GTK_CELL_RENDERER_TEXT(GET_OBJECT("cellrenderertext_item1"));
	string_list->cellrenderertext_item2 = GTK_CELL_RENDERER_TEXT(GET_OBJECT("cellrenderertext_item2"));
	string_list->button_add = GTK_BUTTON(GET_OBJECT("button_add"));
	string_list->button_remove = GTK_BUTTON(GET_OBJECT("button_remove"));
	string_list->button_up = GTK_BUTTON(GET_OBJECT("button_up"));
	string_list->button_down = GTK_BUTTON(GET_OBJECT("button_down"));
	string_list->label_title = GTK_LABEL(GET_OBJECT("label_title"));
	string_list->label_status = GTK_LABEL(GET_OBJECT("label_status"));

	/* Connect signals */
	gtk_builder_connect_signals(string_list->builder, NULL);
	/* Initialize the window and load the values */
	if (!fields_separator)
		fields_separator = STRING_DELIMITOR2;
	string_list->priv->fields_separator = fields_separator;
	string_list->priv->two_columns = two_columns;
	remmina_string_list_init();

	return string_list->dialog;
}
