/*
 * The line plot widget.  Sort of like the xy plot, but has implicit x
 * axis running from start -> (start + num_points - 1).
 */

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>

#include <math.h>

#include "LinePlotP.h"

#ifndef VAL_MACRO
static double val P((LinePlotWidget p, int i));
static double val(p, i)
     LinePlotWidget p;
     int i; 
{
     XtPointer ptr = _ptr(p, i);

     double ret = 
	  ((p)->lineplot.type == AtDouble ? *((double *)ptr) : 
	   (p)->lineplot.type == AtFloat ? (double)*((float *)ptr) : 
	   (p)->lineplot.type == AtInt ? (double)*((int *)ptr) :
	   0.0);
     
     return ret; 
}
#endif 

/*
 * Forward declare all the private widgetclass routines
 */

static void LinePlotInitialize P((LinePlotWidget, LinePlotWidget));
static void LinePlotSetValues P((LinePlotWidget, LinePlotWidget,
				 LinePlotWidget));
     
static void LinePlotDraw P((Display *, Window, AtPlotWidget, Region, int));
static void LinePlotDrawIcon P((Display *, Window, AtPlotWidget,
				int, int, int, int, Region));
static void LinePlotDrawPS P((FILE *, AtPlotWidget, AtScale *, AtScale *));
static void LinePlotDrawIconPS P((FILE*, AtPlotWidget, int, int, int, int));
static void LinePlotRecalc P((AtPlotWidget, AtScale *, AtScale *, 
			       int from, int to));

LinePlotClassRec linePlotClassRec = {
  { /* core fields */
    /* superclass		*/	(WidgetClass) &atPlotClassRec,
    /* class_name		*/	"LinePlot",
    /* widget_size		*/	sizeof(LinePlotRec),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	(XtInitProc) LinePlotInitialize,
    /* initialize_hook		*/	NULL,
    /* pad			*/	NULL,
    /* pad			*/	NULL,
    /* pad			*/	0,
    /* resources		*/	NULL,
    /* num_resources		*/	0,
    /* xrm_class		*/	NULLQUARK,
    /* pad			*/	FALSE,
    /* pad			*/	FALSE,
    /* pad			*/	FALSE,
    /* pad			*/	FALSE,
    /* destroy			*/	NULL,
    /* pad			*/	NULL,
    /* pad			*/	NULL,
    /* set_values		*/	(XtSetValuesFunc) LinePlotSetValues,
    /* set_values_hook		*/	NULL,
    /* pad			*/	NULL,
    /* get_values_hook		*/	NULL,
    /* pad			*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* pad			*/	NULL,
    /* pad			*/	NULL,
    /* pad			*/	NULL,
    /* pad			*/	NULL
  },
  { /* atPlot fields */
    /* draw			*/	LinePlotDraw,
    /* draw_icon		*/	LinePlotDrawIcon,
    /* drawPS			*/	LinePlotDrawPS,
    /* draw_iconPS		*/	LinePlotDrawIconPS,
    /* recalc			*/	LinePlotRecalc,
    /* checkhit			*/	NULL

  },
  { /* linePlot fields */
    /* empty			*/	0
  }
};

WidgetClass linePlotWidgetClass = (WidgetClass)&linePlotClassRec;

/*****************************************************************
 *
 * Initialize
 *
 * (Is not a problem yet as we inherit the AtPlot stuff)
 */
static void LinePlotInitialize(old, new)
LinePlotWidget old, new; 
{
     /* nothing yet */
}

/*****************************************************************
 *
 * SetValues
 *
 * (Again, no private resources yet!)
 */
static void LinePlotSetValues(old, req, new)
LinePlotWidget old, req, new; 
{
     /* nothing yet */
}


/*****************************************************************
 *
 * These routines are the ones called by the parent plot widget
 */
#define lp ((LinePlotWidget)self)

/*
 * Draw the line clipped by the given region.
 */
static void LinePlotDraw(dpy, win, self, region, refresh)
     Display *dpy;
     Window win;
     AtPlotWidget self;
     Region region;
     int refresh; 
{
  if (lp->lineplot.old_pixpts) {
    if (lp->plot.fast_update && refresh) {
      /* 
       * We are in fast update mode, doing a refresh and have old
       * pixpoints, so draw them to "erase" the old line first
       */
      XDrawLines(dpy, win, lp->plot.gc, lp->lineplot.old_pixpts,
		 lp->lineplot.old_num_points, CoordModeOrigin);
    }
    XtFree((char *)lp->lineplot.old_pixpts);
    lp->lineplot.old_pixpts = NULL;
    lp->lineplot.old_num_points = 0;
  }
  
  if (region) XSetRegion(dpy, lp->plot.gc, region);
  XDrawLines(dpy, win, lp->plot.gc, lp->lineplot.pixpts,
	     lp->lineplot.num_points, CoordModeOrigin);
  if (region) XSetClipMask(dpy, lp->plot.gc, None);
}

/*
 * draw the "icon" in the given place.
 */
static void LinePlotDrawIcon(dpy, win, self, x1, y1, width, height, region)
Display *dpy;
Window win;
AtPlotWidget self;
int x1, y1, width, height;
Region region;
{
     if (region) XSetRegion(dpy, lp->plot.gc, region);
     y1 += height >> 1;
     XDrawLine(dpy, win, lp->plot.gc, x1, y1, x1 + width, y1);
     if (region) XSetClipMask(dpy, lp->plot.gc, None);
     
}

/*
 * postscript stuff
 */
static void LinePlotDrawPS(fp, self, xs, ys)
FILE *fp;
AtPlotWidget self;
AtScale *xs, *ys; 
{
  int i = 0, count = 0;
  
  fprintf(fp, "%% BeginObject: LinePlot\ngsave\n");
  AtPlotPSLineStyle(fp, self);
  
  for (i = 1; i < lp->lineplot.num_points; i++) {
    if (!count) {
      fprintf(fp, "%d %d M ", AtScaleUserToPixel(xs, (double)(i - 1)),
	      AtScaleUserToPixel(ys, val(lp, i - 1)));
    }
    fprintf(fp, "%d %d L\n", AtScaleUserToPixel(xs, (double)i),
	    AtScaleUserToPixel(ys, val(lp, i)));
    if (++count > 50) {
      fprintf(fp, "stroke\n");
      count = 0;
    }
  }
  fprintf(fp, "%sgrestore\n%% EndObject\n", count ? "stroke\n" : "");
}


static void LinePlotDrawIconPS(fp, self, x1, y1, width, height)
     FILE *fp;
     AtPlotWidget self;
     int x1, y1, width, height;
{
  fprintf(fp, "gsave "); 
  AtPlotPSLineStyle(fp, self);
  fprintf(fp, "%d %d M %d 0 rlineto stroke grestore\n", x1,
	  y1 - (height >> 1), width);
}


/*
 * Recalc the data according to the passed x and y scales
 */
static void LinePlotRecalc(self, xs, ys, from, to)
AtPlotWidget self;
AtScale *xs, *ys; 
int from, to;
{
  int i;
  
  if (from > to) {
    from = 0;
    to = lp->lineplot.num_points;
  }
  for (i = from; i <= to; i++) {
    lp->lineplot.pixpts[i].x = AtScaleUserToPixel(xs, (double)i);
    lp->lineplot.pixpts[i].y = AtScaleUserToPixel(ys, val(lp, i));
  }
}
#undef lp

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))

/*****************************************************************
 *
 * These are the exported "member" routines
 */
void LinePlotAttatchData(lp, data, type, stride, start, num)
LinePlotWidget lp;
XtPointer data;
AtType type;
Cardinal stride, start, num;
{
  BoundingBox bb;
  int i;

  XtCheckSubclass((Widget)lp, linePlotWidgetClass,
		  "LinePlotAttatchData needs a LinePlot object");

  if (lp->plot.fast_update) {
    /* Save a copy of the current pixpoints and request a refresh */
    if (lp->lineplot.old_pixpts) {
      /* Is this an error???? */
#ifdef DEBUG
      fprintf(stderr, "In LinePLotAttatchData - old pixpts still current!\n");
#endif
      XtFree((char *)lp->lineplot.old_pixpts);
    }
    lp->lineplot.old_pixpts = lp->lineplot.pixpts;
    lp->lineplot.old_num_points = lp->lineplot.num_points; 
  } else 
    XtFree((char *)lp->lineplot.pixpts);
  
  lp->lineplot.pixpts = (XPoint *)XtMalloc(num * sizeof(XPoint));
  lp->lineplot.num_points = num;
  lp->lineplot.data = data;
  lp->lineplot.type = type;
  lp->lineplot.stride = stride; 
  lp->lineplot.start = start;
  
  bb.xmax = start + num - 1;
  bb.xmin = start;
  bb.ymax = -HUGE_VAL;
  bb.ymin = HUGE_VAL;
  
  for (i = 0; i < num; i++) {
    register double v = val(lp, i);
    
    bb.ymax = max(bb.ymax, v);
    bb.ymin = min(bb.ymin, v);
  }
  
  AtPlotterPlotDataChanged((AtPlotWidget)lp, &bb, lp->plot.fast_update);
}

/*
 * Need to use eval rather than just c-style as hack-local-variables
 * is called after the c-mode-hook. 
 *
 * Local Variables:
 * eval: (set-c-style 'GNU)
 * End:
 */
