/* gui.c
 *
 * Copyright (C) 2002 by Jason Day
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gdbm.h>

#include "gui.h"
#include "backup.h"
#include "libplugin.h"
#include "bprefs.h"


static const char RCSID[] = "$Id: gui.c,v 1.1.1.1 2003/02/04 18:01:30 rousseau Exp $";


/* Global variables */
static GtkWidget *active_clist;
static GtkWidget *inactive_clist;
static GtkWidget *backup_new_button;
static GtkWidget *every_sync_button;
static GtkWidget *daily_button;
static GtkWidget *weekly_button;
static GtkWidget *monthly_button;
static GtkWidget *archive_spinner;
static GtkWidget *persistent_archive_button;


/* Local static functions */
static void update_prefs_gui (void);
static void move_selected_item (GtkCList *src, GtkCList *dest);
static void move_all_items (GtkCList *src, GtkCList *dest);
static void cb_move_all_to_inactive (GtkWidget *widget, int data);
static void cb_move_to_inactive (GtkWidget *widget, int data);
static void cb_move_all_to_active (GtkWidget *widget, int data);
static void cb_move_to_active (GtkWidget *widget, int data);
static void cb_toggle_button (GtkWidget *widget, gpointer data);
static void cb_archives (GtkWidget *widget, gpointer data);


int init_gui (GtkWidget *vbox, GtkWidget *hbox, unsigned int unique_id) {
    GtkWidget *vbox1, *vbox2, *vbox0;
    GtkWidget *temp_hbox;
    GtkWidget *button;
    GtkWidget *label;
    GtkWidget *left_scrolled_window;
    GtkWidget *right_scrolled_window;
    GtkObject *adj;


    /* left and right main boxes */
    vbox1 = gtk_vbox_new(FALSE, 0);
    vbox2 = gtk_vbox_new(FALSE, 0);
    vbox0 = gtk_vbox_new(FALSE, 0);

    /* ----------------------------------- */
    /* Top portion of screen (preferences) */
    /* ----------------------------------- */

    /* Make a temporary hbox */
    temp_hbox = gtk_hbox_new (FALSE, 0);

    /* 'Backup new databases' button */
    backup_new_button = gtk_check_button_new_with_label ("Backup new databases");
    gtk_signal_connect (GTK_OBJECT (backup_new_button), "toggled",
                        GTK_SIGNAL_FUNC (cb_toggle_button),
                        GINT_TO_POINTER (BACKUP_NEW_BUTTON));
    gtk_box_pack_start (GTK_BOX(temp_hbox), backup_new_button, FALSE, FALSE, 5);

    /* Archive spinner */
    label = gtk_label_new ("Archives: ");
    gtk_box_pack_start (GTK_BOX(temp_hbox), label, FALSE, FALSE, 5);

    adj = gtk_adjustment_new (1.0, 1.0, MAX_ARCHIVES, 1.0, 5.0, 0.0);
    archive_spinner = gtk_spin_button_new (GTK_ADJUSTMENT(adj), 0, 0);
    gtk_signal_connect (adj, "value-changed",
                        GTK_SIGNAL_FUNC (cb_archives),
                        NULL);
    gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (archive_spinner), TRUE);
#ifndef ENABLE_GTK2
    gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (archive_spinner), GTK_SHADOW_OUT);
#endif
    gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (archive_spinner), TRUE);
    gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON(archive_spinner), GTK_UPDATE_IF_VALID);
    gtk_box_pack_start (GTK_BOX(temp_hbox), archive_spinner, FALSE, TRUE, 0);

    /* Persistent archive button */
    persistent_archive_button = gtk_check_button_new_with_label ("Persistent Archive");
    gtk_signal_connect (GTK_OBJECT (persistent_archive_button), "toggled",
                        GTK_SIGNAL_FUNC (cb_toggle_button),
                        GINT_TO_POINTER (PERSISTENT_ARCHIVE_BUTTON));
    gtk_box_pack_start (GTK_BOX(temp_hbox), persistent_archive_button, FALSE, FALSE, 10);


    gtk_box_pack_start (GTK_BOX(vbox0), temp_hbox, FALSE, FALSE, 5);

    gtk_box_pack_start (GTK_BOX(vbox0), gtk_hseparator_new(), FALSE, FALSE, 0);

    /* Backup how often radio buttons */
    temp_hbox = gtk_hbox_new (FALSE, 0);
    label = gtk_label_new ("Run Backup:");
    gtk_box_pack_start (GTK_BOX(temp_hbox), label, FALSE, FALSE, 5);

    every_sync_button = gtk_radio_button_new_with_label (NULL, "Every Sync");
    gtk_signal_connect (GTK_OBJECT (every_sync_button), "toggled",
                        GTK_SIGNAL_FUNC (cb_toggle_button),
                        GINT_TO_POINTER (EVERY_SYNC_BUTTON));
    gtk_box_pack_start (GTK_BOX (temp_hbox), every_sync_button, TRUE, FALSE, 0);
    daily_button = gtk_radio_button_new_with_label(
        gtk_radio_button_group (GTK_RADIO_BUTTON (every_sync_button)),
        "Daily");
    gtk_signal_connect (GTK_OBJECT (daily_button), "toggled",
                        GTK_SIGNAL_FUNC (cb_toggle_button),
                        GINT_TO_POINTER (DAILY_BUTTON));
    gtk_box_pack_start (GTK_BOX (temp_hbox), daily_button, TRUE, FALSE, 0);
    weekly_button = gtk_radio_button_new_with_label(
        gtk_radio_button_group (GTK_RADIO_BUTTON (daily_button)),
        "Weekly");
    gtk_signal_connect (GTK_OBJECT (weekly_button), "toggled",
                        GTK_SIGNAL_FUNC (cb_toggle_button),
                        GINT_TO_POINTER (WEEKLY_BUTTON));
    gtk_box_pack_start (GTK_BOX (temp_hbox), weekly_button, TRUE, FALSE, 0);
    monthly_button = gtk_radio_button_new_with_label(
        gtk_radio_button_group (GTK_RADIO_BUTTON (weekly_button)),
        "Monthly");
    gtk_signal_connect (GTK_OBJECT (monthly_button), "toggled",
                        GTK_SIGNAL_FUNC (cb_toggle_button),
                        GINT_TO_POINTER (MONTHLY_BUTTON));
    gtk_box_pack_start (GTK_BOX(temp_hbox), monthly_button, TRUE, FALSE, 0);

    gtk_box_pack_start (GTK_BOX(vbox0), temp_hbox, FALSE, FALSE, 5);
    gtk_box_pack_start (GTK_BOX(vbox0), gtk_hseparator_new(), FALSE, FALSE, 0);

    temp_hbox = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX(temp_hbox), vbox1, TRUE, TRUE, 5);
    gtk_box_pack_start (GTK_BOX(temp_hbox), vbox2, TRUE, TRUE, 5);
    gtk_box_pack_start (GTK_BOX(vbox0), temp_hbox, TRUE, TRUE, 5);
    gtk_box_pack_start (GTK_BOX(hbox), vbox0, TRUE, TRUE, 5);

    /* ------------------------------------------------------------- */
    /* Middle portion of screen (active and inactive database lists) */
    /* ------------------------------------------------------------- */

    /* ------------------- */
    /* Left half of screen */
    /* ------------------- */

    /* Make a temporary hbox */
    temp_hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox1), temp_hbox, FALSE, FALSE, 0);

    label = gtk_label_new("Databases to Backup");
    gtk_box_pack_start(GTK_BOX(temp_hbox), label, TRUE, TRUE, 0);

    /* Scrolled Window */
    left_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_set_border_width(GTK_CONTAINER(left_scrolled_window), 0);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(left_scrolled_window),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    gtk_box_pack_start(GTK_BOX(vbox1), left_scrolled_window, TRUE, TRUE, 0);

    gtk_widget_set_usize(GTK_WIDGET(vbox1), 230, 0);

    /* Active Clist */
    active_clist = gtk_clist_new(1);
    gtk_clist_set_selection_mode(GTK_CLIST(active_clist), GTK_SELECTION_SINGLE);
    gtk_clist_set_column_width(GTK_CLIST(active_clist), 0, 150);
    gtk_clist_set_auto_sort (GTK_CLIST(active_clist), TRUE);
    gtk_container_add(GTK_CONTAINER(left_scrolled_window), GTK_WIDGET(active_clist));
    gtk_object_set_user_data (GTK_OBJECT(active_clist), (gpointer)ACTIVE_DBM);

    temp_hbox = gtk_hbox_new(FALSE, 0);

    button = gtk_button_new_with_label("Move All >>");
    gtk_box_pack_start(GTK_BOX(temp_hbox), button, TRUE, FALSE, 10);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       GTK_SIGNAL_FUNC(cb_move_all_to_inactive), NULL);

    button = gtk_button_new_with_label("Move >>");
    gtk_box_pack_start(GTK_BOX(temp_hbox), button, TRUE, FALSE, 10);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       GTK_SIGNAL_FUNC(cb_move_to_inactive), NULL);

    gtk_box_pack_start (GTK_BOX(vbox1), temp_hbox, FALSE, FALSE, 0);

    /* -------------------- */
    /* Right half of screen */
    /* -------------------- */

    temp_hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox2), temp_hbox, FALSE, FALSE, 0);

    label = gtk_label_new("Databases to Ignore");
    gtk_box_pack_start(GTK_BOX(temp_hbox), label, TRUE, TRUE, 0);

    /* Scrolled Window */
    right_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_set_border_width(GTK_CONTAINER(right_scrolled_window), 0);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(right_scrolled_window),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_box_pack_start(GTK_BOX(vbox2), right_scrolled_window, TRUE, TRUE, 0);

    gtk_widget_set_usize(GTK_WIDGET(vbox2), 230, 0);

    /* Inactive Clist */
    inactive_clist = gtk_clist_new(1);
    gtk_clist_set_selection_mode(GTK_CLIST(inactive_clist), GTK_SELECTION_SINGLE);
    gtk_clist_set_column_width(GTK_CLIST(inactive_clist), 0, 150);
    gtk_clist_set_auto_sort (GTK_CLIST(inactive_clist), TRUE);
    gtk_container_add(GTK_CONTAINER(right_scrolled_window), GTK_WIDGET(inactive_clist));
    gtk_object_set_user_data (GTK_OBJECT(inactive_clist), (gpointer)INACTIVE_DBM);

    temp_hbox = gtk_hbox_new(FALSE, 0);

    button = gtk_button_new_with_label("<< Move");
    gtk_box_pack_start(GTK_BOX(temp_hbox), button, TRUE, FALSE, 10);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       GTK_SIGNAL_FUNC(cb_move_to_active), NULL);

    button = gtk_button_new_with_label("<< Move All");
    gtk_box_pack_start(GTK_BOX(temp_hbox), button, TRUE, FALSE, 10);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       GTK_SIGNAL_FUNC(cb_move_all_to_active), NULL);

    gtk_box_pack_start (GTK_BOX(vbox2), temp_hbox, FALSE, FALSE, 0);

    gtk_widget_show_all(hbox);
    gtk_widget_show_all(vbox);

    display_databases();
    update_prefs_gui();

    return 0;
}

int destroy_gui() {
    gtk_clist_clear (GTK_CLIST(active_clist));
    gtk_clist_clear (GTK_CLIST(inactive_clist));

    if (GTK_IS_WIDGET(active_clist)) {
        gtk_widget_destroy (active_clist);
    }
    if (GTK_IS_WIDGET(inactive_clist)) {
        gtk_widget_destroy (inactive_clist);
    }

    active_clist = NULL;
    inactive_clist = NULL;

    return 0;
}



static void update_prefs_gui() {
    long ivalue;

    backup_get_pref (BPREF_BACKUP_NEW, &ivalue, NULL);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (backup_new_button), ivalue);

    backup_get_pref (BPREF_NUM_ARCHIVES, &ivalue, NULL);
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (archive_spinner), ivalue);

    backup_get_pref (BPREF_PERSISTENT_ARCHIVE, &ivalue, NULL);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (persistent_archive_button), ivalue);

    backup_get_pref (BPREF_BACKUP_WHEN, &ivalue, NULL);
    switch (ivalue) {
        case EVERY_SYNC:
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (backup_new_button), TRUE);
            break;

        case DAILY:
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (daily_button), TRUE);
            break;

        case WEEKLY:
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (weekly_button), TRUE);
            break;

        case MONTHLY:
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (monthly_button), TRUE);
            break;

        default:
            jp_logf (JP_LOG_WARN, "Invalid backup_when pref: %d\n", ivalue);
    }
}


/*
 * Loads the active and inactive databases from their respective dbm files
 * and inserts them into the CLists.
 */
int display_databases() {
    char full_name[256];
    GDBM_FILE dbf;
    datum key, nextkey;

    jp_logf (JP_LOG_DEBUG, "Backup: display databases\n");

    /*
     * Let's make sure the CLists have been created before we go and
     * try to update them. Thanks to Martin Sjoelin for pointing this
     * out.
     */
    if (!GTK_IS_CLIST(active_clist) || !GTK_IS_CLIST(inactive_clist)) {
        jp_logf (JP_LOG_DEBUG, "Backup: GUI not created, skipping update\n");
        return 0;
    }

    gtk_clist_freeze (GTK_CLIST(active_clist));
    gtk_clist_clear (GTK_CLIST(active_clist));
    gtk_clist_freeze (GTK_CLIST(inactive_clist));
    gtk_clist_clear (GTK_CLIST(inactive_clist));

    /* open the active dbm file */
    get_backup_file_name (ACTIVE_DBM, full_name, 255);
    dbf = gdbm_open (full_name, 512, GDBM_READER | LOCK_FLAG, 0644, 0);
    if (dbf) {
        /* load the active databases */
        key = gdbm_firstkey (dbf);
        if (key.dptr) {
            jp_logf (JP_LOG_DEBUG, "Retrieved %s from active database file\n", key.dptr);
            gtk_clist_append (GTK_CLIST(active_clist), &key.dptr);

            while (key.dptr) {
                nextkey = gdbm_nextkey (dbf, key);
                if (nextkey.dptr) {
                    jp_logf (JP_LOG_DEBUG, "Retrieved %s from active database file\n", nextkey.dptr);
                    gtk_clist_append (GTK_CLIST(active_clist), &nextkey.dptr);
                }
                free (key.dptr);
                key = nextkey;
            }
        }
        else {
            jp_logf (JP_LOG_GUI, "No active databases found\n");
        }

        gdbm_close (dbf);
    }
    else {
        jp_logf (JP_LOG_DEBUG, "Can't open dbm file %s\n", full_name);
    }


    /* open the inactive dbm file */
    get_backup_file_name (INACTIVE_DBM, full_name, 255);
    dbf = gdbm_open (full_name, 512, GDBM_READER | LOCK_FLAG, 0644, 0);
    if (dbf) {
        /* load the inactive databases */
        key = gdbm_firstkey (dbf);
        if (key.dptr) {
            jp_logf (JP_LOG_DEBUG, "Retrieved %s from inactive database file\n", key.dptr);
            gtk_clist_append (GTK_CLIST(inactive_clist), &key.dptr);

            while (key.dptr) {
                nextkey = gdbm_nextkey (dbf, key);
                if (nextkey.dptr) {
                    jp_logf (JP_LOG_DEBUG, "Retrieved %s from inactive database file\n", nextkey.dptr);
                    gtk_clist_append (GTK_CLIST(inactive_clist), &nextkey.dptr);
                }
                free (key.dptr);
                key = nextkey;
            }
        }
        else {
            jp_logf (JP_LOG_GUI, "No inactive databases found\n");
        }

        gdbm_close (dbf);
    }
    else {
        jp_logf (JP_LOG_DEBUG, "Can't open dbm file %s\n", full_name);
    }

    gtk_clist_unselect_all (GTK_CLIST(active_clist));
    gtk_clist_unselect_all (GTK_CLIST(inactive_clist));

    gtk_clist_thaw (GTK_CLIST(active_clist));
    gtk_clist_thaw (GTK_CLIST(inactive_clist));

    return 0;
}


/*
 * Moves the selected item in the from CList to the to CList.
 */
static void move_selected_item (GtkCList *src, GtkCList *dest) {
    guint row;
    GList *node;
    gchar *text;
    gpointer src_name;
    gpointer dest_name;

    src_name = gtk_object_get_user_data (GTK_OBJECT(src));
    dest_name = gtk_object_get_user_data (GTK_OBJECT(dest));

    node = src->selection;
    if (node) {
        row = GPOINTER_TO_UINT(node->data);
        gtk_clist_get_text (src, row, 0, &text);
        node = NULL;
        node = g_list_append (node, text);
        dbm_move_items (src_name, dest_name, node);
        g_list_free (node);
        gtk_clist_append (dest, &text);
        gtk_clist_remove (src, row);
    }
}

/*
 * Moves all items in the src CList to the dest CList.
 */
static void move_all_items (GtkCList *src, GtkCList *dest) {
    gchar *text;
    gpointer src_name;
    gpointer dest_name;
    GList *list = NULL;
    int i;

    /*
     * Get the DBM filenames from the CList user data.
     */
    src_name = gtk_object_get_user_data (GTK_OBJECT(src));
    dest_name = gtk_object_get_user_data (GTK_OBJECT(dest));

    /*
     * Freeze the CLists, so we don't get an annoying flicker.
     */
    gtk_clist_freeze (src);
    gtk_clist_freeze (dest);

    /*
     * Iterate over each of the items in the src...
     */
    for (i = 0; gtk_clist_get_text (src, i, 0, &text); i++) {
        /* Append the item to the dest CList and store it in the GList */
        gtk_clist_append (dest, &text);
        list = g_list_append (list, text);
    }

    /* Move the items in the DBM files */
    dbm_move_items (src_name, dest_name, list);
    g_list_free (list);

    gtk_clist_clear (src);
    gtk_clist_thaw (src);
    gtk_clist_thaw (dest);
}



/* ----------------------------------------------------------------------- */
/*                               Callbacks                                 */
/* ----------------------------------------------------------------------- */

static void cb_move_all_to_inactive (GtkWidget *widget, int data) {
    move_all_items (GTK_CLIST(active_clist), GTK_CLIST(inactive_clist));
}

static void cb_move_to_inactive (GtkWidget *widget, int data) {
    move_selected_item (GTK_CLIST(active_clist), GTK_CLIST(inactive_clist));
}

static void cb_move_all_to_active (GtkWidget *widget, int data) {
    move_all_items (GTK_CLIST(inactive_clist), GTK_CLIST(active_clist));
}

static void cb_move_to_active (GtkWidget *widget, int data) {
    move_selected_item (GTK_CLIST(inactive_clist), GTK_CLIST(active_clist));
}

static void cb_toggle_button (GtkWidget *widget, gpointer data) {
    switch (GPOINTER_TO_INT (data)) {
        case BACKUP_NEW_BUTTON:
            jp_logf (JP_LOG_DEBUG, "backup new button\n");
            backup_set_pref (BPREF_BACKUP_NEW,
                             GTK_TOGGLE_BUTTON (widget)->active,
                             NULL);
            break;

        case PERSISTENT_ARCHIVE_BUTTON:
            jp_logf (JP_LOG_DEBUG, "persistent archive button\n");
            backup_set_pref (BPREF_PERSISTENT_ARCHIVE,
                             GTK_TOGGLE_BUTTON (widget)->active,
                             NULL);
            check_persistent_archive_dir();
            break;

        case EVERY_SYNC_BUTTON:
            jp_logf (JP_LOG_DEBUG, "every sync button\n");
            backup_set_pref (BPREF_BACKUP_WHEN,
                             EVERY_SYNC,
                             NULL);
            break;

        case DAILY_BUTTON:
            jp_logf (JP_LOG_DEBUG, "daily button\n");
            backup_set_pref (BPREF_BACKUP_WHEN,
                             DAILY,
                             NULL);
            break;

        case WEEKLY_BUTTON:
            jp_logf (JP_LOG_DEBUG, "weekly button\n");
            backup_set_pref (BPREF_BACKUP_WHEN,
                             WEEKLY,
                             NULL);
            break;

        case MONTHLY_BUTTON:
            jp_logf (JP_LOG_DEBUG, "monthly button\n");
            backup_set_pref (BPREF_BACKUP_WHEN,
                             MONTHLY,
                             NULL);
            break;


        default:
            jp_logf (JP_LOG_WARN, "Invalid button data: %d\n",
                         GPOINTER_TO_INT (data));
    }
}

static void cb_archives (GtkWidget *widget, gpointer data) {
    int n;

    n = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (archive_spinner));
    backup_set_pref (BPREF_NUM_ARCHIVES, n, NULL);
}
