/* file_io.c
 * 
 * Copyright (C) 2002 Claudio Girardi <claudio.girardi@ieee.org>
 * 
 * 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
 */


/* This is just a quick hack for saving/loading transmission lines data */
/* some of the functions are adapted from gfit and other GPL'ed software */
/* Thanks goes to all the people writing free software ! */


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fnmatch.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "file_io.h"
#include "transcalc.h"

#ifdef HAVE_FNMATCH
#include <fnmatch.h>
#else
#include "fnmatch.h"
#endif


static gint goto_flag = 0;
static gint idle_flag = 0;
/* file name filter for file dialog */
static gchar *filter_text = "*.trc";
static GtkWidget *filter_entry = NULL;

//extern trans_win *twin;

gint
trc_file_save (FILE *fp, short file_save_mode)
{
  time_t date;
  gint i;
  gchar *tmp_str1 = NULL, *tmp_str2 = NULL;
  gchar *tmp_str = NULL;

  /* get current date and time */
  time(&date);

  /* write header */
  fprintf(fp, "# This file was automatically generated\n");
  fprintf(fp, "#   by transcalc "VERSION" on %s", ctime(&date));
  fprintf(fp, "#\n");
  fprintf(fp, "# It is suggested not to edit this file directly, use transcalc instead\n");
  fprintf(fp, "#\n");
  fprintf(fp, "\n");

  /* write transmission line type */
  tmp_str = (gchar *) gtk_entry_get_text (GTK_ENTRY (GTK_COMBO(transtype_combo)->entry));
  fprintf(fp, "%s\n", tmp_str);

  /* write substrate parameters */
  for (i = 0; i < NUMSUBPARS; i++) {
    tmp_str1 = (gchar *) gtk_entry_get_text(GTK_ENTRY (twin->subparam_text[i]));
    /* if entry has no text write "NULL" on file */
    if (tmp_str1[0] == '\0') strcpy(tmp_str1, "NULL");
    tmp_str2 = (gchar *) gtk_entry_get_text (GTK_ENTRY 
				   (GTK_COMBO (twin->subparam_combo[i])->entry));
    fprintf(fp, "%s %s\n", tmp_str1, tmp_str2);
  }

  /* write component parameters */
  for (i = 0; i < NUMCOMPPARS; i++) {
    tmp_str1 = (gchar *) gtk_entry_get_text (GTK_ENTRY (twin->component_param_text[i]));
    /* if entry has no text write "NULL" on file */
    if (tmp_str1[0] == '\0') strcpy(tmp_str1, "NULL");
    tmp_str2 = (gchar *) gtk_entry_get_text (GTK_ENTRY 
				   (GTK_COMBO (twin->component_param_combo[i])->entry));
    fprintf(fp, "%s %s\n", tmp_str1, tmp_str2);
  }
  
  /* write physical parameters */
  for (i = 0; i < NUMPHYSPARS; i++) {
    tmp_str1 = (gchar *) gtk_entry_get_text (GTK_ENTRY (twin->physical_param_text[i]));
    /* if entry has no text write "NULL" on file */
    if (tmp_str1[0] == '\0') strcpy(tmp_str1, "NULL");
    tmp_str2 = (gchar *) gtk_entry_get_text (GTK_ENTRY 
				   (GTK_COMBO (twin->physical_param_combo[i])->entry));
    fprintf(fp, "%s %s\n", tmp_str1, tmp_str2); 
  }

  /* write physical parameters status (fix/fixed) */
  for (i = 0; i < 2; i++) {
    gtk_label_get(GTK_LABEL (GTK_BIN (twin->physical_param_fix[i])->child), &tmp_str);
    fprintf(fp, "%s %i\n", tmp_str,
	    GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET (twin->physical_param_fix[i])));
  }
  
  /* write electrical parameters */
  for (i = 0; i < NUMELECPARS; i++) {
    tmp_str1 = (gchar *) gtk_entry_get_text (GTK_ENTRY (twin->electrical_param_text[i]));
    /* if entry has no text write "NULL" on file */
    if (tmp_str1[0] == '\0') strcpy(tmp_str1, "NULL");
    tmp_str2 = (gchar *) gtk_entry_get_text (GTK_ENTRY 
				   (GTK_COMBO (twin->electrical_param_combo[i])->entry));
    fprintf(fp, "%s %s\n", tmp_str1, tmp_str2);
  }
  
  /* write status */
  gtk_label_get(GTK_LABEL (twin->status), &tmp_str);
  fprintf(fp, "%s\n", tmp_str);
  
  if (file_save_mode == SAVE_END) {
    gdk_window_get_size ( GTK_WIDGET (tgui->mainwindow)->window, 
			  &main_window_width, &main_window_height);
    fprintf(fp, "%d %d \n", main_window_width, main_window_height);
  }
  fclose(fp);
  return 0;
}
   

/* file_selection_ok_save() - write current transmission line data to file */
static gint
file_selection_ok_save (GtkFileSelection *fs, GtkWidget *w)
{
  FILE *fp;
  gchar *fname, *fname_ext;
  gchar *ext;

  /* get file name */
  fname = (gchar *) gtk_file_selection_get_filename (fs);
  /* add file extension if necessary*/
  ext = rindex (fname, '.');
  if (ext == NULL) {
    fname_ext = g_strconcat(fname, ".trc", NULL);
  } else {
    if (strcmp(ext, ".trc"))
      fname_ext = g_strconcat(fname, ".trc", NULL);
    else 
      fname_ext = g_strdup(fname);
  }
  fp = fopen(fname_ext, "w");
  /* free complete file name string */
  g_free(fname_ext);

  if (!fp) /* problems opening file */
    return -1;

  /* file selection dialog is no longer needed */
  gtk_widget_destroy (GTK_WIDGET (fs));

  trc_file_save (fp, SAVE_OPER);

  return 0;
}


gint
trc_file_load (FILE *fp, short load_mode)
{
  gchar tmp_str1[80], tmp_str2[80];
  gint i, tmp_int;
  char buf[256], *ip = NULL, *end = NULL;


  /* here fgets and scanf are mixed, could be done in a better way... */
  do { 
    fgets(buf, 256, fp);
    ip = &buf[0];
    /* skip spaces */
    while ((*ip == ' ') || (*ip == '\t'))
      ip++;
  }
  while ((*ip == '#') || (*ip == '\n')); /* don't process empty lines */
    
  /* Get the ending position.  */
  for (end = ip; *end && *end != '\n'; end++);
  /* make sure it terminates with '\0' */
  *end = '\0';
  
  /* read transmission line type */
  gtk_entry_set_text (GTK_ENTRY (GTK_COMBO(transtype_combo)->entry), ip);
  
  /* read substrate parameters */
  for (i = 0; i < NUMSUBPARS; i++) {
    fscanf(fp, "%s %s\n", tmp_str1, tmp_str2);
    /* if "NULL" is read entry will have no text */
    if (!strcmp(tmp_str1, "NULL")) tmp_str1[0] = '\0';
    gtk_entry_set_text(GTK_ENTRY (twin->subparam_text[i]), tmp_str1); 
    gtk_entry_set_text (GTK_ENTRY 
			(GTK_COMBO (twin->subparam_combo[i])->entry), tmp_str2);
  }
  
  /* read component parameters */
  for (i = 0; i < NUMCOMPPARS; i++) {
    fscanf(fp, "%s %s\n", tmp_str1, tmp_str2);
    /* if "NULL" is read entry will have no text */
    if (!strcmp(tmp_str1, "NULL")) tmp_str1[0] = '\0';
    gtk_entry_set_text (GTK_ENTRY (twin->component_param_text[i]), tmp_str1);
    gtk_entry_set_text (GTK_ENTRY 
			(GTK_COMBO (twin->component_param_combo[i])->entry), tmp_str2);
  }
  
  /* read physical parameters */
  for (i = 0; i < NUMPHYSPARS; i++) {
    fscanf(fp, "%s %s\n", tmp_str1, tmp_str2);
    /* if "NULL" is read entry will have no text */
    if (!strcmp(tmp_str1, "NULL")) tmp_str1[0] = '\0';
    gtk_entry_set_text (GTK_ENTRY (twin->physical_param_text[i]), tmp_str1);
    gtk_entry_set_text (GTK_ENTRY 
			(GTK_COMBO (twin->physical_param_combo[i])->entry), tmp_str2);
  }
  
  /* read physical parameters status (fix/fixed) */
  for (i = 0; i < 2; i++) {
    fscanf(fp, "%s %i\n", tmp_str1, &tmp_int);
    gtk_label_set_text(GTK_LABEL 
		       (GTK_BIN (twin->physical_param_fix[i])->child), tmp_str1);
    gtk_widget_set_sensitive(GTK_WIDGET (twin->physical_param_fix[i]), tmp_int);
  }
  
  /* read electrical parameters */
  for (i = 0; i < NUMELECPARS; i++) {
    fscanf(fp, "%s %s\n", tmp_str1, tmp_str2);
    /* if "NULL" is read entry will have no text */
    if (!strcmp(tmp_str1, "NULL")) tmp_str1[0] = '\0';
    gtk_entry_set_text (GTK_ENTRY (twin->electrical_param_text[i]), tmp_str1);
    
    gtk_entry_set_text (GTK_ENTRY 
			(GTK_COMBO (twin->electrical_param_combo[i])->entry), tmp_str2);
  }
  
  /* read status string */
  /* get whole line */
  fgets(buf, 256, fp);
  ip = &buf[0];
  /* Get the ending position.  */
  for (end = ip; *end && *end != '\n'; end++);
  /* make sure it terminates with '\0' */
  *end = '\0';
  gtk_label_set_text(GTK_LABEL (twin->status), ip);

  if (load_mode == LOAD_INIT) {
    fscanf(fp, "%d %d\n", &main_window_width, &main_window_height);
  }
  fclose (fp);
  return 0;
}

/* file_selection_ok_load() - read transmission line data from file */
static gint
file_selection_ok_load (GtkFileSelection *fs, GtkWidget *w)
{
  FILE *fp;
  gchar *fname;


  /* get selected file name */
  fname = (gchar *) gtk_file_selection_get_filename (fs);
  /* try to open the file */
  fp = fopen(fname, "r");

  /* done with fname, destroy the file dialog */
  gtk_widget_destroy (GTK_WIDGET (fs));

  if (!fp) /* problems opening file */
    return -1;

  trc_file_load (fp, LOAD_OPER);
  return 0;
}


/* this function is called when the file selection entry changes */
static void 
file_selection_update_file_list(GtkWidget * dialog)
{
  GtkFileSelection *fs = NULL;
  gchar *list_name;
  guint i = 0;

  g_return_if_fail(GTK_IS_FILE_SELECTION(dialog));

  fs = GTK_FILE_SELECTION(dialog);
  
  gtk_idle_remove(idle_flag);
  idle_flag = 0;
  
  if (!filter_text || !strlen(filter_text))
    return;
  
  /* stop updating the list visuals */
  gtk_clist_freeze(GTK_CLIST(fs->file_list));
  
  while (gtk_clist_get_text(GTK_CLIST(fs->file_list), i, 0, &list_name)) {
    if (fnmatch(filter_text, list_name, 0)) {
      gtk_clist_remove(GTK_CLIST(fs->file_list), i);
      continue;
    }
    i++;
  }
  /* enable updating list visuals */
  gtk_clist_thaw(GTK_CLIST(fs->file_list));
}


/* if user double-click on a directory update the file list according to the current filter */
static void 
file_selection_dir_button(GtkWidget * widget, GtkWidget * dialog)
{
  if (idle_flag) {
    return;
  }
  
  idle_flag = gtk_idle_add((GtkFunction) file_selection_update_file_list, dialog);
}


/* When file_selection_dialog() is called with a path argument ending with
 * a file name this function search and higlight this file in the file list,
 * if present
 *
 * Adapted from gtk-app-devel-list, Andreas Tille: tille@physik.uni-halle.de 
 *
 */
static void 
file_selection_goto(GtkFileSelection * fs)
{
  const char *pre_file;
  gchar *list_name;
  gint i;

  gtk_idle_remove(goto_flag);
  goto_flag = 0;

  g_return_if_fail(GTK_IS_FILE_SELECTION(fs));

  pre_file = gtk_entry_get_text(GTK_ENTRY(fs->selection_entry));
 
  if (pre_file) {
    i = -1;
    while (gtk_clist_get_text(GTK_CLIST(fs->file_list), ++i, 0, &list_name)) {
      if (list_name && g_str_equal(list_name, pre_file)) {
	gtk_clist_select_row(GTK_CLIST(fs->file_list), i, 0);
	gtk_clist_moveto(GTK_CLIST(fs->file_list), i, 0, 0.5, 0.0);
	break;
      }
    }
  }
}


/* when filter entry is changed update the file list according to the current filter */
static gint 
file_selection_filter_key_press(GtkWidget * widget, GdkEventKey * event, GtkWidget * dialog)
{
  GtkFileSelection *fs = NULL;
  
  if (!GTK_IS_FILE_SELECTION(dialog))
    return FALSE;

  fs = GTK_FILE_SELECTION(dialog);
  if (event->keyval == GDK_KP_Enter) {
    /* convert keypad enter key to return key (?) */
    event->keyval = GDK_Return;
  }
  if (event->keyval == GDK_Tab || event->keyval == GDK_Return) {
    filter_text = (gchar *) gtk_entry_get_text(GTK_ENTRY(widget));
    /* this will trigger the file list updating */
    gtk_file_selection_set_filename(fs, "./");
  }
  return FALSE;
}


/* create a file selection dialog with a given window title and given current files path */
static GtkWidget*
file_selection_dialog (gchar * title, gchar *path)
{
  static GtkWidget *dialog = NULL;
  GtkWidget *label = NULL;
  
  if (!dialog) {
    dialog = gtk_file_selection_new (title);
    
    gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (dialog));
    
    gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
    
    gtk_signal_connect (GTK_OBJECT (dialog), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dialog);
    
    label = gtk_label_new("Filter:");
    gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION (dialog)->action_area), label, FALSE, FALSE, 0);
    gtk_widget_show(label);
    filter_entry = gtk_entry_new();
    
    gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION (dialog)->action_area), filter_entry, FALSE, FALSE, 0);
    gtk_entry_set_text(GTK_ENTRY(filter_entry), filter_text);
    gtk_widget_show(filter_entry);

    /* when the selection entry changes update the file list */
    /* this can be triggered also by double clicking on a directory */
    gtk_signal_connect(GTK_OBJECT (GTK_FILE_SELECTION(dialog)->selection_entry), "changed", (GtkSignalFunc) file_selection_dir_button, (gpointer) dialog);
    
    gtk_signal_connect(GTK_OBJECT(filter_entry), "key_press_event", (GtkSignalFunc) file_selection_filter_key_press, dialog);
    
    /* set current filename (if any); this will also trigger file list update, according to filter_text */
    gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog), path);
    /* select the current selection entry */
    gtk_editable_select_region(GTK_EDITABLE(GTK_FILE_SELECTION(dialog)->selection_entry), 0, -1);

    goto_flag = gtk_idle_add((GtkFunction) file_selection_goto, (gpointer) dialog);
  }
  
  if (!GTK_WIDGET_VISIBLE (dialog))
    gtk_widget_show (dialog);
  else
    gtk_widget_destroy (dialog);
  
  return dialog;
}


/* prepare creation of file selection dialoc setting the callbacks and window title */
void 
file_dialog(gpointer callback_data, guint callback_action, GtkWidget *widget)
{
  GtkWidget *filesel = NULL;

  switch (callback_action) {
  case TC_FILE_LOAD :
    filesel = file_selection_dialog("Load file...", "./");
    gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", GTK_SIGNAL_FUNC(file_selection_ok_load), GTK_OBJECT(filesel));
    break;
  case TC_FILE_SAVE :
    filesel = file_selection_dialog("Save file...", "./");
    gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", GTK_SIGNAL_FUNC(file_selection_ok_save), GTK_OBJECT(filesel));
    break;
  default :
    g_assert_not_reached();
  }


  gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked", (GtkSignalFunc)gtk_widget_destroy, GTK_OBJECT(filesel));
  
  gtk_widget_show (filesel);

}

