/* GtkSQL -- an interactive graphical query tool for PostgreSQL
 * Copyright (C) 1998  Lionel ULMER
 *
 * 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 <string.h>
#include <stdio.h>

#include "common.h"

#include "queries.h"
#include "status.h"

#define TYPE_KEY "Type key"

typedef struct _call_data {
  GtkWidget *file_dialog;
  GtkWidget *type_menu;
  GtkWidget *query;
  char *query_name;
} CallData;

typedef struct _common_export_data {
  GtkWidget *dialog;
  char *filename;
  char *raw_filename;
} CommonExportData;

typedef struct _text_export_data {
  GtkWidget *dialog;
  char *filename;
  char *raw_filename;

  GtkWidget *query;
  GtkWidget *separator;
  GtkWidget *pprint;
} TextExportData;

typedef struct {
  GtkWidget *dialog;
  char *filename;
  char *raw_filename;

  GtkWidget *query;
  GtkWidget *rec_per_page;
  GtkWidget *titles;
  GtkWidget *links;
} HtmlExportData;

static char *Types[] = {
  "Choose from extension", "", "Html", "Text", NULL
};

static char *NoExtensions[] = { NULL };
static char *HtmlExtensions[] = { "html", "htm", NULL };
static char *TextExtensions[] = { "txt", "", NULL};
static char **TypesExtensions[] = {
  NoExtensions, NoExtensions,
  HtmlExtensions, TextExtensions,
  NULL
};

static void ExportAsText(char *filename, char *name,
			 GtkWidget *query, char *query_name) ;
static void ExportAsHtml(char *filename, char *name,
			 GtkWidget *query, char *query_name) ;

static void (*ExportFunctions[])() = {
  NULL, NULL,
  ExportAsHtml, ExportAsText,
  NULL
};

static GtkWidget *current_dialog;
static int file_dialog_displayed = 0;

/* Helper function */
static void AddEntryLine(char *labeltext, GtkWidget *widget,
			 GtkWidget *table, int height) {
  GtkWidget *label;
  
  label = gtk_label_new(labeltext);
  gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1,
			    height, height + 1);
  gtk_widget_show(label);
  
  gtk_table_attach_defaults(GTK_TABLE(table), widget, 1, 2,
			    height, height + 1);
}

static void common_cancel_callback(GtkWidget *widget, gpointer data) {
  CommonExportData *cd = (CommonExportData *) data;
  
  gtk_widget_destroy(GTK_WIDGET(cd->dialog));
  g_free(cd->filename);
  g_free(cd->raw_filename);
  g_free(cd);
}

static void AddSpace(FILE *f, int exists, int todo) {
  int i;

  for (i = exists; i < todo; i++)
    fputc(' ', f);
}

static void text_export_ok_callback(GtkWidget *widget, gpointer data) {
  TextExportData *cd = (TextExportData *) data;
  int pprint;
  char *sep;
  char *tab = "\t", *space = " ";
  FILE *f;
  void *result;
  
  /* Get the parameters */
  pprint = GTK_TOGGLE_BUTTON(cd->pprint)->active;
  sep = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(cd->separator)->entry));
  if (!strcmp(sep, "TAB"))
    sep = tab;
  else if (!strcmp(sep, "SPACE"))
    sep = space;

  /* Opens the file */
  f = fopen(cd->filename, "wb");
  
  /* Start exporting ... */
  result = GetQueryResult(cd->query);
  
  if (conn->DBget_query_status(conn, result) == DB_QUERY_RECORDS_OK) {
    int nb_tuples = conn->DBget_record_number(conn, result);
    int nb_cols = conn->DBget_field_number(conn, result);
    int *col_length = (int *) g_malloc(sizeof(int) * nb_cols);
    int i, j;

    /* Pretty Printing */
    if (pprint) {
      for (j = 0; j < nb_cols; j++)
	col_length[j] = 0;
      
      for (i = 0; i < nb_tuples; i++)
	for (j = 0; j < nb_cols; j++)
	  if (strlen(conn->DBget_field_value(conn, result, i, j)) >
	      col_length[j])
	    col_length[j] =
	      strlen(conn->DBget_field_value(conn, result, i, j));
    }
    
    status_push("Exporting Text...");
    status_reset();
    
    /* Fill the CList */
    for (i = 0; i < nb_tuples; i++) {
      for (j = 0; j < nb_cols; j++) {
	if (j > 0)
	  fprintf(f, "%s", sep);
	fprintf(f, conn->DBget_field_value(conn, result, i, j));
	if (pprint)
	  AddSpace(f, strlen(conn->DBget_field_value(conn, result, i, j)), col_length[j]);
      }
      status_update(i / (float) nb_tuples);
      fprintf(f, "\n");
    }

    g_free(col_length);
  }

  /* Closes the file */
  fclose(f);
  
  /* Close the dialog */
  status_reset();
  status_pop();
  gtk_widget_destroy(GTK_WIDGET(cd->dialog));
  g_free(cd->filename);
  g_free(cd->raw_filename);
  g_free(cd);
}
  
/* Dialog for exporting Text */
static void ExportAsText(char *filename, char *name,
			 GtkWidget *query, char *query_name) {
  GtkWidget *dialog, *sep_combo;
  GtkWidget *table, *vbox;
  GtkWidget *pp_button;
  GtkWidget *ok_button, *cancel_button;
  TextExportData *okdata;
  
  char buf[256];
  char *sep[] = { ",", ";", "TAB", "SPACE", "-", "|", NULL };
  int i;
  GList *cbitems = NULL;

  /* The dialog */
  dialog = gtk_dialog_new();
  sprintf(buf, "Export '%s' as text", query_name);
  gtk_window_set_title(GTK_WINDOW(dialog), buf);
  gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
  
  /* The vbox */
  vbox = gtk_vbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
		     vbox, FALSE, FALSE, 0);
  gtk_widget_show(vbox);
  
  /* The table to have a nice layout :-) */
  table = gtk_table_new(3, 2, FALSE);
  gtk_container_border_width(GTK_CONTAINER(table), 6);
  
  /* The combo box for the separator */
  for (i = 0; sep[i] != NULL; i++)
    cbitems = g_list_append(cbitems, sep[i]);
  sep_combo = gtk_combo_new();
  gtk_combo_set_popdown_strings(GTK_COMBO(sep_combo), cbitems);
  gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(sep_combo)->entry), sep[0]);
  gtk_widget_show(sep_combo);
  AddEntryLine("Separator : ", sep_combo, table, 0);

  /* End of table "layout" */
  gtk_widget_show(table);
  gtk_box_pack_start(GTK_BOX(vbox),
		     table, FALSE, FALSE, 0);

  /* The pretty print button */
  pp_button = gtk_check_button_new_with_label("Pretty print");
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(pp_button), FALSE);
  gtk_widget_show(pp_button);
  gtk_box_pack_start(GTK_BOX(vbox),
		     pp_button, FALSE, FALSE, 0);
  
  /* Now, the buttons ! */
  okdata = (TextExportData *) g_malloc(sizeof(TextExportData));
  okdata->dialog = dialog;
  okdata->separator = sep_combo;
  okdata->pprint = pp_button;
  okdata->filename = filename;
  okdata->raw_filename = name;
  okdata->query = query;
  
  ok_button = gtk_button_new_with_label("Ok");
  gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
		     GTK_SIGNAL_FUNC(text_export_ok_callback), okdata);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
		     ok_button, FALSE, FALSE, 0);
  GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default(ok_button);
  gtk_widget_show(ok_button);
  cancel_button = gtk_button_new_with_label("Cancel");
  gtk_signal_connect(GTK_OBJECT(cancel_button), "clicked",
		     GTK_SIGNAL_FUNC(common_cancel_callback), dialog);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
		     cancel_button, FALSE, FALSE, 0);
  gtk_widget_show(cancel_button);
  
  gtk_widget_show(dialog);
}

static void html_export_ok_callback(GtkWidget *widget, gpointer data)
{
  HtmlExportData *cd = (HtmlExportData *) data;
  FILE *f = NULL;
  void *result;
  char *basename, *lbase = NULL;
  char current_file[256];
  int current_page, rec_per_page;
  int links, titles;
  int nb_tuples;
  int nb_cols;
  int i, j;

  /* Gets all the info we need before exporting */
  result = GetQueryResult(cd->query);
  if (conn->DBget_query_status(conn, result) != DB_QUERY_RECORDS_OK)
    return;
  nb_tuples = conn->DBget_record_number(conn, result);
  nb_cols = conn->DBget_field_number(conn, result);

  basename = gtk_entry_get_text(GTK_ENTRY(cd->rec_per_page));
  rec_per_page = atoi(basename); /* FIXME: check the return value */
  basename = NULL;
  links = GTK_TOGGLE_BUTTON(cd->links)->active;
  titles = GTK_TOGGLE_BUTTON(cd->titles)->active;

  if (rec_per_page < nb_tuples) {
      basename = strdup(cd->filename);
      for (i = strlen(basename); (i > 0) && (basename[i] != '.'); i--)
          ;
      basename[i] = '\0';
      for (j = i; (j > 0) && (basename[j] != '/'); j--)
          ;
      lbase = strdup(basename + j + 1);
  }

  /* Start exporting */
  status_push("Exporting HTML...");
  status_reset();
  current_page = 0;
  
  for (i = 0; i < nb_tuples; i++) {
      if ((i % rec_per_page) == 0) {
          if (rec_per_page < nb_tuples) {
              if (current_page != 0) {
                  fprintf(f, "</TABLE>\n");
                  fclose(f);
              }
              sprintf(current_file, "%s%03d.html", basename, current_page++);
              f = fopen(current_file, "w");
              if (links) {
                  if (current_page != 1)
                      fprintf(f, "<A HREF=\"%s%03d.html\">Previous</A> ",
                              lbase, current_page-2);
                  if (nb_tuples > (current_page + 1) * rec_per_page)
                      fprintf(f, " <A HREF=\"%s%03d.html\">Next</A>",
                              lbase, current_page);
              }
              fprintf(f, "<TABLE>\n");
          } else if (i == 0) {
              f = fopen(cd->filename, "w");
              fprintf(f, "<TABLE>\n");
          }
      }
      /* Exports the line */
      fprintf(f, " <TR>\n");
      for (j = 0; j < nb_cols; j++) {
          fprintf(f, "  <TD> %s </TD>\n",
		  conn->DBget_field_value(conn, result, i, j));
      }
      status_update(i / (float) nb_tuples);
      fprintf(f, " </TR>\n");
  }
  
  /* Closes the file */
  fprintf(f, "</TABLE>\n");
  fclose(f);

  status_reset();
  status_pop();
  /* Memory cleaning */
  gtk_widget_destroy(cd->dialog);
  g_free(basename);
  g_free(cd->filename);
  g_free(cd->raw_filename);
  g_free(cd);
}

static void html_cancel_callback(GtkWidget *widget, gpointer data) {
  gtk_widget_destroy((GtkWidget *) data);
}

/* Dialog for exporting HTML */
static void ExportAsHtml(char *filename, char *name,
			 GtkWidget *query, char *query_name)
{
  GtkWidget *dialog, *table;
  GtkWidget *vbox, *hbox;
  GtkWidget *rec_per_page, *titles, *links, *label;
  GtkWidget *ok_button, *cancel_button;
  HtmlExportData *okdata;
  void *result;

  char buf[256];
  
  dialog = gtk_dialog_new();
  sprintf(buf, "Export '%s' as HTML", query_name);
  gtk_window_set_title(GTK_WINDOW(dialog), buf);
  gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);

  vbox = gtk_vbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
                     vbox, FALSE, FALSE, 0);
  gtk_widget_show(vbox);

  table = gtk_table_new(2, 2, FALSE);

  hbox = gtk_hbox_new(FALSE, 5);
  label = gtk_label_new("Records per page: ");
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 5);
  rec_per_page = gtk_entry_new();

  result = GetQueryResult(query);
  if (conn->DBget_query_status(conn, result) != DB_QUERY_RECORDS_OK)
    return;
  sprintf(buf, "%d", conn->DBget_record_number(conn, result));
  gtk_entry_set_text(GTK_ENTRY(rec_per_page), buf);

  gtk_widget_show(rec_per_page);
  gtk_box_pack_start(GTK_BOX(hbox), rec_per_page, TRUE, FALSE, 5);
  gtk_widget_show(hbox);
  gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 2, 0, 1);
  
  links = gtk_check_button_new_with_label("Include links");
  gtk_widget_show(links);
  gtk_table_attach_defaults(GTK_TABLE(table), links, 0, 1, 1, 2);

  titles = gtk_check_button_new_with_label("Include columns titles");
  gtk_widget_show(titles);
  gtk_table_attach_defaults(GTK_TABLE(table), titles, 1, 2, 1, 2);

  gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, FALSE, 5);
  gtk_widget_show(table);
  
  okdata = (HtmlExportData *) g_malloc(sizeof(HtmlExportData));
  okdata->dialog = dialog;
  okdata->filename = filename;
  okdata->raw_filename = name;
  okdata->query = query;
  okdata->rec_per_page = rec_per_page;
  okdata->titles = titles;
  okdata->links = links;

  ok_button = gtk_button_new_with_label("Ok");
  gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
		     GTK_SIGNAL_FUNC(html_export_ok_callback), okdata);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
		     ok_button, FALSE, FALSE, 0);
  GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default(ok_button);
  gtk_widget_show(ok_button);
  cancel_button = gtk_button_new_with_label("Cancel");
  gtk_signal_connect(GTK_OBJECT(cancel_button), "clicked",
		     GTK_SIGNAL_FUNC(html_cancel_callback), dialog);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
		     cancel_button, FALSE, FALSE, 0);
  gtk_widget_show(cancel_button);
  gtk_widget_show(dialog);
}

int ExportInProgress() {
  return file_dialog_displayed;
}

static GtkWidget *CreateTypeMenu() {
  GtkWidget *optionmenu, *menu;
  int i;

  optionmenu = gtk_option_menu_new();
  menu = gtk_menu_new();
  
  i = 0;
  while (Types[i] != NULL) {
    GtkWidget *menuitem;

    if (strlen(Types[i]) == 0)
      menuitem = gtk_menu_item_new();
    else
      menuitem = gtk_menu_item_new_with_label(Types[i]);

    gtk_object_set_data(GTK_OBJECT(menuitem),
			TYPE_KEY,
			(gpointer) i);
    
    gtk_menu_append (GTK_MENU (menu), menuitem);
    gtk_widget_show (menuitem);
    
    i++;
  }

  gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
  
  return optionmenu;
}

void cancel_callback(GtkWidget *widget, gpointer data) {
  CallData *cdata = (CallData *) data;

  gtk_widget_destroy(GTK_WIDGET(cdata->file_dialog));
  file_dialog_displayed = 0;
  UpdateButtonSensitivity();

  g_free(cdata);
}

void ok_callback(GtkWidget *widget, gpointer data) {
  CallData *cdata = (CallData *) data;
  GtkFileSelection *fs = GTK_FILE_SELECTION(cdata->file_dialog);
  GtkWidget *type_w;
  char *filename, *raw_filename;
  int type;

  /* We get the file name */
  filename = gtk_file_selection_get_filename(fs);
  raw_filename = gtk_entry_get_text(GTK_ENTRY(fs->selection_entry));

  if (((filename == NULL) || (strlen(filename) == 0)) ||
      ((raw_filename == NULL) || (strlen(raw_filename) == 0))) {
    /* TODO */
    printf("Error...\n");

    cancel_callback(widget, data);
    return;
  }

  /* Makes a copy of the resources... */
  filename = g_strdup(filename);
  raw_filename = g_strdup(raw_filename);
  
  /* First, we need to find what is in the menu */
  type_w = gtk_menu_get_active(GTK_MENU(cdata->type_menu));
  type = (int) gtk_object_get_data(GTK_OBJECT(type_w),
				   TYPE_KEY);
  if (type == 0) {
    /* Need to find extension */
    int l = strlen(raw_filename), j;
    char *ext;

    while ((l >=0) && (raw_filename[l] != '.'))
      l--;

    if (l < 0)
      ext = raw_filename + strlen(raw_filename);
    else
      ext = raw_filename + l + 1;

    /* Need to find corresponding code */
    type = 0;
    while (TypesExtensions[type] != NULL) {
      j = 0;
      while (TypesExtensions[type][j] != NULL) {
	if (!strcasecmp(TypesExtensions[type][j], ext))
	  goto end;
	j++;
      }
      type++;
    }
    
  end:
    if (TypesExtensions[type] == NULL) {
      /* TODO */
      printf("Error !!!\n");
      return;
    }
  }
  
  /*printf("%d - %s\n", type, Types[type]);*/
  /* We call the corresponding dialog */
  if (ExportFunctions[type] == NULL) {
    /* TODO */
    printf("Error !!!\n");
    return;
  }
  (ExportFunctions[type])(filename, raw_filename,
			  cdata->query, cdata->query_name);
  
  /* Now, we can delete the dialog */
  gtk_widget_destroy(GTK_WIDGET(cdata->file_dialog));
  file_dialog_displayed = 0;
  UpdateButtonSensitivity();
}


gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) {
  /* We call the cancel callback */
  cancel_callback(widget, data);
  
  /* We do not delete it, the cancel call back will do it :-) */
  return (TRUE);
}

void ExportQueryDialog() {
  if (file_dialog_displayed) {
    /* Should never happen */
  } else {
    GtkWidget *file_dialog;
    GtkWidget *frame, *menu;
    CallData *cdata;
    char buf[256];
    
    sprintf(buf, "Exporting query '%s'", GetCurrentQueryName());
    file_dialog = gtk_file_selection_new(buf);
    gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(file_dialog));
    
    file_dialog_displayed = 1;
    current_dialog = file_dialog;

    /* Now, we create the save options */
    frame = gtk_frame_new("Export Type");
    gtk_container_border_width(GTK_CONTAINER(frame), 5);

    menu = CreateTypeMenu();
    gtk_widget_show(menu);
    gtk_container_add(GTK_CONTAINER(frame), menu);
    
    gtk_widget_show(frame); 
    gtk_box_pack_end(GTK_BOX(GTK_FILE_SELECTION(file_dialog)->main_vbox),
		     frame, FALSE, FALSE, 5);

    /* Now, we connect the signals */
    cdata = (CallData *) g_malloc(sizeof(CallData));
    cdata->file_dialog = file_dialog;
    cdata->type_menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu));
    cdata->query = GetCurrentQuery();
    cdata->query_name = GetCurrentQueryName();
    
    gtk_signal_connect(GTK_OBJECT(file_dialog), "delete_event",
		       GTK_SIGNAL_FUNC(delete_event), cdata);
    gtk_signal_connect
      (GTK_OBJECT(GTK_FILE_SELECTION(file_dialog)->cancel_button),
       "clicked", GTK_SIGNAL_FUNC(cancel_callback), cdata);
    gtk_signal_connect
      (GTK_OBJECT(GTK_FILE_SELECTION(file_dialog)->ok_button),
       "clicked", GTK_SIGNAL_FUNC(ok_callback), cdata);
    
    gtk_widget_show(file_dialog);
  }
}
