/* formulas.c
 * geg, a GTK+ Equation Grapher
 * David Bryant 1998
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include "formulas.h"
#include "log.h"
#include "prefs.h"
#include "colors.h"
#include "i18n.h"
#include <ctype.h>
#include <string.h>

/* global data */
extern struct_prefs prefs;

/* locally global data */
static gchar **formulas = NULL;
static int nformulas = 0;
static GtkWidget *wi = NULL;
static GtkStyle *style;

static void close_event(GtkWidget *widget, gpointer data);
static void remove_event(GtkWidget *widget, gpointer data);
static void really_remove_event(gpointer data, gpointer user_data);

void
remove_all_event(GtkWidget *widget, gpointer data)
{
  int i;

  for(i = 0; i < nformulas; i++)
    g_free(formulas[i]);

  if(formulas)
    g_free(formulas);
  formulas = NULL;

  nformulas = 0;
}

/* formula_foreach, calls the supplied function for each formula in the
 * array of formulas, called by go_event in app.c
 */
void
formula_foreach(FormulaFunc func)
{
  gint i;
  for(i = 0; i < nformulas; i++) {
    func(formulas[i], i);
  }
}

/* formula_forall,
 */
void formula_forall(FormulaAllFunc func,
		    gdouble x1, gdouble x2,
		    gdouble y1, gdouble y2)
{
  func(formulas, nformulas, x1, x2, y1, y2);
}

/* formula_add
 */
gint
formula_add(FormulaFunc func, gchar *formula)
{
  if(*formula == '\0') /* check for a real formula */
    return(FALSE);    

  if(nformulas >= prefs.maxform) {
    write_log(_("Maximum reached"));
    return(FALSE);
  }

  if (formula[0] != '#') {
    addto_log("▶ ");  
    write_log(formula);  
    if(func && !func(formula, nformulas)) {
      write_log(_("Parse Error!"));
      return(FALSE);
    }
  }

  /* do not record evaluation requests */
  if (*formula == '?') return(TRUE);
  
  /* add the formula or comment to the list */
  formulas = (gchar **)g_realloc(formulas, ++nformulas * sizeof(gchar **));
  formulas[nformulas - 1] = g_strdup(formula);

  return(TRUE);
}

/* formula_find
 */
gint
formula_find(gchar *formula)
{
  gint i;
  if (formula[0] == '#') return(FALSE);  
  if (formula[0] == '&') return(FALSE);
  if (formula[0] == '?') return(FALSE);
  if (formula[0] == '=') return(FALSE);  
  for (i=0; i<nformulas; i++)
    if (!strcasecmp(formulas[i], formula)) return(TRUE);
  return(FALSE);
}

gchar **
formula_list()
{
  return formulas;
}

gint
formula_count()
{
  return nformulas;
}

/* delete_event, deletes the window, not sure if it would be GTK+ approved 
 */
static gint
delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gtk_widget_destroy(wi);
  wi = NULL;
  gtk_style_unref(style);
  return(TRUE);
}

/* close_event, closes the 'erase' dialog, it is called by the 'Cancel'
 * and the 'Ok' button
 */
static void
close_event(GtkWidget *widget, gpointer data)
{
  gtk_widget_destroy(wi);
  wi = NULL;
  gtk_style_unref(style);
}

/* erase_event, brings up the 'erase' dialog, it is called by the 'erase'
 * toolbar button in the main dialog
 */
void
erase_event(GtkWidget *widget, gpointer data)
{
  int i, j, c;
  GtkWidget *temp_bu, *fr, *li;
  GtkSignalFunc refresh_graph;

  if(wi && wi->window) {
    gtk_widget_map(wi);
    gdk_window_raise(wi->window);
    return;
  }

  refresh_graph = (GtkSignalFunc)data;

  wi = gtk_dialog_new();

  gtk_signal_connect(GTK_OBJECT(wi), "delete_event",
                     GTK_SIGNAL_FUNC(delete_event), NULL);

  gtk_window_set_wmclass(GTK_WINDOW(wi), "erase", "geg");
  gtk_window_set_title(GTK_WINDOW(wi), _("Erase"));
  gtk_window_set_policy(GTK_WINDOW(wi), FALSE, TRUE, FALSE);
  gtk_window_position(GTK_WINDOW(wi), GTK_WIN_POS_MOUSE);

  gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(wi)->vbox), 5);

  /* the list to delete functions from */
  fr = gtk_frame_new(_("Select functions to erase:"));
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->vbox), fr, TRUE, TRUE, 0);
  li = gtk_clist_new(1);	/* only want 1 column */

  /* gtk_clist_set_reorderable(GTK_CLIST(li), TRUE); */

  /* style stuff */
  style = gtk_style_copy(gtk_widget_get_style(widget));
  style->bg[GTK_STATE_PRELIGHT] = prefs.gdk_color[DEF_BACK_COLOR];

  gtk_widget_set_style(li, style);
  gtk_clist_set_selection_mode(GTK_CLIST(li), GTK_SELECTION_MULTIPLE);

  /* add the formulas to the list */
  j = 0;
  for(i = 0; i < nformulas; i++) {
    gtk_clist_append(GTK_CLIST(li), &formulas[i]);
    /* compute color, eliminate coord() calls */
    if (strncasecmp(formulas[i], "coord(", 6)) {
      c = DEF_DEFAULT_COLOR + j % (prefs.n_colors - DEF_DEFAULT_COLOR);
      j++;
    } else
      c = 1;
    gtk_clist_set_foreground(GTK_CLIST(li), i, &prefs.gdk_color[c]);    
    gtk_clist_set_background(GTK_CLIST(li), i, &prefs.gdk_color[DEF_BACK_COLOR]);
  }
  /* make the list a bit bigger than the default size */
  gtk_widget_set_usize(li, 600, 450);
  gtk_container_add(GTK_CONTAINER(fr), li);
  gtk_widget_show(li);
  gtk_widget_show(fr);

  /* Ok button */
  temp_bu = gtk_button_new_with_label(_("Ok"));
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                     GTK_SIGNAL_FUNC(remove_event), li);
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                     GTK_SIGNAL_FUNC(refresh_graph), NULL);
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                     GTK_SIGNAL_FUNC(close_event), NULL);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->action_area), temp_bu,
                     TRUE, TRUE, 0);
  gtk_widget_show(temp_bu);

  /* Cancel button */
  temp_bu = gtk_button_new_with_label(_("Cancel"));
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                     GTK_SIGNAL_FUNC(close_event), NULL);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->action_area), temp_bu,
                     TRUE, TRUE, 0);
  gtk_widget_show(temp_bu);

  gtk_grab_add(wi);
  gtk_widget_show(wi);
  /* how do I do this ? */
  /* gdk_window_set_decorations(wi->window, GDK_DECOR_BORDER); */
}

/* really_remove_event, called for each item in the glist in remove_event    */
static void
really_remove_event(gpointer data, gpointer user_data)
{
  gint i = 0;
  gchar *text;

  gtk_clist_get_text((GtkCList *)user_data, (gint)(glong)data, 0, &text);
  while(strcmp(text, formulas[i++]));
  addto_log(_("Erase: "));  
  write_log(text);
  g_free(formulas[--i]);
  nformulas--;
  /* the next part shuffles the order if we didn't delete the last one       */
  /* this disturbs the order, ideally this should not happen... maybe later  */
  if(i != nformulas)
    formulas[i] = formulas[nformulas];
  formulas = (gchar **)g_realloc(formulas, nformulas * sizeof(gchar **));
}

/* remove_event, called from the 'ok' button in the 'erase' dialog. it
 * passes a pointer to the CList, which has selected entries which we
 * wish to delete
 */
static void
remove_event(GtkWidget *widget, gpointer data)
{
  GtkCList *clist;
  GList *glist;		/* glib doubly linked list */

  clist = GTK_CLIST((GtkWidget *)data);
  glist = clist->selection;
  g_list_foreach(glist, (GFunc)really_remove_event, clist);
}
