/**
 *  File nm_cmd.c
 *  Follow_me_on and undo/redo nm_cmd* functions
 *  GUI dependence is concentrated in the gg_cmd_prt(buf) calls.  // A REVOIR
 *
 *  Undo/Redo is based on Grace language and libundo library (see src/nn_cmd.c)
 *
 *  Each time changes are made effective (e.g. when "Apply" or "Accept" is
 *  clicked), a set of command lines is registered with "undo_snapshot" via
 *  "nn_cmd_undo_snap" with two lines for each change:
 *     the first is the old value,
 *     the second the new one.
 *  Thus undo is performed using odd lines, and redo using even lines.
 *  As one click can be related to several lines, the "libundo count"
 *  is different from the "user undo count" and the undo_count[] array
 *  store the last lib_undo_count for each user snapshot.
 *
 */

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

#define MAX_UNDO 200

#include <gtk/gtk.h>

#include "globals.h"
#include "defines.h"
#include "utils.h"
#include "graphutils.h"
#include "t1fonts.h"
#include "parser.h"

#include "nn_cmd.h"
#include "gg_gutil.h"
#include "zc.h"

// for gg_update_undo ,ge_rpane_update: not clean  A REVOIR
#include "gg_protos.h"
#include "ge_protos.h"

#ifdef HAVE_LIBUNDO
#include <undo.h>
static UNDO *heap = NULL;
static int err;
static int undo_count[MAX_UNDO]; /* array of pointers to undo/redo command lines */
static int un_index = 0;         /* the current pointer into undo_count[] */
static int un_index_last = 0;    /* point to last significant line into undo_count */
#endif

extern void gg_cmd_prt (char *buf);
extern void nn_cmd_obj_prefix (QDobject *new ,char *prefix);

static int all;
static char buf[1028];
static char *prefix;


static char *buf_undo;
static int something_changed;
static int undo_on 	= FALSE;
static long int tv_sec  = 0;
static long int tv_usec = 0;
static void *w_spin 	= NULL;
static long int dcsec 	= 0;
static int discard_undo = FALSE;
static FILE *pp        	= NULL;

static void nn_cmd_undo_snap ();
static void nn_cmd_redo_snap ();

/**
 *  nn_cmd_all : sets the "all" switch
 */
void nn_cmd_all (int al)
{
  all = al;
}

void nn_cmd_undo_on (int sw)
{
  undo_on = sw;
}

void nn_cmd_prefix (char *pref)
{
  prefix = pref;
}

void nn_cmd_set_filep (FILE *fp)
{
  pp = fp;
}

FILE *nn_cmd_get_filep (void)
{
  return pp;
}


void nn_cmd_prt (char *buf)
{
  if (pp != NULL) fprintf (pp ,"%s" ,buf);
  gg_cmd_prt (buf);
}

void nn_cmd_set_time (void *w ,long int sec ,long int usec)
{
  long int dsec;
  dsec  = sec - tv_sec;
  dcsec = (usec - tv_usec) / 10000;
  dcsec += 100 * dsec;
  discard_undo =  (w == w_spin && dcsec < 20);
  w_spin  = w;
  tv_sec  = sec;
  tv_usec = usec;
}


/******* MACRO for 1 variable *******/

/**
 *  The following functions store command lines in buf_undo
 * and print them with nn_cmd_prt ()
 * in macros: o:old , n:new , p:value to print , f:format
 */
#define DO1(o,n,p,f)		 \
  if (all || *o != n) {		 \
    sprintf (buf ,f ,prefix ,p(n));		\
    nn_cmd_prt (buf); \
    if (undo_on) {  \
      sprintf (buf_undo ,f ,prefix ,p(*o));	\
      nn_cmd_undo_snap (); \
      if (o != NULL) *o = n;	  \
      sprintf (buf_undo ,f ,prefix ,p(n));	\
      nn_cmd_redo_snap (); \
    } \
  }

static int    I(int n)    { return n;}
static double D(double d) { return d;}

void nn_cmd_i 	   (int *old ,int new ,const char *fmt)	      { DO1 (old ,new ,I 	      	  ,fmt); }
void nn_cmd_d 	   (double *old ,double new ,const char *fmt) { DO1 (old ,new ,D 	      	  ,fmt); }
void nn_cmd_fmt    (int *old ,int new ,const char *fmt)       { DO1 (old ,new ,get_format_types   ,fmt); }
void nn_cmd_font   (int *old ,int new ,const char *fmt)       { DO1 (old ,new ,get_font_mapped_id ,fmt); }
void nn_cmd_cmap   (int *old ,int new ,const char *fmt)       { DO1 (old ,new ,zc_cmap_get_name   ,fmt); }
void nn_cmd_on_off (int *old ,int new ,const char *fmt)       { DO1 (old ,new ,on_or_off 	  ,fmt); }
void nn_cmd_t_f    (int *old ,int new ,const char *fmt)       { DO1 (old ,new ,true_or_false 	  ,fmt); }
void nn_cmd_w_v(LocType *old ,LocType new ,const char *fmt)   { DO1 (old ,new ,w_or_v 	  	  ,fmt); }

/**
 * linepen.color is a special case because realloc_graph_plots()
 * don't use  grdefaults = d_d to initialize the variable,
 * but change the value for each new set.
 */
void nn_cmd_linecolor (int *old ,int new ,const char *fmt)
{
  if (undo_on) {
    if (all || *old != new) {
      sprintf    (buf      ,fmt ,prefix ,new);
      nn_cmd_prt (buf);
      sprintf (buf_undo ,fmt ,prefix ,*old);
      nn_cmd_undo_snap ();
      if (old != NULL) *old = new;
      sprintf (buf_undo ,fmt ,prefix ,new);  /* for redo */
      nn_cmd_redo_snap ();                   /* for redo */
    }
  } else {
    /* write unconditionally the value */
    sprintf    (buf      ,fmt ,prefix ,new);
    nn_cmd_prt (buf);
  }
}

/******* MACRO for 2 variables *******/

#define DO2(o1,o2,n1,n2,f) \
  if (all            || \
      *old1  != new1 || \
      *old2  != new2  ) { \
    sprintf (buf      ,fmt ,prefix ,new1   ,new2); \
    nn_cmd_prt (buf); \
    if (undo_on) { \
      sprintf (buf_undo ,fmt ,prefix ,*old1 ,*old2); \
      nn_cmd_undo_snap (); \
      if (old1 != NULL) { \
	*old1 = new1; \
	*old2 = new2; \
      } \
      sprintf (buf_undo ,fmt ,prefix ,new1 ,new2); \
      nn_cmd_redo_snap (); \
    } \
  } 

/* 2 int */
void nn_cmd_2i (int *old1 ,int *old2 ,int new1 ,int new2 ,char *fmt)
{
  DO2 (old1 ,old2 ,new1 ,new2 ,fmt);
}

/* 2 doubles */
void nn_cmd_2d (double *old1 ,double *old2 ,double new1 ,double new2 ,char *fmt)
{
  DO2 (old1 ,old2 ,new1 ,new2 ,fmt);
}

/* For int values associated to a string we use the functions below */
static void nn_cmd_ns (int *old ,int new ,const char *fmt ,char *ns[] ,int n)
{
  if (*old < 0 || *old >= n || new < 0 || new >= n) return;

  if (all || *old != new) {
    sprintf (buf      ,fmt ,prefix ,ns[new]);
    nn_cmd_prt (buf);
    if (undo_on) {
      sprintf (buf_undo ,fmt ,prefix ,ns[*old]);
      nn_cmd_undo_snap ();
      if (old != NULL) *old = new;
      sprintf (buf_undo ,fmt ,prefix ,ns[new]);        /* for redo */
      nn_cmd_redo_snap ();                             /* for redo */
    }
  }
}

static char *inoutboth[] = {"in" ,"out" ,"both"};  /* TICKS_IN , TICKS_OUT , TICKS_BOTH */
void nn_cmd_in_out (int *old ,int new ,const char *fmt)
{
  nn_cmd_ns (old ,new ,fmt ,inoutboth ,3);
}

static char *paraperp[] = {"para" ,"perp"};       /* LAYOUT_PARALLEL , LAYOUT_PERPENDICULAR */
void nn_cmd_para_perp (int *old ,int new ,const char *fmt)
{
  nn_cmd_ns (old ,new ,fmt ,paraperp ,2);
}

static char *autospec[] = {"auto" ,"spec"};       /* TYPE_AUTO , TYPE_SPEC */
void nn_cmd_auto_spec (int *old ,int new ,const char *fmt)
{
  nn_cmd_ns (old ,new ,fmt ,autospec ,2);
}

static char *placemnt[] = {"normal" ,"opposite" ,"both"}; /* PLACEMENT_NORMAL , PLACEMENT_OPPOSITE , PLACEMENT_BOTH */
void nn_cmd_placemnt (PlacementType *old ,PlacementType new ,const char *fmt)
{
  nn_cmd_ns ((int*)old ,(int)new ,fmt ,placemnt ,3);
}

static char *tickspec[] = {"none" ,"ticks" ,"both"}; /* TICKS_SPEC_NONE ,TICKS_SPEC_MARKS  ,TICKS_SPEC_BOTH  */
void nn_cmd_tick_spec (int *old ,int new ,const char *fmt)
{
  nn_cmd_ns (old ,new ,fmt ,tickspec ,3);
}
static char *region_typ[] = {   /* synchronize with defines.h 212 */
   "above"    
  ,"below"
  ,"left"
  ,"right"
  ,"polyi"
  ,"polyo"
  ,"horizi"
  ,"verti"
  ,"horizo"
  ,"verto"
};
void nn_cmd_region_type (int *old ,int new ,const char *fmt)
{
  nn_cmd_ns (old ,new ,fmt ,region_typ ,10);
}

static char *scales_typ[] = {  /* SCALE_NORMAL , SCALE_LOG , SCALE_REC , SCALE_LOGIT , SCALE_DEGREES */
  "Normal" ,"Logarithmic" ,"Reciprocal" ,"Logit" ,"Degrees" ,"Unknown"};
void nn_cmd_graph_scale (int *old ,int gno ,int axisno  ,const char *fmt)
{
  int new = get_graph_scale (gno ,axisno);
  nn_cmd_ns (old ,new ,fmt ,scales_typ ,6);
}

/* 4 doubles */
void nn_cmd_4d (double *old1 ,double *old2 ,double *old3 ,double *old4
	       ,double new1 ,double new2  ,double new3  ,double new4
	       ,const char *fmt)
{
  if (all            ||
      *old1  != new1 ||
      *old2  != new2 ||
      *old3  != new3 ||
      *old4  != new4  ) {
    sprintf (buf      ,fmt ,prefix ,new1   ,new2  ,new3 ,new4);
    nn_cmd_prt (buf);
    if (undo_on) {
      sprintf (buf_undo ,fmt ,prefix ,*old1 ,*old2 ,*old3 ,*old4);
      nn_cmd_undo_snap ();
      if (old1 != NULL) {
	*old1 = new1;
	*old2 = new2;
	*old3 = new3;
	*old4 = new4;
      }
      sprintf (buf_undo ,fmt ,prefix ,new1 ,new2 ,new3 ,new4); /* for redo */
      nn_cmd_redo_snap ();                                     /* for redo */
    }
  }
}

void nn_cmd_world (world *old ,world new ,const char *fmt)
{
  if (all ||
      old->xg1 != new.xg1  ||
      old->xg2 != new.xg2  ||
      old->yg1 != new.yg1  ||
      old->yg2 != new.yg2 ) {
    sprintf (buf      ,fmt ,prefix ,new.xg1  ,new.yg1  ,new.xg2  ,new.yg2);
    nn_cmd_prt (buf);
    if (undo_on) {
      sprintf (buf_undo ,fmt ,prefix ,old->xg1 ,old->yg1 ,old->xg2 ,old->yg2);
      nn_cmd_undo_snap ();
      if (old != NULL) {
	old->xg1 = new.xg1;
	old->xg2 = new.xg2;
	old->yg1 = new.yg1;
	old->yg2 = new.yg2;
	something_changed = TRUE;
      }
      sprintf (buf_undo  ,fmt ,prefix ,new.xg1  ,new.yg1  ,new.xg2  ,new.yg2); /* for redo */
      nn_cmd_redo_snap ();                                                     /* for redo */
    }
  }
}

void nn_cmd_view (view *old ,view new ,const char *fmt)
{
  if (all ||
      old->xv1 != new.xv1  ||
      old->xv2 != new.xv2  ||
      old->yv1 != new.yv1  ||
      old->yv2 != new.yv2 ) {
    sprintf (buf      ,fmt ,prefix ,new.xv1  ,new.yv1  ,new.xv2  ,new.yv2);
    nn_cmd_prt (buf);
    if (undo_on) {
      sprintf (buf_undo ,fmt ,prefix ,old->xv1 ,old->yv1 ,old->xv2 ,old->yv2);
      nn_cmd_undo_snap ();
      if (old != NULL) {
	old->xv1 = new.xv1;
	old->xv2 = new.xv2;
	old->yv1 = new.yv1;
	old->yv2 = new.yv2;
	something_changed = TRUE;
      }
      sprintf (buf_undo ,fmt ,prefix ,new.xv1  ,new.yv1  ,new.xv2  ,new.yv2); /* for redo */
      nn_cmd_redo_snap ();                                                    /* for redo */
    }
  }
}


void nn_cmd_region_line (region *old ,int i ,const char *fmt)
{
  int j;
  if (rg[i].type != REGION_POLYI && rg[i].type != REGION_POLYO) {
    nn_cmd_4d (&(old->x1) ,&(old->y1) ,&(old->x2) ,&(old->y2) 
	       ,rg[i].x1   ,rg[i].y1   ,rg[i].x2   ,rg[i].y2 ,fmt);
  } else {
    if (all || rg[i].x != NULL) { 
      for (j = 0; j < rg[i].n; j++) {
	sprintf    (buf ,"@r%1d xy %.12g, %.12g\n" ,i ,rg[i].x[j], rg[i].y[j]);
	nn_cmd_prt (buf);                           //22  rajouter undo/redo
      }
    }
  }
}


/* strings */
//  pour undo: A VOIR <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

/**
 * return 0 (FALSE) if s1=s2, even if s1 or s2 are 0x0
 * if only one is 0x0, allocate it with same length as the other
 */
int gg_strcmp (char *s1 ,const char *s2)
{
  if (s1 == NULL && s2 == NULL)  return 0;
  if (s1 == NULL && *s2 == '\0') return 0;
  if (s1 == NULL && s2 != NULL) {
    s1 = malloc (strlen (s2));
    s1 = "\0";
    return TRUE;
  } else if (s1 != NULL && s2 == NULL) {
    s2 = malloc (strlen (s1));
    s2 = "\0";
    return TRUE;
  } else {
    return strcmp (s1 ,s2);
  }
}

void nn_cmd_str (char **old ,char *new ,const char *fmt)
{
  if (all || gg_strcmp (*old ,new)) {
    sprintf (buf_undo ,fmt ,prefix ,PSTRING (*old));
    sprintf (buf      ,fmt ,prefix ,PSTRING (new));
    nn_cmd_prt (buf);
    if (undo_on) {
      nn_cmd_undo_snap ();
      if (old != NULL) *old = copy_string (*old ,new);
      sprintf (buf_undo ,fmt ,prefix ,PSTRING (new));           /* for redo */
      nn_cmd_redo_snap ();                                      /* for redo */
    }
  }
}

void nn_cmd_strn (int n ,char *old ,char *new ,const char *fmt)
{
  if (all || gg_strcmp (old ,new)) {
    sprintf (buf_undo ,fmt ,prefix ,PSTRING (old));
    sprintf (buf      ,fmt ,prefix ,PSTRING (new));
    nn_cmd_prt (buf);
    if (undo_on) {
      nn_cmd_undo_snap ();
      if (old != NULL) strncpy (old ,new ,n);
      sprintf (buf_undo ,fmt ,prefix ,PSTRING (old));           /* for redo */
      nn_cmd_redo_snap ();                                      /* for redo */
    }
  }
}

/**
 * Services the collapsing of layers with follow_me_on
 */
// void nn_cmd_layer_collapsed (QDobject *old ,QDobject *new ,int all)
void nn_cmd_layer_collapsed (int i ,int layer)
{
  char prefix[64];
  int  gno;
  if (follow_me_on) {
    Qtype typ = objs[i].typ;
    int    id = objs[i].id;
    switch (typ) {
    case Q_Compound:
    case Q_Line:
    case Q_Polyline:
    case Q_Arc:
    case Q_Box:
    case Q_String:
    case Q_TimeStamp:
      if (objs[i].layer == layer) return;
      nn_cmd_undo_start (FALSE ,-1);
      nn_cmd_obj_prefix (&objs[i] ,prefix);
      nn_cmd_prefix (prefix);
      nn_cmd_i (&(objs[i].layer) ,layer ,"%s layer %d\n");
      break;
    case Q_Graph:
      sprintf (prefix ,"@ g%1d " ,id);
      nn_cmd_prefix (prefix);
      nn_cmd_i (&(objs[i].layer) ,layer ,"%s layer %d\n");
      break;
    case Q_Axis:
    case Q_Set:
    case Q_LegendBox:
      gno = objs[i].father_id;
      nn_cmd_undo_start (TRUE ,gno);
      if (follow_me_on) {
	if (gno != follow_gno) {
	  sprintf    (buf, "@ with g%d\n" ,gno);
	  nn_cmd_prt (buf);
	  follow_gno = gno;
	}
      }
      switch (typ) {
      case Q_Axis:
	sprintf (prefix ,"@    %s " ,axis_name[id]);
	break;
      case Q_Set:
	sprintf (prefix ,"@  s%d " ,id);
	break;
      case Q_LegendBox:
	sprintf (prefix ,"@    legend ");
	break;
      default:
	break;
      }
      nn_cmd_prefix (prefix);
      nn_cmd_i (&objs[i].layer ,layer ,"%s layer %d\n");
      nn_cmd_undo_end (TRUE ,gno);
      break;
    default:
      break;
    }
  }
}

/**
 *   nn_cmd_label_edited : is called when label column is changed in getree
 */
void nn_cmd_label_edited (char *old_text ,char *new_text)
{
  char *typname;
  switch (cur_obj_typ) {
  case Q_Compound:
  case Q_Line:
  case Q_Polyline:
  case Q_Arc:
  case Q_Box:
  case Q_String:
    typname = Qtype_name[cur_obj_typ];
    nn_cmd_undo_start (FALSE ,-1);
    sprintf (buf_undo ,"@ with %s %1d\n@    %s def \"%s\"\n" ,typname ,cur_obj_id ,typname ,old_text);
    nn_cmd_undo_snap ();
    sprintf (buf_undo ,"@ with %s %1d\n@    %s def \"%s\"\n" ,typname ,cur_obj_id ,typname ,new_text);
    nn_cmd_redo_snap ();
    nn_cmd_undo_end (FALSE ,-1);
    break;
  case Q_Axis:
    nn_cmd_undo_start (FALSE ,-1);
    sprintf (buf_undo ,"@    g%1d %s label \"%s\"\n" ,cur_parent_id ,axis_name[cur_obj_id] ,old_text);
    nn_cmd_undo_snap ();
    sprintf (buf_undo ,"@    g%1d %s label \"%s\"\n" ,cur_parent_id ,axis_name[cur_obj_id] ,new_text);
    nn_cmd_redo_snap ();
    nn_cmd_prt (buf_undo);
    nn_cmd_undo_end (FALSE ,-1);
    break;
  default:
    switch (cur_obj_typ) {
    case Q_Graph:
      nn_cmd_undo_start (TRUE ,cur_obj_id);
      sprintf (buf_undo ,"@ with g%1d\n@    title \"%s\"\n" ,cur_obj_id ,old_text);
      nn_cmd_undo_snap ();
      sprintf (buf_undo ,"@ with g%1d\n@    title \"%s\"\n" ,cur_obj_id ,new_text);
      nn_cmd_redo_snap ();
      nn_cmd_prt (buf_undo);
      nn_cmd_undo_end (TRUE ,cur_obj_id);
      break;
    case Q_Set:
      nn_cmd_undo_start (TRUE ,cur_parent_id);
      sprintf (buf_undo ,"@ with g%1d\n@   s%d  legend  \"%s\"\n" ,cur_parent_id ,cur_obj_id ,old_text);
      nn_cmd_undo_snap ();
      sprintf (buf_undo ,"@ with g%1d\n@   s%d  legend  \"%s\"\n" ,cur_parent_id ,cur_obj_id ,new_text);
      nn_cmd_redo_snap ();
      nn_cmd_prt (buf_undo);
      nn_cmd_undo_end (TRUE ,cur_parent_id);
      break;
    default:
      errmsg ("# nn_cmd_label_edited internal error");
      break;
    }
    break;
  }
  nn_cmd_prt (buf_undo);
}


/************* UNDO INTERFACE ****************/

#ifdef HAVE_LIBUNDO

static int nb_local_undo;
/**
 *  nn_cmd_undo_new initialize the undo/redo interface
 */
void nn_cmd_undo_new (void)
{
  if (heap == NULL) {
    heap = undo_new ();
    err = undo_set_history_logical (heap ,1);
    if ( (err = undo_set_memory_limit (heap ,0x1000000L)) ) {
      fprintf (stderr ,"nn_cmd_undo_new: undo_set_memory_limit error = %d\n",err );
      return;
    }
    buf_undo = undo_malloc (heap ,20000);
    /* Seems necessary to avoid redo problems with the first sequence */
    sprintf (buf_undo ,"# Start of undo stack\n");
    nn_cmd_undo_snap ();
    un_index = 0;
    un_index_last = 0;
    gg_undo_update (0 ,0);   // gg_undo_update is GUI dependent
  }
}

void nn_cmd_undo_destroy (void)
{
  if (heap != NULL) {
    undo_destroy (heap);
    heap = NULL;
  }
}

int  nn_cmd_undo_undo (void)
{
  static char *s0;
  int i ,err;
  int re_index;
  if (un_index_last == 0 || un_index == 0) return RETURN_FAILURE;
  if (nb_local_undo == 0)                  return RETURN_FAILURE;
  for (i = 0; i < undo_count[un_index]; i++) {
    err = undo_undo (heap);  /* get undo line */
    s0 = buf_undo;
    while ((s0 = strtok (s0 ,"\n"))  != NULL) {
      if (s0[0] == '@') s0++;
      scanner (s0);
      s0 = NULL;
    }
    err = undo_undo (heap);  /* skip redo line */
    if (err != UNDO_NOERROR && err != UNDO_NODO) {
      fprintf (stderr ,"nn_cmd_undo_undo: ERROR %d\n" ,err);
      return RETURN_FAILURE;
    }
  }
  if (un_index > 0) un_index--;
  re_index = un_index_last - un_index;
  gg_undo_update (un_index ,re_index);   // gg_undo_update is GUI dependent
  return RETURN_SUCCESS;
}

int  nn_cmd_undo_redo (void)
{
  static char *s0;
  int i ,err;
  int re_index;
  if (un_index_last == 0 || un_index == un_index_last) return RETURN_FAILURE;
  if (nb_local_undo == 0)                              return RETURN_FAILURE;
  if (un_index < un_index_last) un_index++;
  for (i = 0; i < undo_count[un_index]; i++) {
    err = undo_redo (heap);    /* skip undo line */
    err = undo_redo (heap);    /* get  redo line */
    s0 = buf_undo;
    while ((s0 = strtok (s0 ,"\n"))  != NULL) {
      if (s0[0] == '@') s0++;
      scanner (s0);
      s0 = NULL;
    }
    if (err != UNDO_NOERROR && err != UNDO_NODO) {
      fprintf (stderr ,"nn_cmd_undo_redo: ERROR %d\n" ,err);
      return RETURN_FAILURE;
    }
  }
  re_index = un_index_last - un_index;
  gg_undo_update (un_index ,re_index);   // gg_undo_update is GUI dependent
  return RETURN_SUCCESS;
}

static void nn_cmd_undo_snap ()
{
  if (discard_undo) {
    err = undo_undo (heap);  /* erase last redo value */
  } else {
    undo_snapshot (heap);
    nb_local_undo++;
  }
  something_changed = TRUE;
}

static void nn_cmd_redo_snap ()
{
  undo_snapshot (heap);      /* snap redo value */
  something_changed = TRUE;
}

/**
 *   nn_cmd_undo_start :   start to store change commands in buf_undo
 */
void nn_cmd_undo_start (int with_gno ,int gno)
{
  if (discard_undo) return;
  if (with_gno) {
    sprintf (buf_undo ,"@ With G%d\n" ,gno);
    if (gno != follow_gno) {
      nn_cmd_prt (buf_undo);
      follow_gno = gno;
    }
    nn_cmd_undo_snap ();
    nn_cmd_redo_snap (); 
    nb_local_undo = 1;
  } else {
    nb_local_undo = 0;
  }
  un_index++;
  un_index_last = un_index;
  something_changed = FALSE;
}

/**
 * nn_cmd_undo_end :  to finalize the records for  future undo(s)
 */
void nn_cmd_undo_end (int with_gno ,int gno)
{
  int re_index;
  if (discard_undo) return;
  if (with_gno) {
    if (something_changed) {
      sprintf (buf_undo ,"@ With G%d\n" ,gno);
      nn_cmd_undo_snap ();
      nn_cmd_redo_snap ();
      nb_local_undo++;
      undo_count[un_index] = nb_local_undo;
      un_index_last = un_index;
    } else {
      /* Wipe the "WITH gno" if nothing to undo after */
      nn_cmd_undo_snap ();
      nn_cmd_redo_snap ();
      un_index--;
    }
  } else {
    if (something_changed) {
      undo_count[un_index] = nb_local_undo;
      un_index_last = un_index;
    } else {
      un_index--;
    }
  }
  re_index = un_index_last - un_index;
  gg_undo_update (un_index ,re_index);   /* // gg_undo_update is GUI dependent */
  something_changed = FALSE;
}


#else  /* HAVE_LIBUNDO not defined : dummy interface */

void nn_cmd_undo_new     (void)
{
  buf_undo = malloc (MAX_STRING_LENGTH + 2000);
}
void nn_cmd_undo_destroy (void)
{
  free (buf_undo);
}
int  nn_cmd_undo_undo    (void) {return 0;}
int  nn_cmd_undo_redo    (void) {return 0;}
static void nn_cmd_undo_snap (void) { }
static void nn_cmd_redo_snap (void) { }
void nn_cmd_undo_start   (int with_gno ,int gno) { }
void nn_cmd_undo_end     (int with_gno ,int gno) { }

#endif /* HAVE_LIBUNDO */
