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

Copyright (C) 2005-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.
*/

/**
@file plugins/e2p_upgrade.c
@brief plugin for updating config files when a new emelFM2 version so requires

This file contains functions that help upgrading the default configuration
data file to the current version. Note that upgrading may also be needed for
imported config data - refer to the config plugin
*/

#include "emelfm2.h"
#include <string.h>
#include "e2_plugins.h"
#include "e2_option.h"
#include "e2_output.h"
#include "e2_dialog.h"
#include "e2_task.h"
#include "e2_complete.h"

static gboolean cancelled = FALSE;

static gchar *default_msg =
	N_("Configuration arrangements for this version %s of %s are considerably "
	"different from those of old versions. To reliably ensure access to the "
	"program's current features, it is best to start with fresh settings.\n"
	"If you proceed, the superseded configuration files in\n %s will have '.save' "
	"appended to their names.\nFeel free to delete them."
	);
#if 0
static gchar *option_msg =
	N _ ("Several default configuration settings of this version %s of %s"
	" are different from those of recent versions (see changelog).\n"
	"If you click OK, those settings will be updated where possible.\n"
	"Or else you can Cancel, and later, via the configuration dialog, manually"
	"change individual settings, or change all settings to current defaults."
	);
#endif //0
static void _e2p_upgrade_reconfig (void)
{
	//prevent attempts to clean non-existent backup data
	guint i;
	gpointer *walker;
	E2_OptionSet *set;
	for (i = 0, walker = options_array->pdata; i < options_array->len; i++, walker++)
	{
		set = *walker;
		if (set->type == E2_OPTION_TYPE_TREE)
			set->ex.tree.def.tree_strings = NULL;	//CHECKME leak ?
	}
	//clear current option values
	e2_option_clear_data ();
	e2_option_default_register ();
	e2_option_file_read ();
}

//CHECKME assumes BGL is closed, native file only
static void _e2p_upgrade_backup (gchar *file)
{
	gchar *cfg_file = g_strdup_printf ("%s"G_DIR_SEPARATOR_S"%s",
		e2_cl_options.config_dir, file);
	gchar *local = F_FILENAME_TO_LOCALE (cfg_file);
#ifdef E2_VFS
	VPATH ddata = { local, NULL };
	if (e2_fs_access (&ddata, F_OK E2_ERR_NONE()) == 0)	//traverse link, if any
#else
	if (e2_fs_access (local, F_OK E2_ERR_NONE()) == 0)	//traverse link, if any
#endif
	{
		gchar *saved_file = g_strconcat (local, ".save", NULL);
		gdk_threads_leave ();	//downstream errors invoke local mutex locking
#ifdef E2_VFS
		VPATH sdata = { saved_file, NULL };
		e2_task_backend_rename (&ddata, &sdata);
#else
		e2_task_backend_rename (local, saved_file);
#endif
		gdk_threads_enter ();
		g_free (saved_file);
	}
	g_free (cfg_file);
	F_FREE (local, cfg_file);
}

static gboolean _e2p_upgrade_run (const gchar *command, const gchar *cfg_path)
{
	if (system (command) == 0)
		cancelled = FALSE;
	else
	{
		cancelled = TRUE;
		printd (DEBUG, "failed command '%s'", command);
		gchar *revert = g_strconcat ("mv -f ", cfg_path, ".save ", cfg_path, NULL);
		system (revert);
		g_free (revert);
	}
	return !cancelled;
}

static gint _e2p_upgrade_dialog (gchar *msg)
{
	//main button structs not created yet
	E2_Button yes_btn =
		{ _("_Apply"),
#ifdef E2_IMAGECACHE
	//FIXME gtk images not yet available
		NULL,
#else
		GTK_STOCK_YES,
#endif
		NULL, E2_BTN_DEFAULT, E2_BTN_DEFAULT, GTK_RESPONSE_YES };
	E2_Button no_btn =
		{ _("_Cancel"),
#ifdef E2_IMAGECACHE
	//FIXME gtk images not yet available
		NULL,
#else
		GTK_STOCK_NO,
#endif
		NULL, E2_BTN_DEFAULT, E2_BTN_DEFAULT, GTK_RESPONSE_NO };

	GtkWidget *dialog = e2_dialog_create (
#ifdef E2_IMAGECACHE
	//FIXME gtk images not yet available
		NULL,
#else
		GTK_STOCK_DIALOG_INFO,
#endif
		msg, _("update information"), DEFAULT_RESPONSE_CB, NULL);
	e2_dialog_show (dialog, NULL, 0,
		&yes_btn, &no_btn, NULL);
//	gint choice = e2_dialog_run_simple (dialog, NULL);
	//not a local loop, as main-window (for fake event) & options not yet available
	gint choice = gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);
	cancelled = (choice != GTK_RESPONSE_YES);
	return choice;
}
//#if 0
static gchar *_e2p_upgrade_get_sed (void)
{
	gchar *sed = g_find_program_in_path ("sed");
	if (sed == NULL)
	{
		//FIXME should always warn the user about this
		printd (ERROR, "can't find 'sed' so i can't upgrade the config file");
		cancelled = TRUE;
	}
	return sed;
}
//#endif
#ifdef E2_VERSIONDOCS
static void _e2p_upgrade_numbers (void)
{
	gchar *sed = _e2p_upgrade_get_sed ();
	if (sed != NULL)
	{
		gchar *cfg_file = g_build_filename (e2_cl_options.config_dir,
				default_config_file, NULL);
		gchar *local = F_FILENAME_TO_LOCALE (cfg_file);
		gchar *localtmp = e2_utils_get_tempname (local);
		gchar *command = g_strconcat ("cp -f ", local, " ", localtmp, ".save;",
			sed, " -e '1s/", app.cfgfile_version, "/"VERSION RELEASE"/'",
				 " -e '2,$s/0\\.[0-9]\\.[0-9]/"VERSION"/'",
				 " ",localtmp,".save >",local,NULL);
		if (!_e2p_upgrade_run (command, localtmp))
			printd (WARN, "failed to execute command to upgrade config version no's");
		g_free (sed);
		g_free (cfg_file);
		F_FREE (local, cfg_file);
		g_free (localtmp);
		g_free (command);
	}
	else
		cancelled = TRUE;
}
#endif

#ifdef E2_POLKIT
/**
@brief upgrade su-related strings
@return TRUE if the upgrade was done
*/
static gboolean _e2p_upgrade_su (void)
{
	gchar *sed = _e2p_upgrade_get_sed ();
	if (sed != NULL)
	{
		const gchar *prompt1 = _("Enter command:");
		const gchar *prompt2 = _("Done. Press enter ");
		//commandbar item
		gchar *oldstr1 = g_strconcat("\\|xterm\\|-e 'su -c \\\"%\\{\\(root-commands\\)@",prompt1,"\\}\\\";echo -n \\\"",prompt2,"\\\";read'",NULL);
		gchar *newstr1 = g_strconcat("|pkexec|%{(root-commands)@",prompt1,"}",NULL);
		//alias
		//2nd (.*) around "\2" so this can work (and be inserted in the replacement string)
		gchar *oldstr2 = g_strconcat("\\|xterm -e sh -c 'su -c \\\"(.*)\\\";echo -n \\\"",prompt2,"\\\";read'",NULL);
		const gchar *newstr2 = "|pkexec \\2";

		gchar *cfg_file = g_build_filename (e2_cl_options.config_dir,
			default_config_file, NULL);
		gchar *local = F_FILENAME_TO_LOCALE (cfg_file);
		gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;",
		sed,
			" -r"	//support back-referencing
			" -e \"s~(.*)",oldstr1,"~\\1",newstr1,"~\"",
			" -e \"s~(.*)",oldstr2,"~\\1",newstr2,"~\"",
			" ",local,".save >",local,NULL);
		gboolean success = _e2p_upgrade_run (command, local);
		g_free (oldstr1);
		g_free (newstr1);
		g_free (oldstr2);
		g_free (cfg_file);
		F_FREE (local, cfg_file);
		g_free (command);
		g_free (sed);
		if (success)
			return TRUE;
		printd (WARN, "failed to execute command to do su-upgrade");
	}
	cancelled = TRUE;
	return FALSE;
}
#endif

static void _e2p_upgrade_pre_0_1 (void)
{
	gchar *msg = g_strdup_printf (gettext (default_msg),
	VERSION, PROGNAME, e2_cl_options.config_dir);
	gint choice = _e2p_upgrade_dialog (msg);
	g_free (msg);

	if (choice == GTK_RESPONSE_YES)
	{
		_e2p_upgrade_backup ("config");
//		_e2p_upgrade_backup ("cache");  this one is ok to leave as is
		_e2p_upgrade_backup ("filetypes");
		_e2p_upgrade_backup ("plugins");
		_e2p_upgrade_backup ("settings");

		e2_option_clear_data ();
		e2_option_default_register ();
	}
	else
		exit (1);
}

static void _e2p_upgrade_0_4_1 (void)
{
	//get rid of outdated cache flags for find plugin, which hasn't been loaded yet
	e2_cache_clean1 ("find-plugin-flags");
}

static void _e2p_upgrade_0_4_5 (void)
{	//one dialog only !
	gchar *msg = g_strdup_printf (gettext (default_msg),
	VERSION, PROGNAME, e2_cl_options.config_dir);
	gint choice = _e2p_upgrade_dialog (msg);
	g_free (msg);

	if (choice == GTK_RESPONSE_YES)
		_e2p_upgrade_backup (default_config_file);
	else
		cancelled = TRUE;
}

static void _e2p_upgrade_0_5_1 (void)
{
	gchar *sed = _e2p_upgrade_get_sed ();
	if (sed != NULL)
	{
		//keybinding to add
		gchar *oldstr1 = g_strconcat("\t\t\t|<Control>i|false|",_A(7),".",_A(57),"|",NULL);
		gchar *newstr1 = g_strconcat("\t\t\t|<Control>d|false|",_A(7),".",_A(80),"|",NULL);

		gchar *cfg_file = g_build_filename (e2_cl_options.config_dir,
			default_config_file, NULL);
		gchar *local = F_FILENAME_TO_LOCALE (cfg_file);
		gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;",
		sed,
#ifndef E2_VERSIONDOCS
			" -e '1s/", app.cfgfile_version, "/", VERSION RELEASE, "/'",
#endif
			" -e '/",oldstr1,"$/a\\\n",newstr1,"'",
			" ",local,".save >",local,NULL);
		if (!_e2p_upgrade_run (command, local))
			printd (WARN, "failed to execute command to do upgrade 0.5.1");
		g_free (oldstr1);
		g_free (newstr1);
		g_free (cfg_file);
		F_FREE (local, cfg_file);
		g_free (command);
		g_free (sed);
	}
	else
		_e2p_upgrade_0_4_5 ();	//suggest default
}

static void _e2p_upgrade_0_5_1_1 (void)
{
	gchar *sed = _e2p_upgrade_get_sed ();
	if (sed != NULL)
	{
		//keybindings to change
		gchar *oldstr1 = g_strconcat(_A(10),".",_A(31),"|*,1",NULL);
		gchar *newstr1 = g_strconcat("!",_A(10),".",_A(31),"|1,*",NULL);
		gchar *oldstr2 = g_strconcat(_A(10),".",_A(31),"|*,0",NULL);
		gchar *newstr2 = g_strconcat("!",_A(10),".",_A(31),"|0,*",NULL);
		gchar *oldstr3 = g_strconcat(_A(14),".",_A(31),"|*,1",NULL);
		gchar *newstr3 = g_strconcat("!",_A(14),".",_A(31),"|0,*",NULL);
		gchar *oldstr4 = g_strconcat(_A(14),".",_A(31),"|*,0",NULL);
		gchar *newstr4 = g_strconcat("!",_A(14),".",_A(31),"|1,*",NULL);

		gchar *cfg_file = g_build_filename (e2_cl_options.config_dir,
			default_config_file, NULL);
		gchar *local = F_FILENAME_TO_LOCALE (cfg_file);
		gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;",
		sed,
#ifndef E2_VERSIONDOCS
			" -e '1s/", app.cfgfile_version, "/", VERSION RELEASE, "/'",
#endif
			" -e 's/",oldstr1,"/",newstr1,"/'",
			" -e 's/",oldstr2,"/",newstr2,"/'",
			" -e 's/",oldstr3,"/",newstr3,"/'",
			" -e 's/",oldstr4,"/",newstr4,"/'",
			" ",local,".save >",local,NULL);
		if (!_e2p_upgrade_run (command, local))
			printd (WARN, "failed to execute command to do upgrade 0.5.1.1");
		g_free (oldstr1);
		g_free (newstr1);
		g_free (oldstr2);
		g_free (newstr2);
		g_free (oldstr3);
		g_free (newstr3);
		g_free (oldstr4);
		g_free (newstr4);
		g_free (cfg_file);
		F_FREE (local, cfg_file);
		g_free (command);
		g_free (sed);
	}
	else
		_e2p_upgrade_0_4_5 ();	//suggest default
}

static void _e2p_upgrade_0_6_0 (void)
{
	//update key-shortcut translations
	E2_OptionSet *set = e2_option_get ("keybindings");
	if (set->ex.tree.synced) //this set's data processed already
	{
		GtkTreeIter iter;
		if (gtk_tree_model_get_iter_first (set->ex.tree.model, &iter))
			e2_keybinding_localise (set->ex.tree.model, &iter);
	}
	//else FIXME
}

static void _e2p_upgrade_0_7_2 (void)
{
	gchar *sed = _e2p_upgrade_get_sed ();
	if (sed != NULL)
	{
		//context menu item add
		gchar *oldstr1 = g_strconcat("\t",_("_Edit bookmarks.."),"|gtk-preferences|false|false|",_A(3),".",_C(1),"|",NULL);
		gchar *newstr1 = g_strconcat(_("_History"),"|gtk-jump-to|false|false|",_A(8),".",_A(27),"|",NULL);

		gchar *cfg_file = g_build_filename (e2_cl_options.config_dir,
			default_config_file, NULL);
		gchar *local = F_FILENAME_TO_LOCALE (cfg_file);
		gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;",
		sed,
#ifndef E2_VERSIONDOCS
			" -e '1s/", app.cfgfile_version, "/", VERSION RELEASE, "/'",
#endif
			" -e '/",oldstr1,"$/a\\\n",newstr1,"'",
			" ",local,".save >",local,NULL);
		if (!_e2p_upgrade_run (command, local))
			printd (WARN, "failed to execute command to do upgrade 0.7.2");
		g_free (oldstr1);
		g_free (newstr1);
		g_free (cfg_file);
		F_FREE (local, cfg_file);
		g_free (command);
		g_free (sed);
	}
	else
		_e2p_upgrade_0_4_5 ();	//suggest default
}

gboolean init_plugin (Plugin *p)
{
#define ANAME "uprade"
	p->signature = ANAME VERSION;

	if (strcmp (app.cfgfile_version,"0.1") < 0)
		//this one's major - dump the lot and start afresh
		_e2p_upgrade_pre_0_1();
	else
	{
//NOTE ensure that config plugin uses this same definition
#define OLDEST_UPGRADE "0.4.1.3"
		if (strcmp (app.cfgfile_version,"0.5.0") < 0)
		{
			_e2p_upgrade_0_4_1 ();	//change the find-plugin ABI
			_e2p_upgrade_0_4_5 ();
		}
		else
		{
#ifdef E2_VERSIONDOCS
			_e2p_upgrade_numbers ();	//always update docs version etc
#endif
#ifdef E2_POLKIT
			//this #define can be changed at any time
			cancelled = !_e2p_upgrade_su ();
#else
			cancelled = TRUE;
#endif
			if (strcmp (app.cfgfile_version,"0.5.1") < 0)
			{
				cancelled = FALSE; //may be changed downstream
				_e2p_upgrade_0_5_1 ();
			}
			if (strcmp (app.cfgfile_version,"0.5.1.1") < 0)
			{
				cancelled = FALSE;
				_e2p_upgrade_0_5_1_1 ();
			}
			if (strcmp (app.cfgfile_version,"0.7.2") < 0)
			{
				cancelled = FALSE;
				_e2p_upgrade_0_7_2 ();
			}
		}
		if (!cancelled)
			_e2p_upgrade_reconfig ();	//implement all updates

		//after any config-file-based updates, work on stored data ...

		if (strcmp (app.cfgfile_version,"0.6.0") < 0)
			_e2p_upgrade_0_6_0 ();

	}
	return TRUE;
}

gboolean clean_plugin (Plugin *p)
{
	return TRUE;
}
