/* GAdmin-Rsync - An easy to use GTK+ frontend for the rsync backup client and server.
 * Copyright (C) 2007-2011 Magnus Loef <magnus-swe@telia.com> 
 *
 * 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 3 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.
 *
*/


#include "../config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include "gettext.h"
#include "widgets.h"
#include "allocate.h"
#include "show_info.h"
#include "make_settings_entries.h"
#include "make_settings_buttons.h"
#include "make_settings_checkbuttons.h"
#include "restore_menu.h"
#include "backup_and_restore.h"
#include "cron_functions.h"
#include "commands.h"
#include "file_chooser.h"
//#include "key_handling.h"


GtkTreeIter iter_filesel;

extern gchar *global_key_path;
extern gchar *global_scripts_dir;
extern gchar *global_backup_name;


/* A source has been backed up to a destination.
 * Restore from this destination selection to the source.
 */


/* Duplicate of has_value() in backup_menu.c */
int has_restore_value(gchar *input)
{
    int ret = 0;

    if( input!=NULL && strlen(input) > 0 )
      ret = 1;

    return ret;
}


/* Change local source path entry with a file chooser dialog.
   Appends a slash to the end of the source path. */
void show_restore_local_src_filesel(struct w *widgets)
{
    G_CONST_RETURN gchar *path;
    gchar *new_path;

    path = gtk_entry_get_text(GTK_ENTRY(widgets->restore_src_path_entry));

//    new_path = get_local_dialog_path_selection((gchar *)path);
    new_path = g_strdup_printf("%s/", get_local_dialog_path_selection((gchar *)path));

    if( has_restore_value(new_path) )
      gtk_entry_set_text(GTK_ENTRY(widgets->restore_src_path_entry), new_path);

    if( new_path!=NULL )
      g_free(new_path);
}


void show_restore_local_dst_filesel(struct w *widgets)
{
    G_CONST_RETURN gchar *path;
    gchar *new_path;

    path = gtk_entry_get_text(GTK_ENTRY(widgets->restore_dst_path_entry));

    new_path = get_local_dialog_path_selection((gchar *)path);

    if( has_restore_value(new_path) )
      gtk_entry_set_text(GTK_ENTRY(widgets->restore_dst_path_entry), new_path);

    if( new_path!=NULL )
      g_free(new_path);
}


/* Show a restore menu window and fill it with
   backup options from the selected treeview. */
void show_restore_menu(struct w *widgets)
{
    /* Menu widgets */
    GtkWidget *frame1;
    GtkWidget *table1;
    GtkWidget *menu_vbox;
    GtkTooltips *tooltips;
    gchar *info, *tmp_path;
    int a=0, b=1;

    /* Treeview widgets */
    GtkTreeModel *model;
    GtkTreeIter iter;
    gboolean have_iter;
    gchar *src_server=NULL, *src_path=NULL, *dst_server=NULL, *dst_path=NULL;
    int i=0;

    /* Get some restore values from the treeview */
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widgets->backup_treeview));
    have_iter = gtk_tree_model_get_iter_first(model, &iter);
    if( ! have_iter )
    {
	info = g_strdup_printf(_("No valid backups to restore from could be found.\n"));
	show_info(info);
	if( info!=NULL )
	  g_free(info);

	return;
    }

    /* Get source server */
    gtk_tree_model_get(model, &iter, 0, &src_server, -1);

    /* Get source path */
    gtk_tree_model_get(model, &iter, 1, &src_path, -1);

    /* Get destination server */
    gtk_tree_model_get(model, &iter, 2, &dst_server, -1);

    /* Get destination path */
    gtk_tree_model_get(model, &iter, 3, &dst_path, -1);


    /* Create and show the restore window */
    widgets->restore_menu_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(widgets->restore_menu_window), GTK_WIN_POS_CENTER);
    gtk_widget_set_size_request(widgets->restore_menu_window, 600, -1);

    /* Set window information */
    info = g_strdup_printf(_("GAdmin-Rsync %s Restore from selection"), VERSION);
    gtk_window_set_title(GTK_WINDOW(widgets->restore_menu_window), info);
    g_free(info);

    menu_vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(widgets->restore_menu_window), menu_vbox);

    tooltips = gtk_tooltips_new();

    /* 1 table with some settings and 2 columns */
    table1 = gtk_table_new(2, 2, FALSE);

    frame1 = gtk_frame_new(_("Supply restore information:"));
    gtk_box_pack_start(GTK_BOX(menu_vbox), frame1, TRUE, TRUE, 1);
    gtk_container_add(GTK_CONTAINER(frame1), table1);


    /* Local to local restore, show file selectors for both src and dst paths */
    if( ! has_restore_value(src_server) && ! has_restore_value(dst_server) )
    {
	/* Source path selector */
	GtkWidget *src_path_button;
        widgets->restore_src_path_entry = gtk_entry_new();
        src_path_button = make_button_with_entry(GTK_TABLE(table1),
                                  widgets->restore_src_path_entry,
                                                 "gtk-refresh",
                                             _("Source path:"),
			_("Choose a source directory or file"),
						    0,1,a,b,350);
	a++; b++;
	g_signal_connect_swapped((gpointer)src_path_button, "clicked",
				G_CALLBACK(show_restore_local_src_filesel), widgets);

	/* Destination path selector */
	GtkWidget *dst_path_button;
        widgets->restore_dst_path_entry = gtk_entry_new();
        dst_path_button = make_button_with_entry(GTK_TABLE(table1),
                                  widgets->restore_dst_path_entry,
                                                 "gtk-refresh",
                                             _("Destination path:"),
			_("Choose a destination directory or file"),
						    0,1,a,b,350);
	a++; b++;
	g_signal_connect_swapped((gpointer)dst_path_button, "clicked",
				G_CALLBACK(show_restore_local_dst_filesel), widgets);
    }
    else  /* Local to remote backup. Remote to local restore, show local dest file selector. */
    if( ! has_restore_value(src_server) )
    {
	/* Source server entry */
	widgets->restore_src_server_entry = make_entry_with_label(GTK_TABLE(table1), _(" Source server: "), 0,1,a,b, 300);
	a++; b++;

	/* FIX: Remote file selector */
	widgets->restore_src_path_entry   = make_entry_with_label(GTK_TABLE(table1), _(" Source path: "), 0,1,a,b, 300);
	a++; b++;

	/* Dont show destination server entry */

	/* Destination path selector */
	GtkWidget *dst_path_button;
        widgets->restore_dst_path_entry = gtk_entry_new();
        dst_path_button = make_button_with_entry(GTK_TABLE(table1),
                                  widgets->restore_dst_path_entry,
                                                 "gtk-refresh",
                                             _("Local destination path:"),
			_("Choose a local destination directory or file"),
						    0,1,a,b,350);
	a++; b++;
	g_signal_connect_swapped((gpointer)dst_path_button, "clicked",
				G_CALLBACK(show_restore_local_dst_filesel), widgets);
    }
    else  /* Remote to local backup. Local to remote restore, show local src file selector. */
    if( ! has_restore_value(dst_server) )
    {
	/* Dont show source server entry */

	/* Source path selector */
	GtkWidget *src_path_button;
        widgets->restore_src_path_entry = gtk_entry_new();
        src_path_button = make_button_with_entry(GTK_TABLE(table1),
                                  widgets->restore_src_path_entry,
                                                    "gtk-refresh",
                                                _("Local source path:"),
			    _("Choose a source directory or file"),
							0,1,a,b,350);
	a++; b++;
	g_signal_connect_swapped((gpointer)src_path_button, "clicked",
				G_CALLBACK(show_restore_local_src_filesel), widgets);
    // ToolTip ???
    /* A source path should have a trailing "/" unless
       the last source directory name has the same name and
       contents as the last destination path or unless its a file. */

	/* Destination server entry */
	widgets->restore_dst_server_entry = make_entry_with_label(GTK_TABLE(table1), _(" Destination server: "), 0,1,a,b, 300);
	a++; b++;


	/* FIX: Remote file selector */
	widgets->restore_dst_path_entry = make_entry_with_label(GTK_TABLE(table1), _(" Destination path: "), 0,1,a,b, 300);
	a++; b++;
    }
    else /* Error... */
    {
	printf("Selection error.\n");
    }


    /* Add a checkbutton for dry-run (Test only) */
    widgets->restore_test_only_checkbutton = make_checkbutton_with_label(GTK_TABLE(table1),
					_("Test only, do not transfer any files: "), 0, 1, a, b);
    // Not selected by default. gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(widgets->restore_test_only_checkbutton), TRUE);
    a++; b++;


    /* Buttons "Cancel" and "Forward"*/
    GtkWidget *hbutton_box = gtk_hbutton_box_new();
    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbutton_box), GTK_BUTTONBOX_SPREAD);

    GtkWidget *cancel_button, *forward_button;
    cancel_button = gtk_button_new_from_stock(GTK_STOCK_QUIT);
    forward_button = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);

    gtk_box_pack_start(GTK_BOX(hbutton_box), cancel_button, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbutton_box), forward_button, FALSE, FALSE, 0);
    gtk_container_add(GTK_CONTAINER(menu_vbox), hbutton_box);

    /* Window exit signal */
    g_signal_connect(GTK_WINDOW(widgets->restore_menu_window), "delete_event",
                     G_CALLBACK(gtk_widget_destroy), NULL);

    /* Quit / Cancel button */
    g_signal_connect_swapped((gpointer)cancel_button, "clicked",
                    	    G_CALLBACK(gtk_widget_destroy),
                            GTK_OBJECT(widgets->restore_menu_window));

    /* Forward button signal */
    g_signal_connect_swapped((gpointer)forward_button, "clicked",
		G_CALLBACK(restore_menu_forward_clicked), widgets);


    /* Populate the menu with relevant server/path values from the treeview.
       Append a "/" last in the source path entry. */

    /* Set restore information in reverse order (src -> dst) */
    if( has_restore_value(src_server) )
    {
	gtk_entry_set_text(GTK_ENTRY(widgets->restore_dst_server_entry), src_server);
    }
    if( has_restore_value(src_path) )
    {
	gtk_entry_set_text(GTK_ENTRY(widgets->restore_dst_path_entry), src_path);
    }
    if( has_restore_value(dst_server) )
    {
	gtk_entry_set_text(GTK_ENTRY(widgets->restore_src_server_entry), dst_server);
    }
    if( has_restore_value(dst_path) )
    {
	/* Add dst_path + last part of src_path + "/" to the src_path_entry. */
	for(i=strlen(src_path)-1; src_path[i]!='\0'; i--)
	if( src_path[i]=='/' )
	{
//	  printf("Last dir in path: %s\n", &src_path[i]);
	  break;
	}

	/* Different paths based on dir/file or local/remote. */
	if( has_restore_value(src_server) || has_restore_value(dst_server) )
	  tmp_path = g_strdup_printf("%s%s/", dst_path, &src_path[i]); /* Remote src or dst. */
	else
	{
	    tmp_path = g_strdup_printf("%s%s/", dst_path, &src_path[i]); /* Local to local dir. */
	    if( ! file_exists(tmp_path) )
	    {
		g_free(tmp_path);
		tmp_path = g_strdup_printf("%s%s", dst_path, &src_path[i]); /* Local to local file. */
	    }
	}

	gtk_entry_set_text(GTK_ENTRY(widgets->restore_src_path_entry), tmp_path);
	g_free(tmp_path);
    }

    if( src_server!=NULL )
      g_free(src_server);
    if( src_path!=NULL )
      g_free(src_path);
    if( dst_server!=NULL )
      g_free(dst_server);
    if( dst_path!=NULL )
      g_free(dst_path);

    gtk_widget_show_all(widgets->restore_menu_window);
}


/* Restore menu forward clicked. Write the restore script
   from the restore menu and treeview then run it.
   Get paths from the menu, servers and keys etc from the treeview. */
void restore_menu_forward_clicked(struct w *widgets)
{
    FILE *fp;
    GtkTreeModel *model;
    GtkTreeIter iter;
    gboolean have_iter;
    gchar *script_file=NULL, *script=NULL, *script_part1=NULL;
    gchar *cmd, *info, *utf8;
    gchar *src_server=NULL, *dst_server=NULL;
    gchar *src_path=NULL, *dst_path=NULL;
    gchar *user=NULL, *port=NULL, *priv_key_path;
    GtkTextBuffer *text_buffer;

    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widgets->backup_treeview));
    have_iter = gtk_tree_model_get_iter_first(model, &iter);
    if( ! have_iter )
    {
	/* Show restore menu already shows a popup for this. */
	printf("No valid backups to restore from could be found.\n");
	return;
    }

    /* Get source server (as dst server) */
    gtk_tree_model_get(model, &iter, 0, &dst_server, -1);

    /* Get source path */
    src_path = g_strdup_printf("%s", gtk_entry_get_text(GTK_ENTRY(widgets->restore_src_path_entry)));

    /* Get destination server (as src server) */
    gtk_tree_model_get(model, &iter, 2, &src_server, -1);

    /* Get destination path */
    dst_path = g_strdup_printf("%s", gtk_entry_get_text(GTK_ENTRY(widgets->restore_dst_path_entry)));


    /* Any source path should have a trailing "/" unless
       the last source directory name has the same name and
       contents as the last destination path or unless its a file. */
    // FIX...
//    if( ! is_a_file(src_path) )
//    {
    // Cut from the last "/" in the src and dst paths.
    // If they dont have the same name, add a trailing slash to the source path.

    // Else...
    // If they have, compare the first dir entries in them.
    // If they are not the same, add a trailing slash to the source path.
//    }

    /* Get user */
    gtk_tree_model_get(model, &iter, 8, &user, -1);

    /* Get port */
    gtk_tree_model_get(model, &iter, 9, &port, -1);

    /* Get priv key path */
    gtk_tree_model_get(model, &iter, 10, &priv_key_path, -1);


    /* There must always be a source and a destination directory */
    if( ! has_restore_value(src_path) || ! has_restore_value(dst_path) )
    {
	info = g_strdup_printf(_("Error: The restore selection is missing a required source or destination path.\n"));
    	show_info(info);
    	g_free(info);
    }

    /* Rsync cant do server to server restores */
    if( has_restore_value(src_server) && has_restore_value(dst_server) )
    {
	info = g_strdup_printf(_("Error: Server to server restores are not supported by rsync.\n"));
    	show_info(info);
    	g_free(info);
    }

    /* Create a reverse restore command from the backup values... */

    /* Test only [x] / dry-run checkbutton */
    if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->restore_test_only_checkbutton)) )
      script_part1 = g_strdup_printf("%s", "#!/bin/sh\n\nrsync --dry-run --archive --progress --human-readable --verbose --stats");
    else
      script_part1 = g_strdup_printf("%s", "#!/bin/sh\n\nrsync --archive --progress --human-readable --verbose --stats");

    /* Local to Local restore. */
    if( ! has_restore_value(src_server) && ! has_restore_value(dst_server) )
    {
	script = g_strconcat(
	script_part1, " ",
	"'", src_path, "'", " '", dst_path, "'\n",
	NULL);
    }

    /* Local to Remote backup. Remote to Local restore. */
    if( has_restore_value(src_server) && ! has_restore_value(dst_server) )
    {
	script = g_strconcat(
	script_part1, " ",
	"-e \"ssh -l ", user, " -p ", port, " -i ", priv_key_path, "\" ",
	user, "@", src_server, ":'", src_path, "' ", "'", dst_path, "'\n",
	NULL);
    }

    /* Remote to Local backup. Local to Remote restore. */
    if( ! has_restore_value(src_server) && has_restore_value(dst_server) )
    {
	script = g_strconcat(
	script_part1, " ",
	"-e \"ssh -l ", user, " -p ", port, " -i ", priv_key_path, "\" ",
	"'", src_path, "' ", user, "@", dst_server, ":'", dst_path, "'\n",
	NULL);
    }

    if( script_part1!=NULL )
      g_free(script_part1);

    if( src_server!=NULL )
      g_free(src_server);
    if( dst_server!=NULL )
      g_free(dst_server);
    if( src_path!=NULL )
      g_free(src_path);
    if( dst_path!=NULL )
      g_free(dst_path);
    if( user!=NULL )
      g_free(user);
    if( port!=NULL )
      g_free(port);
    if( priv_key_path!=NULL )
      g_free(priv_key_path);

    /* Create a restore script path and write the restore script to it. */
    script_file = mk_restore_script_path();

    if((fp=fopen(script_file, "w+"))==NULL)
    {
	info = g_strdup_printf(_("Error: Can not restore script here:\n%s\n"), script_file);
        show_info(info);
        g_free(info);

	if( script!=NULL )
          g_free(script);
	if( script_file!=NULL )
          g_free(script_file);

	return;
    }
    fputs(script, fp);
    fclose(fp);

//    printf("Script: %s\n", script);

    if( script!=NULL )
      g_free(script);

    /* Chmod the script file to 755 so cron can run it. */
    cmd = g_strdup_printf("chmod 755 \"%s\"", script_file);
    if( ! run_command(cmd) )
    {
	info = g_strdup_printf(_("Error: Can not make the restore script executable.\n"));
        show_info(info);
        g_free(info);
        /* Dont return */
    }
    if( cmd!=NULL )
      g_free(cmd);

    /* Destroy the restore menu window */
    gtk_widget_destroy(widgets->restore_menu_window);

    /* Switch to the notebooks progress tab */
    gtk_notebook_set_current_page(GTK_NOTEBOOK(widgets->main_notebook), 1);

    /* Clear the progress textview and set informational text */
    info = g_strdup_printf("\nRestore operation starting, please wait...\n\n");
    utf8 = g_locale_to_utf8(info, strlen(info), NULL, NULL, NULL);
    text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widgets->progress_textview));
    gtk_text_buffer_set_text(text_buffer, utf8, -1);
    if( utf8!=NULL )
      g_free(utf8);

    /* Run the script */
    run_process_command(widgets, script_file);

    if( script_file!=NULL )
    {
	/* The script file cannot be unlinked here */
        g_free(script_file);
    }
}
