/*
 * file:     plot.c
 * author:   Wes Barris
 * date:     5/27/92
 * purpose:  handles background functionality
 *
 * copyright info:
 *
 *                           @Copyright 1992
 *      Research Equipment Inc. dba Minnesota Supercomputer Center
 *
 * RESTRICTED RIGHTS LEGEND
 *
 * Use, duplication, or disclosure of this software and its documentation
 * by the Government is subject to restrictions as set forth in subdivision
 * { (b) (3) (ii) } of the Rights in Technical Data and Computer Software
 * clause at 52.227-7013.
 */

#include "icop.h"
#include "proto.h"
#include <math.h>
#include <gl/gl.h>
#include <gl/image.h>
#include <X11/StringDefs.h>
#include <X11/Xirisw/GlxMDraw.h>
#include <Xm/Xm.h>
#include <Xm/ToggleB.h>
#include <Xm/Frame.h>

extern ICOP	icop;
extern Widget	plotType[5];
extern Widget	standard[2];
extern char	*tmpname;
extern int	havefile;

int		haveplot;
static long	zfar;
static double	xmin, xmid, xmax, ymin, ymid, ymax, zmin, zmid, zmax;
static int omx, mx, omy, my;	/* old and new mouse position*/
static unsigned long	background;

static GLXconfig glxConfig [] = {
    { GLX_NORMAL, GLX_RGB, TRUE },
    { GLX_NORMAL, GLX_DOUBLE, TRUE },
    { 0, 0, 0 }
};

static char defaultTranslations[] =
    "<Btn1Down>:        arm() \n\
     <Btn1Up>:          disarm() \n\
     <Btn1Motion>:      move() \n ";

static XtActionsRec actionsTable[] = {
    {"arm",	MouseActionCB},
    {"disarm",	MouseActionCB},
    {"move",	MouseActionCB},
};

Matrix objmat = {
    {1.0, 0.0, 0.0, 0.0},
    {0.0, 1.0, 0.0, 0.0},
    {0.0, 0.0, 1.0, 0.0},
    {0.0, 0.0, 0.0, 1.0},
};

Matrix idmat = {
    {1.0, 0.0, 0.0, 0.0},
    {0.0, 1.0, 0.0, 0.0},
    {0.0, 0.0, 1.0, 0.0},
    {0.0, 0.0, 0.0, 1.0},
};



#define NOTHING 0
#define ORIENT 1

static int mode = 0;


void getrow(IMAGE*, short*, int, int);
void iclose(IMAGE*);


static void
PlotYSLimits(void)
{
   short	cv[3];
   float	vert[2];

   if (XmToggleButtonGetState(standard[0])) {	/* Limits for YIQ (NTSC) */
      bgnpolygon();
         cv[0] = cv[1] = cv[2] = 250; c3s(cv);
         vert[0] = 0.0;		vert[1] = 0.0;		v2f(vert);
         vert[0] = 0.792;	vert[1] = 0.0;		v2f(vert);
         cv[0] = cv[1] = cv[2] = 0; c3s(cv);
         vert[0] = 0.792;	vert[1] = 1.0;		v2f(vert);
         vert[0] = 0.0;		vert[1] = 1.0;		v2f(vert);
      endpolygon();
      cv[0] = cv[1] = cv[2] = 30; c3s(cv);
      bgnpolygon();
         vert[0] = 0.0;		vert[1] = 0.0;		v2f(vert);
         vert[0] = 0.0;		vert[1] = 1.0;		v2f(vert);
         vert[0] = 0.6255;	vert[1] = 0.3745;	v2f(vert);
         vert[0] = 0.251;	vert[1] = 0.0;		v2f(vert);
      endpolygon();
      cv[0] = 255; cv[1] = 0; cv[2] = 0; c3s(cv);
      }
   else {		/* Limits for YUV (PAL) */
      bgnpolygon();
         cv[0] = cv[1] = cv[2] = 250; c3s(cv);
         vert[0] = 0.0;		vert[1] = 0.0;		v2f(vert);
         vert[0] = 0.792;	vert[1] = 0.0;		v2f(vert);
         cv[0] = cv[1] = cv[2] = 0; c3s(cv);
         vert[0] = 0.792;	vert[1] = 1.0;		v2f(vert);
         vert[0] = 0.0;		vert[1] = 1.0;		v2f(vert);
      endpolygon();
      cv[0] = cv[1] = cv[2] = 30; c3s(cv);
      bgnpolygon();
         vert[0] = 0.0;		vert[1] = 0.0;		v2f(vert);
         vert[0] = 0.0;		vert[1] = 1.334;	v2f(vert);
         vert[0] = 0.8365;	vert[1] = 0.4975;	v2f(vert);
         vert[0] = 0.339;	vert[1] = 0.0;		v2f(vert);
      endpolygon();
      cv[0] = 255; cv[1] = 0; cv[2] = 0; c3s(cv);
      }
}


static void
PlotYSText(void)
{
   short	cv[3];
   float	vert[2];

   if (XmToggleButtonGetState(standard[0])) {	/* Limits for YIQ (NTSC) */
      cv[0] = cv[1] = cv[2] = 200; c3s(cv);
      cmov(0.1, 0.4, 0.0);
      charstr("Legal Colors");
      cv[0] = cv[1] = cv[2] = 20; c3s(cv);
      cmov(0.4, 0.1, 0.0);
      charstr("Under Saturated");
      cv[0] = cv[1] = cv[2] = 200; c3s(cv);
      cmov(0.4, 0.8, 0.0);
      charstr("Over Saturated");
      }
   else {		/* Limits for YUV (PAL) */
      cv[0] = cv[1] = cv[2] = 200; c3s(cv);
      cmov(0.1, 0.5, 0.0);
      charstr("Legal Colors");
      cv[0] = cv[1] = cv[2] = 20; c3s(cv);
      cmov(0.45, 0.05, 0.0);
      charstr("Under Saturated");
      cv[0] = cv[1] = cv[2] = 200; c3s(cv);
      cmov(0.45, 0.9, 0.0);
      charstr("Over Saturated");
      }
}


static void
PlotYS(void)
{
   int		ntsc;
   int		x, y;
   int		red, green, blue;
   double	sat, yy, i, q;
   double	i_new, q_new;
   float	vert[2];
/*
 * Open the image file and plot Y versus S.
 */
   ntsc = XmToggleButtonGetState(standard[0]);
   bgnpoint();
   for(y=0; y<icop.image.height; y++) {
      for (x=0; x<(havefile == IMAGE_FILE?icop.image.width:1); x++) {
         red   = icop.image.origbuf[y*icop.image.width+x] & 0xff;
         green = icop.image.origbuf[y*icop.image.width+x] >> 8 & 0xff;
         blue  = icop.image.origbuf[y*icop.image.width+x] >> 16 & 0xff;
         if (ntsc)
            rgbtoyiq(red, green, blue, &yy, &i, &q);
         else /* if standard == PAL */
            rgbtoyuv(red, green, blue, &yy, &i, &q);
         sat = sqrt(i*i + q*q);
         vert[0] = sat;
         vert[1] = yy;
         cpack(icop.image.origbuf[y*icop.image.width+x]);
         v2f(vert);
         }
      }
   endpoint();
}


static void
SetLimits(void)
{
   if (XmToggleButtonGetState(plotType[1])) {
      xmin = 0.0;
      xmax = 255.0;
      ymin = 0.0;
      ymax = 255.0;
      zmin = 0.0;
      zmax = 255.0;
      }
   else if (XmToggleButtonGetState(plotType[2])) {
      xmin = 0.0;
      xmax = 1.0;
      ymin = -0.596;
      ymax = 0.596;
      zmin = -0.523;
      zmax = 0.523;
      }
   else if (XmToggleButtonGetState(plotType[3])) {
      xmin = 0.0;
      xmax = 1.0;
      ymin = -0.436;
      ymax = 0.436;
      zmin = -0.615;
      zmax = 0.615;
      }
   xmid = 0.5*(xmin + xmax);
   ymid = 0.5*(ymin + ymax);
   zmid = 0.5*(zmin + zmax);
}


static void
PlotAxes(int type)
{
   short	cv[3];
   float	vert[3];

   cv[0] = 0;
   cv[1] = 0;
   cv[2] = 125;
   c3s(cv);
   if (type == RGB_3D) {
      cmov(0.0, 0.0, 0.0);
      charstr(" Black");
      cmov(xmax, 0.0, 0.0);
      charstr(" Red");
      cmov(0.0, ymax, 0.0);
      charstr(" Green");
      cmov(0.0, 0.0, zmax);
      charstr(" Blue");
      cmov(0.0, ymax, zmax);
      charstr(" Cyan");
      cmov(xmax, 0.0, zmax);
      charstr(" Magenta");
      cmov(xmax, ymax, 0.0);
      charstr(" Yellow");
      cmov(xmax, ymax, zmax);
      charstr(" White");
      }
   else if (type == YIQ_3D) {
      cmov(xmin, ymid, zmid);
      charstr(" Ymin");
      cmov(xmax, ymid, zmid);
      charstr(" Ymax");
      cmov(xmid, ymin, zmid);
      charstr(" Imin");
      cmov(xmid, ymax, zmid);
      charstr(" Imax");
      cmov(xmid, ymid, zmin);
      charstr(" Qmin");
      cmov(xmid, ymid, zmax);
      charstr(" Qmax");
      }
   else if (type == YUV_3D) {
      cmov(xmin, ymid, zmid);
      charstr(" Ymin");
      cmov(xmax, ymid, zmid);
      charstr(" Ymax");
      cmov(xmid, ymin, zmid);
      charstr(" Umin");
      cmov(xmid, ymax, zmid);
      charstr(" Umax");
      cmov(xmid, ymid, zmin);
      charstr(" Vmin");
      cmov(xmid, ymid, zmax);
      charstr(" Vmax");
      }
   bgnline();
      vert[0] = xmin;	vert[1] = ymin;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmax;	vert[1] = ymin;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmax;	vert[1] = ymax;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmin;	vert[1] = ymax;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmin;	vert[1] = ymin;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmin;	vert[1] = ymin;	vert[2] = zmax;	v3f(vert);
      vert[0] = xmax;	vert[1] = ymin;	vert[2] = zmax;	v3f(vert);
      vert[0] = xmax;	vert[1] = ymax;	vert[2] = zmax;	v3f(vert);
      vert[0] = xmin;	vert[1] = ymax;	vert[2] = zmax;	v3f(vert);
      vert[0] = xmin;	vert[1] = ymin;	vert[2] = zmax;	v3f(vert);
   endline();
   bgnline();
      vert[0] = xmax;	vert[1] = ymin;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmax;	vert[1] = ymin;	vert[2] = zmax;	v3f(vert);
   endline();
   bgnline();
      vert[0] = xmax;	vert[1] = ymax;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmax;	vert[1] = ymax;	vert[2] = zmax;	v3f(vert);
   endline();
   bgnline();
      vert[0] = xmin;	vert[1] = ymax;	vert[2] = zmin;	v3f(vert);
      vert[0] = xmin;	vert[1] = ymax;	vert[2] = zmax;	v3f(vert);
   endline();
}


static void
PlotRGB(int stride)
{
   int		red, green, blue;
   int		x, y;
   short	vert[3];
/*
 * Plot RGB from the image buffer.
 */
   bgnpoint();
   for(y=0; y<icop.image.height; y+=stride) {
      for (x=0; x<(havefile == IMAGE_FILE?icop.image.width:1); x+=stride) {
         red   = icop.image.origbuf[y*icop.image.width+x] & 0xff;
         green = icop.image.origbuf[y*icop.image.width+x] >> 8 & 0xff;
         blue  = icop.image.origbuf[y*icop.image.width+x] >> 16 & 0xff;
         vert[0] = red;
         vert[1] = green;
         vert[2] = blue;
         c3s(vert);
         v3s(vert);
         }
      }
   endpoint();
}


static void
PlotYIQ(int stride)
{
   int		red, green, blue;
   int		x, y;
   float	vert[3];
   double	yy, i, q;
/*
 * Plot YIQ from the image buffer.
 */
   bgnpoint();
   for(y=0; y<icop.image.height; y+=stride) {
      for (x=0; x<(havefile == IMAGE_FILE?icop.image.width:1); x+=stride) {
         red   = icop.image.origbuf[y*icop.image.width+x] & 0xff;
         green = icop.image.origbuf[y*icop.image.width+x] >> 8 & 0xff;
         blue  = icop.image.origbuf[y*icop.image.width+x] >> 16 & 0xff;
         rgbtoyiq(red, green, blue, &yy, &i, &q);
         vert[0] = yy;
         vert[1] = i;
         vert[2] = q;
         cpack(icop.image.origbuf[y*icop.image.width+x]);
         v3f(vert);
         }
      }
   endpoint();
}


static void
PlotYUV(int stride)
{
   int		red, green, blue;
   int		x, y;
   float	vert[3];
   double	yy, u, v;
/*
 * Plot YUV from the image buffer.
 */
   bgnpoint();
   for(y=0; y<icop.image.height; y+=stride) {
      for (x=0; x<(havefile == IMAGE_FILE?icop.image.width:1); x+=stride) {
         red   = icop.image.origbuf[y*icop.image.width+x] & 0xff;
         green = icop.image.origbuf[y*icop.image.width+x] >> 8 & 0xff;
         blue  = icop.image.origbuf[y*icop.image.width+x] >> 16 & 0xff;
         rgbtoyuv(red, green, blue, &yy, &u, &v);
         vert[0] = yy;
         vert[1] = u;
         vert[2] = v;
         cpack(icop.image.origbuf[y*icop.image.width+x]);
         v3f(vert);
         }
      }
   endpoint();
}


/*
 * This routine is used to initialize a glx widget.  It only has to set the
 * viewport so it can also be used for resizing.
 */
void
InitPlotCB(Widget w, XtPointer client_data, char *call_data)
{
   switch (((GlxDrawCallbackStruct *)call_data)->reason) {
      case GlxCR_GINIT:
      case GlxCR_RESIZE:
         GLXwinset(XtDisplay(w), ((GlxDrawCallbackStruct *)call_data)->window);
         viewport(0, (Screencoord)((GlxDrawCallbackStruct *)call_data)->width-1,
                  0, (Screencoord)((GlxDrawCallbackStruct *)call_data)->height-1);
         zfar = getgdesc(GD_ZMAX);
         doublebuffer();
         readsource(SRC_FRONT);
         break;
      default:
         fprintf(stderr, "WARNING -- Unknown reason in InitPlotCB.\n");
      }
}


static void
PlotRGBCube(int stride) {

   /*czclear(0, zfar);*/
   cpack(background); clear();

   perspective(600.0, 1.0, 200.0, 700.0);
   translate(-xmid, -ymid, -(zmid+450.0));
   multmatrix(objmat);
   PlotRGB(stride);
   PlotAxes(RGB_3D);
   swapbuffers();
   gflush();
}


static void
PlotYIQCube(int stride) {

   /*czclear(0, zfar);*/
   cpack(background); clear();

   perspective(600.0, 1.0, 0.5, 4.0);
   translate(-xmid, -ymid, -(zmid+1.8));
   multmatrix(objmat);
   PlotYIQ(stride);
   PlotAxes(YIQ_3D);
   swapbuffers();
   gflush();
}


static void
PlotYUVCube(int stride) {

   /*czclear(0, zfar);*/
   cpack(background); clear();

   perspective(600.0, 1.0, 0.5, 4.0);
   translate(-xmid, -ymid, -(zmid+1.8));
   multmatrix(objmat);
   PlotYUV(stride);
   PlotAxes(YUV_3D);
   swapbuffers();
   gflush();
}


static void
reset(void)
{
   pushmatrix();
   loadmatrix(idmat);
   getmatrix(objmat);
   popmatrix();
}

static void
orient(int omx, int omy, int mx, int my)
{
   pushmatrix();			/* duplicate top matrix */
   loadmatrix(idmat);			/* replace top matrix with idmat */
   translate(xmid, ymid, zmid);		/* premultiply top matrix */
   rotate(2*(mx-omx), 'y');		/* premultiply top matrix */
   rotate(2*(my-omy), 'x');		/* premultiply top matrix */
   translate(-xmid, -ymid, -zmid);	/* premultiply top matrix */
   multmatrix(objmat);			/* premultiply top matrix with objmat */
   getmatrix(objmat);			/* store the result in objmat */
   popmatrix();				/* remove top matrix from the stack */
}


/*
 * This routine will draw the icop background.
 */
void
DrawPlotCB(Widget w, XtPointer client_data, char *call_data)
{
   XEvent	event_return;
   short	cv[3];

   while (XCheckWindowEvent(XtDisplay(w),
          XtWindow(w),
          ExposureMask, &event_return)) {
      /*printf("Discarded an expose event.\n");*/
      ;
      }

   GLXwinset(XtDisplay(icop.toplevel), XtWindow(w));
/*
 * Do we already have an initialized data image?
 */
   if (haveplot) {
      lrectwrite(0, 0, icop.plot.width-1, icop.plot.height-1, icop.plot.buf);
      swapbuffers();
      }
   else {
      /*cv[0] = 0;
      cv[1] = 0;
      cv[2] = 0;
      c3s(cv);*/
      cpack(background);
      clear();
      swapbuffers();
      clear();
      BusyCursorCB((Widget)NULL, (XtPointer)NULL, (char *)NULL);
      if (XmToggleButtonGetState(plotType[0])) {
         ortho2((Coord)0.0, (Coord)1.0, (Coord)0.0, (Coord)1.0);
         PlotYSLimits();
         if (havefile)
            PlotYS();
         else
            PlotYSText();
         swapbuffers();
         }
      else if (XmToggleButtonGetState(plotType[1])) {
         SetLimits();
         reset();
         PlotRGBCube(1);
         }
      else if (XmToggleButtonGetState(plotType[2])) {
         SetLimits();
         reset();
         PlotYIQCube(1);
         }
      else if (XmToggleButtonGetState(plotType[3])) {
         SetLimits();
         reset();
         PlotYUVCube(1);
         }
      lrectread(0, 0, icop.plot.width-1, icop.plot.height-1, icop.plot.buf);
      haveplot = 1;
      UnBusyCursorCB((Widget)NULL, (XtPointer)NULL, (char *)NULL);
      }
}


static void
MouseActionCB(Widget w, XEvent *event, String *params, Cardinal *num_params)
{

   if (XmToggleButtonGetState(plotType[0]) ||
       XmToggleButtonGetState(plotType[0]))
      return;
   switch(event->type) {
   case ButtonPress:
      switch(event->xbutton.button) {
      case Button1:
         GLXwinset(XtDisplay(icop.toplevel), XtWindow(icop.plot.w));
         mode = ORIENT;
         omx = mx = event->xbutton.x;
         omy = my = event->xbutton.y;
         break;
         }
      break;
   case ButtonRelease:
      switch(event->xbutton.button) {
      case Button1:
         BusyCursorCB((Widget)NULL, (XtPointer)NULL, (char *)NULL);
         if (XmToggleButtonGetState(plotType[1]))
            PlotRGBCube(1);
         else if (XmToggleButtonGetState(plotType[2]))
            PlotYIQCube(1);
         else if (XmToggleButtonGetState(plotType[3]))
            PlotYUVCube(1);
         lrectread(0, 0, icop.plot.width-1, icop.plot.height-1, icop.plot.buf);
         UnBusyCursorCB((Widget)NULL, (XtPointer)NULL, (char *)NULL);
         mode = NOTHING;
         break;
         }
      break;
   case MotionNotify:
      if (mode == ORIENT && event->xmotion.state & Button1Mask) {
         omx = mx;
         omy = my;
         mx = event->xbutton.x;
         my = event->xbutton.y;
         orient(omx, omy, mx, my);
         if (XmToggleButtonGetState(plotType[1]))
            PlotRGBCube((int)(0.01*icop.image.width));
         else if (XmToggleButtonGetState(plotType[2]))
            PlotYIQCube((int)(0.02*icop.image.width));
         else if (XmToggleButtonGetState(plotType[3]))
            PlotYUVCube((int)(0.02*icop.image.width));
         }
      break;
    }
}


/*
 * Set the initial preferences.
 */
Widget
PlotInit(Widget parent, Widget neighbor)
{
   Arg		args[10];
   Widget	glPlot, f1;
   int		n;

   XtAppAddActions(icop.appContext, actionsTable, XtNumber(actionsTable));
   n = 0;
   XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
   XtSetArg(args[n], XmNtopWidget, neighbor); n++;
   XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
   XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
   XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
   f1 = XtCreateManagedWidget("f1", xmFrameWidgetClass, parent, args, n);
/*
 * Create a glx widget for the background.
 */
   n = 0;
   XtSetArg(args[n], GlxNglxConfig, glxConfig); n++;
   XtSetArg(args[n], XmNtranslations, XtParseTranslationTable("")); n++;
   glPlot = GlxCreateMDraw(f1, "plot", args, n);
/*
 * Get (and save) the size of the just created glx widget.
 * The X and Y size come from the app-defaults file.
 */
   n = 0;
   XtSetArg(args[n], XtNwidth, &icop.plot.width); n++;
   XtSetArg(args[n], XtNheight, &icop.plot.height); n++;
   XtGetValues(glPlot, args, n);
/*
 * Add some callbacks.
 */
   XtAddCallback(glPlot, GlxNginitCallback, InitPlotCB, 0);
   XtAddCallback(glPlot, GlxNexposeCallback, DrawPlotCB, 0);
   XtOverrideTranslations(glPlot, XtParseTranslationTable(defaultTranslations));
   background = WidgetBackgroundToGlRgb(glPlot);

   icop.plot.buf = (unsigned long *)malloc(icop.plot.width*icop.plot.height*sizeof(long));
   XtManageChild(glPlot);
   return glPlot;
}
