/*--------------------------------------------------------------------
 new_init_graphics.c

 Programmed by: Kenneth Sakai
 Company      : Lockheed Martin
 Date         : 3-6-00

 Note: The following code is not meant necessarily to be commercial grade
       code.  It is just meant to be better than the original grad-student version.
       K.S.
  --------------------------------------------------------------------
 */
#include <stdio.h>
#include "vizual.h"

#include <grass/config.h>

#include <Xm/Xm.h>
#include <Xm/Form.h>
      /* include file for GL drawing widget */
#ifdef HAVE_GL_GLWMDRAWA_H
#include <GL/GLwMDrawA.h> 
#else
#ifdef HAVE_X11_GLW_GLWMDRAWA_H
#include <X11/GLw/GLwMDrawA.h> 
#endif
#endif
#include "Ball.h"

BallData Trackball;

#include "kns_defines.h"
#include "kns_globals.h"

#include <X11/Xlib.h>
#include <X11/extensions/XI.h>
#include <X11/extensions/XInput.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#define MAXTHRESHOLDS 200
static Widget Threshbutton[MAXTHRESHOLDS];
static Widget Button_plus;
static Widget Button_minus;
static Widget SingleToggle,MultipleToggle;
static Widget ThresholdRadio;
static Widget PlotSelected;
static int MultipleThresholdFlag;
static int ProceedStatus;
static GLuint FontBase;
static int Attributes[] = {GLX_RGBA,GLX_DEPTH_SIZE,16,GLX_RED_SIZE, 1, 
            GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,GLX_DOUBLEBUFFER,None};
static int SingleAttributes[] = {GLX_RGBA,GLX_RED_SIZE, 1, 
            GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,GLX_DOUBLEBUFFER,None};
static int RotationEnabled;

void glinitCB(Widget widget,XtPointer client_data,XtPointer cdata);
void glexposeCB(Widget widget,XtPointer client_data,XtPointer cdata);
void glinputCB(Widget widget,XtPointer client_data,XtPointer cdata);
void glresizeCB(Widget widget,XtPointer client_data,XtPointer cdata);
void glexpose2CB(Widget widget,XtPointer client_data,XtPointer cdata);
void glresize2CB(Widget widget,XtPointer client_data,XtPointer cdata);
void ThresholdCB(Widget widget,XtPointer client_data,XtPointer call_data);
void PlusMinusCB(Widget widget,XtPointer client_data,XtPointer call_data);
void SingleMultipleCB(Widget widget,XtPointer client_data,XtPointer cdata);
void PlotSelectedCB(Widget widget,XtPointer client_data,XtPointer cdata);

void set_threshold_button(int iset);
void draw_ctable();
void rotateLoop();
void draw_multiple();

/*-------------------------------------------------------------------
  get_trackball_rotation_matrix()
  This functions retrieves the current trackball rotation matrix.
  -------------------------------------------------------------------
*/
void get_trackball_rotation_matrix(float mat[4][4])
{
   int i,j;
   HMatrix mNow;
   Ball_Value(&Trackball, mNow);
   for(i = 0; i < QuatLen; i++)
     for(j = 0; j < QuatLen; j++)
        mat[i][j] = mNow[i][j];
}

/*-------------------------------------------------------------------
  This functions set the trackball state based upon the set of rotation
  angles contained in the dspect struct.
  -------------------------------------------------------------------
*/
void set_trackball_rotations(struct dspec *D_spec)
{
    GLfloat tranmat[4][4];
    glPushMatrix();
    glLoadIdentity();
    glRotatef(.1*(D_spec->yrot * 10),0.,1.,0.);
    glRotatef(.1*(D_spec->zrot * 10),0.,0.,1.);
    glRotatef(.1*(D_spec->xrot * 10),1.,0.,0.);
    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tranmat);
    Ball_SetMatrix(&Trackball,tranmat);
    glPopMatrix();
}

/*-------------------------------------------------------------------
  new_swapbuffers()
  This is an OpenGL version of the IRISGL swapbuffers() function.
  -------------------------------------------------------------------
*/
void new_swapbuffers()
{
    glXSwapBuffers(XtDisplay(MainOGLWindow.widget), MainOGLWindow.window);
}

/*-------------------------------------------------------------------
  enableRotation()
  -------------------------------------------------------------------
*/
void enableRotation()
{
   RotationEnabled = 1;
}
/*-------------------------------------------------------------------
  disableRotation()
  -------------------------------------------------------------------
*/
void disableRotation()
{
   RotationEnabled = 0;
}

/*-------------------------------------------------------------------
  new_swapbuffers()
  This is a substitute for the IRISGL getsize() function.
  -------------------------------------------------------------------
*/
void new_getsize(long *x, long *y)
{
   *x =  MainOGLWindow.width;
   *y =  MainOGLWindow.height;
}


/*-------------------------------------------------------------------
  initializeFonts()
  This function initializes the OpenGL display lists required for drawing 
  2D text in a 3D window.  FontBase will end up pointing to a sequence
  of display lists in which each individual display charactor in a font.
  The font chosen in a Courier Bold font chosen to match the font used 
  by the IRISGL charstr() function.
  -------------------------------------------------------------------
*/
void initializeFonts()
{
   XFontStruct *fontInfo;
   Font id;
   GLuint base;
   unsigned int first, last;

   fontInfo = XLoadQueryFont(XtDisplay(MainOGLWindow.widget),
            "-adobe-courier-bold-r-normal--14-100-100-100-m-90-iso8859-1");
   id = fontInfo->fid;
   first = fontInfo->min_char_or_byte2;
   last = fontInfo->max_char_or_byte2;
   base = glGenLists((GLuint) last+1);
   glXUseXFont(id, first, last-first+1, base+first);
   XFreeFont(XtDisplay(MainOGLWindow.widget),fontInfo);
   FontBase = base;
}

/*-------------------------------------------------------------------
  new_charstr()
  This is an OpenGL version of the IRISGL charstr() function. The initializeFonts
  function must have been called prior to calling this function.
  -------------------------------------------------------------------
*/
void new_charstr(char *str)
{
   glPushAttrib (GL_LIST_BIT);
   glListBase(FontBase);
   glCallLists(strlen(str), GL_UNSIGNED_BYTE, (GLubyte *)str);
   glPopAttrib ();
}
/*-------------------------------------------------------------------
  winset_main()
  This function is the equivalent of the IRISGL winset command for 
  the main 3D drawing window.
  -------------------------------------------------------------------
*/
void winset_main()
{
    glXMakeCurrent(XtDisplay(MainOGLWindow.widget),MainOGLWindow.window,MainOGLWindow.glx_context);
}

/*-------------------------------------------------------------------
  loadrect()
  This function loads a file created via the loadrect() function into the
  framebuffer ff the main 3D drawing window
  -------------------------------------------------------------------
*/
int loadrect (char *name)
{
    FILE *fp;
    unsigned long *buffer;

   float xd,yd,zd,trd; 
    float aspect;
    int xsiz,ysiz;
    long xsiz2,ysiz2;

    new_getsize(&xsiz2,&ysiz2);

    winset_main();
    fp = fopen (name, "r");
    if(!fp)
    {
      fprintf(stderr," Error: Could not open file %s for reading\n",name);
      G_free (buffer);
      return 0;
    }
    fread (&xsiz, sizeof (int), 1, fp);
    fread (&ysiz, sizeof (int), 1, fp);
    if (NULL == (buffer = (unsigned long *)G_malloc (xsiz * ysiz * sizeof (long))))
    {
       fprintf (stderr, "Out of memory\n");
       return -1;
    }

    if (ysiz != fread (buffer, xsiz * sizeof (long), ysiz, fp))
	fprintf (stderr, "Write failed file '%s'\n", name);

    clear_screen();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(0, xsiz2, 0, ysiz2,-100.,10);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glRasterPos2i(0,0);
    glDrawPixels(xsiz, ysiz, GL_RGBA, GL_BYTE,  buffer);

    glXSwapBuffers(XtDisplay(MainOGLWindow.widget), MainOGLWindow.window);
    fclose (fp);
    G_free (buffer);


     aspect = (float)MainOGLWindow.width / (float)MainOGLWindow.height;

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(45., aspect, .10, 1000.0);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();

      xd = Headfax.xdim * MainOGLWindow.ptr_D_spec->xscale;
      yd = Headfax.ydim * MainOGLWindow.ptr_D_spec->yscale;
      zd = Headfax.zdim * MainOGLWindow.ptr_D_spec->zscale;

    /* pick greatest dimension to use for translation of viewer from origin*/
      if(xd  < yd)
	trd  = yd;
      else
	trd = xd;
      if(trd < zd)
	trd = zd;

      glTranslatef(0.0, 0.0, -trd*1.6);




    return (0);
}

/*-------------------------------------------------------------------
  dumprect()
  This function dumps the framebuffer from the main 3D drawing window
  into a file.
  -------------------------------------------------------------------
*/
int dumprect (char *name)
{
    FILE *fp;
    long xsiz, ysiz;
    unsigned long *buffer;
    new_getsize(&xsiz,&ysiz);
    if (NULL == (buffer = (unsigned long *)G_malloc (xsiz * ysiz * sizeof (long))))
    {
       fprintf (stderr, "Out of memory\n");
       return -1;
    }
    winset_main();


    fp = fopen (name, "w");
    if(!fp)
    {
      fprintf(stderr," Error: Could not open file %s for writing\n",name);
      G_free (buffer);
      return 0;
    }
    
    /* write dims */
    fwrite (&xsiz, sizeof (int), 1, fp);
    fwrite (&ysiz, sizeof (int), 1, fp);

    glReadBuffer(GL_FRONT);

    glReadPixels(0,  0, xsiz, ysiz, GL_RGBA, GL_BYTE,  buffer);
    if (ysiz != fwrite (buffer, xsiz * sizeof (long), ysiz, fp))
	fprintf (stderr, "Write failed file '%s'\n", name);

    fclose (fp);
    G_free (buffer);


    return (0);
}
/*-------------------------------------------------------------------
  dumpgif()
  This function dumps the framebuffer from the main 3D drawing window
  into a GIF file.
  -------------------------------------------------------------------
*/
#define CPACKTORGBA(l,r,g,b,a)			\
   (r) = ((l)>>0) & 0xff;			\
   (g) = ((l)>>8) & 0xff;			\
   (b) = ((l)>>16) & 0xff;			\
(a) = ((l)>>24) & 0xff;	

#include "togif.h"		
int dumpgif (char *name)
{
    FILE *fp;
    long xwid, ywid;
    unsigned long *pixels;
    long numpixels;
    vgl_GIFWriter *writer;
    int i;
    unsigned short red,green,blue,alpha;

    new_getsize(&xwid,&ywid);
    if(xwid%2)
       xwid--;
    if(ywid%2)
       ywid--;



    if (NULL == (pixels = (unsigned long *)G_malloc (xwid * ywid * sizeof (long))))
    {
       fprintf (stderr, "Out of memory\n");
       return -1;
    }

    winset_main();


    fp = fopen (name, "wb");
    if(!fp)
    {
      fprintf(stderr," Error: Could not open file %s for writing\n",name);
      G_free (pixels);
      return 0;
    }
    
    
    glReadBuffer(GL_FRONT);
    glReadPixels(0,0,xwid,ywid,GL_RGBA,GL_UNSIGNED_BYTE,pixels);

      numpixels = xwid * ywid;
      for(i = 0; i < numpixels; i++)
      {
         CPACKTORGBA(pixels[i],red,green,blue,alpha);
#ifdef WIN32
         pixels[i] = red | (green<<8) | (blue<<16) | (alpha<<24);
#else
         pixels[i] = alpha | (blue<<8) | (green<<16) | (red<<24);
#endif
      }

      
      writer = vgl_GIFWriterBegin();
      vgl_GIFWriterWriteGIFFile(writer,pixels,xwid,ywid,0,fp);


    fclose (fp);
    G_free (pixels);


    return (0);
}


/*-------------------------------------------------------------------
  winset_colortable()
  This function is the equivalent of the IRISGL winset command for 
  the colormap window.
  -------------------------------------------------------------------
*/
void winset_colortable()
{
   glXMakeCurrent(XtDisplay(ColormapWindow.widget),ColormapWindow.window,ColormapWindow.glx_context);
}

/*-------------------------------------------------------------------
  init_graphics()
  This function creates both the main 3D drawing window and the 2D
  colormap window.
  -------------------------------------------------------------------
*/
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>
#include <Xm/ToggleB.h>
#include <Xm/Frame.h>
#include <Xm/SeparatoG.h>
#include <grass/gis.h>
static String Fallback_resources[] = 
{
"*.fontList:-adobe-helvetica-bold-r-normal--12-120-75-75-p-70-iso8859-1",
"*Thresholds.fontList:-adobe-helvetica-bold-r-normal--17-120-100-100-p-92-iso8859-1",
};

void init_graphics(char *name,int argc,char **argv,struct dspec *dspecptr)
{
   int n;
   Arg args[20];             /* array used to set widget resources */
   XVisualInfo *vi;
   XEvent event;
   Widget toplevel,form;
   int i;
   char buffer[20];
   Widget separator;
   Widget gl_shell;
   Widget buttonrowcol,label1,radio;
   Widget frame;
   MainOGLWindow.ptr_D_spec = dspecptr;

   n = 0;
   XtSetArg(args[n],XmNwidth,660);
   n++;
   XtSetArg(args[n],XmNheight,600);
   n++;
   XtSetArg(args[n],XmNx,10);
   n++;
   XtSetArg(args[n],XmNy,100);
   n++;
   toplevel = XtAppInitialize (&App_context, "Showdspf", NULL, 
                                0, &argc, argv, Fallback_resources, args, n);
   /* 
    *     Create the 3D drawing window
    */
   n = 0;
   XtSetArg(args[n],XmNwidth,660);
   n++;
   XtSetArg(args[n],XmNheight,600);
   n++;
   XtSetArg(args[n],XmNx,10);
   n++;
   XtSetArg(args[n],XmNy,100);
   n++;
   form = XmCreateForm (toplevel, "form", args, n);
   XtManageChild(form);
   vi = glXChooseVisual(XtDisplay(form),DefaultScreen(XtDisplay(form)),Attributes);
   if(!vi)
   {
      fprintf(stderr,"Fatal error: Couldn't get a visual\n");
      fprintf(stderr,"See program developer.\n");
      exit(1);
   }

   n = 0;
   XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNshadowThickness,3);
   n++;
   XtSetArg(args[n],XmNshadowType,XmSHADOW_OUT);
   n++;
   frame = XmCreateFrame(form,"frame_r",args,n);
   XtManageChild(frame);

   n = 0;
   XtSetArg(args[n],XmNentryAlignment,XmALIGNMENT_CENTER);
   n++;
   XtSetArg(args[n],XmNpacking,XmPACK_TIGHT);
   n++;
   XtSetArg(args[n],XmNorientation,XmVERTICAL);
   n++;
   XtSetArg(args[n],XmNnumColumns,1);
   n++;
   XtSetArg(args[n],XmNmarginHeight,0);
   n++;
   XtSetArg(args[n],XmNmarginWidth,0);
   n++;
   buttonrowcol = XmCreateRowColumn(frame,"rowcol",args,n);
   XtManageChild(buttonrowcol);
   label1 = XmCreateLabel(buttonrowcol,"Thresholds",args,n);
   XtManageChild(label1);

   n = 0;
   XtSetArg(args[n],XmNshadowThickness,5);
   n++;
   separator = XmCreateSeparatorGadget(buttonrowcol,"separator1",args,n);

   XtManageChild(separator);
   n = 0;
   radio = XmCreateRadioBox(buttonrowcol,"radio",args,n);
   XtManageChild(radio);
   n = 0;
   SingleToggle = XtVaCreateManagedWidget("Single",xmToggleButtonWidgetClass, 
                                                   radio,NULL);
   XtAddCallback(SingleToggle,XmNarmCallback,SingleMultipleCB,(XtPointer)1);
   MultipleToggle = XtVaCreateManagedWidget("Multiple",xmToggleButtonWidgetClass, 
                                                   radio,NULL);
   XtAddCallback(MultipleToggle,XmNarmCallback,SingleMultipleCB,(XtPointer)0);
   XmToggleButtonSetState(SingleToggle,True,False);

   n = 0;
   XtSetArg(args[n],XmNshadowThickness,5);
   n++;
   separator = XmCreateSeparatorGadget(buttonrowcol,"separator1",args,n);
   XtManageChild(separator);


   n = 0;
   Button_plus = XtCreateManagedWidget ("Increase",xmPushButtonWidgetClass,buttonrowcol,args,n);
   XtAddCallback(Button_plus,XmNactivateCallback,PlusMinusCB,(XtPointer)1);
   Button_minus = XtCreateManagedWidget ("Decrease",xmPushButtonWidgetClass,buttonrowcol,args,n);
   XtAddCallback(Button_minus,XmNactivateCallback,PlusMinusCB,(XtPointer)0);

   n = 0;
   PlotSelected = XmCreatePushButton(buttonrowcol,"Plot Selected",args,n);
   XtAddCallback(PlotSelected,XmNactivateCallback,PlotSelectedCB,NULL);


   n = 0;
   ThresholdRadio = XmCreateRadioBox(buttonrowcol,"radio",args,n);
   XtManageChild(ThresholdRadio);
   if(Headfax.linefax.nthres)
   {
      for(i = 1; i <= Headfax.linefax.nthres; i++)
      {
         n = 0;
         if(i <= MAXTHRESHOLDS)
         {
            sprintf(buffer,"Threshold %d",i);
            Threshbutton[i-1] = XtVaCreateManagedWidget(buffer,xmToggleButtonWidgetClass, 
                                                   ThresholdRadio,NULL);
            XtAddCallback(Threshbutton[i-1],XmNarmCallback,ThresholdCB,
                    (XtPointer)(i-1));
         }
      }
      XmToggleButtonSetState(Threshbutton[0],True,False);
   }


   n = 0;
   XtSetArg(args[n],XmNrightAttachment,XmATTACH_WIDGET);
   n++;
   XtSetArg(args[n],XmNrightWidget,frame);
   n++;
   XtSetArg(args[n],GLwNvisualInfo,vi);
   n++;
   XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM);
   n++;
   MainOGLWindow.widget =  
            GLwCreateMDrawingArea(form,"glwidget",args,n);

   XtManageChild(MainOGLWindow.widget);

   XtAddCallback(MainOGLWindow.widget,GLwNginitCallback,glinitCB,0);

   XtAddCallback(MainOGLWindow.widget,GLwNexposeCallback,glexposeCB,0);
   XtAddCallback(MainOGLWindow.widget,GLwNinputCallback,glinputCB,0);
   XtAddCallback(MainOGLWindow.widget,GLwNresizeCallback,glresizeCB,0);
 

   /* 
    *     Create the colormap window
    */
   n = 0;
   XtSetArg(args[n],XmNwidth,100);
   n++;
   XtSetArg(args[n],XmNheight,500);
   n++;
   XtSetArg(args[n],XmNx,5);
   n++;
   XtSetArg(args[n],XmNy,5);
   n++;
   gl_shell = XtAppCreateShell("Colormap","GL_window", 
                 topLevelShellWidgetClass,XtDisplay(toplevel),args,n);

   n = 0;
   XtSetArg(args[n],XmNwidth,100);
   n++;
   XtSetArg(args[n],XmNheight,500);
   n++;
   XtSetArg(args[n],XmNx,5);
   n++;
   XtSetArg(args[n],XmNy,5);
   n++;
   form = XmCreateForm (gl_shell, "form", args, n);
   XtManageChild(form);
   vi = glXChooseVisual(XtDisplay(form),DefaultScreen(XtDisplay(form)),SingleAttributes);
   if(!vi)
   {
      fprintf(stderr,"Fatal error: Couldn't get a visual\n");
      fprintf(stderr,"See program developer.\n");
      exit(1);
   }



   n = 0;
   XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],GLwNvisualInfo,vi);
   n++;
   XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM);
   n++;
   XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM);
   n++;
   ColormapWindow.widget =  
            GLwCreateMDrawingArea(form,"glwidget",args,n);

   XtManageChild(ColormapWindow.widget);

   XtAddCallback(ColormapWindow.widget,GLwNginitCallback,glinitCB,(void *)1);

   XtAddCallback(ColormapWindow.widget,GLwNexposeCallback,glexpose2CB,0);

   XtAddCallback(ColormapWindow.widget,GLwNresizeCallback,glresize2CB,0);


   XtRealizeWidget(toplevel);
   XStoreName(XtDisplay(toplevel),
                     XtWindow(toplevel),name);

   XtPopup(gl_shell,XtGrabNone);

   XMapRaised(XtDisplay(gl_shell),XtWindow(gl_shell));


   while(ProceedStatus < 4)
   {
      XtAppNextEvent(App_context,&event);
      XtDispatchEvent(&event);
   }

}
/*-------------------------------------------------------------------
  ThresholdCB()
  This is a Motif callback that is called when one of the threshold toggle
  buttons is hit.
  -------------------------------------------------------------------
*/
void ThresholdCB(Widget widget,XtPointer client_data,XtPointer call_data)
{
   int num = (int)client_data;

   if(MultipleThresholdFlag) return;
   MainOGLWindow.ptr_D_spec->Thresh = num;
   do_draw_immediate_mode();
}
/*-------------------------------------------------------------------
  PlusMinusCB()
  This is a Motif callback that is called when either the "Increase" or
  "Decrease" button is hit in the GUI interface.
  -------------------------------------------------------------------
*/
void PlusMinusCB(Widget widget,XtPointer client_data,XtPointer call_data)
{
   int flag = (int)client_data;
   if(flag)
   {
      MainOGLWindow.ptr_D_spec->Thresh++;
      if (MainOGLWindow.ptr_D_spec->Thresh > Headfax.linefax.nthres-1)
         MainOGLWindow.ptr_D_spec->Thresh = 0;
      set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
      do_draw_immediate_mode();
     
   }
   else
   {
      MainOGLWindow.ptr_D_spec->Thresh--;
      if (MainOGLWindow.ptr_D_spec->Thresh < 0)
         MainOGLWindow.ptr_D_spec->Thresh = Headfax.linefax.nthres-1;
      set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
      do_draw_immediate_mode();
   }
}
/*-------------------------------------------------------------------
  set_threshold_button(int iset)
  This function sets the GUI toggle button corresponding to a threshold value
  -------------------------------------------------------------------
*/
void set_threshold_button(int iset)
{
  int i;
  for(i = 1; i <= Headfax.linefax.nthres; i++)
  {
     if(i == iset)
     {
        if(i <= MAXTHRESHOLDS)
           XmToggleButtonSetState(Threshbutton[i-1],True,False);
     }
     else
     {
        if((i <= MAXTHRESHOLDS) && !MultipleThresholdFlag)
           XmToggleButtonSetState(Threshbutton[i-1],False,False);
     }
  }
}
/*-------------------------------------------------------------------
  unset_threshold_button(int iset)
  This function unsets the GUI toggle button corresponding to a threshold value
  -------------------------------------------------------------------
*/
void unset_threshold_button(int iset)
{
  if(iset <= MAXTHRESHOLDS)
      XmToggleButtonSetState(Threshbutton[iset-1],False,False);
}

/*-------------------------------------------------------------------
  rotateLoop()
  This function is executed while the user attempts to rotate/translate
  the 3D model in the main 3D drawing window
  -------------------------------------------------------------------
*/

void rotateLoop()
{
   XEvent event;
   XKeyPressedEvent *K;
   KeySym sym;
   int thresh = 1;
   for(;;)
   {
      XtAppNextEvent(App_context,&event);

      if(event.type == KeyPress)
      {
         K = (XKeyPressedEvent *) &event;
         sym = XKeycodeToKeysym(XtDisplay(MainOGLWindow.widget), K->keycode, 0);
         if(sym == XK_Escape)
         {
            fprintf(stderr,"Escape\n");
            return;
         }
         else if(sym == XK_equal)
         {
            if(K->state & ShiftMask)
            {
		MainOGLWindow.ptr_D_spec->Thresh++;
		if (MainOGLWindow.ptr_D_spec->Thresh > Headfax.linefax.nthres-1)
		    MainOGLWindow.ptr_D_spec->Thresh = 0;
                do_draw_immediate_mode();
                set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_minus)
         {
            if(!(K->state & ShiftMask))
            {
		MainOGLWindow.ptr_D_spec->Thresh--;
		if (MainOGLWindow.ptr_D_spec->Thresh < 0)
		    MainOGLWindow.ptr_D_spec->Thresh = Headfax.linefax.nthres-1;
                do_draw_immediate_mode();
                set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_C)
         {
            clear_screen();
         }
         else if(sym == XK_1)
         {
            thresh = 0;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_2)
         {
            thresh = 1;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_3)
         {
            thresh = 2;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_4)
         {
            thresh = 3;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_5)
         {
            thresh = 4;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_6)
         {
            thresh = 5;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_7)
         {
            thresh = 6;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_8)
         {
            thresh = 7;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_9)
         {
            thresh = 8;
            if(thresh < Headfax.linefax.nthres)
            {
               
               MainOGLWindow.ptr_D_spec->Thresh = thresh;
               do_draw_immediate_mode();
               set_threshold_button(MainOGLWindow.ptr_D_spec->Thresh + 1);
            }
         }
         else if(sym == XK_Escape)
         {
            fprintf(stderr,"Escape\n");
            return;
         }
         
      }
      else if(MainOGLWindow.right_button_hit)
      {
         MainOGLWindow.right_button_hit = 0;
         return;
      }
      else
      {
	 XtDispatchEvent(&event);
      }
   }
}
/*-------------------------------------------------------------------
  glinputCB()
  This function handles all mouse inputs in the main 3D drawing window
  -------------------------------------------------------------------
*/
HVect vNow;
HVect vNowLast;
void glinputCB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   long xpos, ypos;
   
   static int tr_first = 0;
   static int rot_first = 0;
   static int tr_oymouse;
   static int rot_oxmouse, rot_oymouse;
   int dymouse;


   GLwDrawingAreaCallbackStruct *call_data = 
      (GLwDrawingAreaCallbackStruct *)cdata;

   if(!RotationEnabled)
       return;


   switch(call_data -> event -> type)
   {
      case ButtonPress:
         if(call_data -> event -> xbutton.button == Button1)
         {
            if(MainOGLWindow.middle_button_status == 0)
            {
               MainOGLWindow.left_button_status = 1;
               rot_first = 1;
               xpos = call_data -> event -> xbutton.x;
               ypos = MainOGLWindow.height - call_data -> event -> xbutton.y;
               vNow.x = (2 * (float)xpos /MainOGLWindow.width) - 1.;
               vNow.y = (2 * (float)ypos /MainOGLWindow.height) - 1.;
               Ball_Mouse(&Trackball, vNow);
               Ball_Update(&Trackball);



               Ball_BeginDrag(&Trackball);
            }
         }
         else if(call_data -> event -> xbutton.button == Button2)
         {
            if(MainOGLWindow.left_button_status == 0)
            {
               tr_first = 1;
               MainOGLWindow.middle_button_status = 1;
            }
         }
         else if(call_data -> event -> xbutton.button == Button3)
         {
            MainOGLWindow.right_button_hit = 1;
         }
         break;
      case ButtonRelease:
         if(call_data -> event -> xbutton.button == Button1)
         {
            MainOGLWindow.left_button_status = 0;
            Ball_EndDrag(&Trackball);
            Ball_Mouse(&Trackball, vNow);
            Ball_Update(&Trackball);
            do_draw_with_display_list(MainOGLWindow.ptr_D_spec);
         }
         else if(call_data -> event -> xbutton.button == Button2)
         {
            MainOGLWindow.middle_button_status = 0;
         }
         break;
      case MotionNotify:

         if(MainOGLWindow.left_button_status)
         {
            /* Rotate 3D scene */
            xpos = call_data -> event -> xmotion.x;
            ypos = MainOGLWindow.height - call_data -> event -> xmotion.y;
            if(rot_first)
            {
               rot_oxmouse = xpos;
               rot_oymouse = ypos;
               rot_first = 0;
            }
            vNow.x = (2 * (float)xpos /MainOGLWindow.width) - 1.;
            vNow.y = (2 * (float)ypos /MainOGLWindow.height) - 1.;

            Ball_Mouse(&Trackball, vNow);
            Ball_Update(&Trackball);
            if(((vNowLast.x - vNow.x) * (vNowLast.x - vNow.x) +
                (vNowLast.y - vNow.y) * (vNowLast.y - vNow.y)) > .01)
            {
               vNowLast = vNow;
               do_draw_with_display_list (MainOGLWindow.ptr_D_spec);
            }
         }
         else if(MainOGLWindow.middle_button_status)
         {
            /* Translate 3D scene */
            xpos = call_data -> event -> xmotion.x;
            ypos = MainOGLWindow.height - call_data -> event -> xmotion.y;
            if(tr_first)
            {
               tr_oymouse = ypos;
               tr_first = 0;
            }
            dymouse = ypos - tr_oymouse;
            if(dymouse > 10 || dymouse < -10)
            {
               tr_oymouse = ypos;

               if(MainOGLWindow.ptr_D_spec)
               {
                  MainOGLWindow.ptr_D_spec->ztrans -= (float)dymouse;
                  do_draw_with_display_list (MainOGLWindow.ptr_D_spec);
               }
            }
         }
         break;
   }
}

/*-------------------------------------------------------------------
  glinitCB()
  This function is called during initialization of both the main 3D 
  drawing window and colormap window
  -------------------------------------------------------------------
*/
void glinitCB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   float xd,yd,zd,trd; 
   Arg args[10];
   XVisualInfo *vi;
   int height,width;   
   float aspect;
   GLwDrawingAreaCallbackStruct *call_data = 
      (GLwDrawingAreaCallbackStruct *)cdata;
   int id = (int)client_data;

   XtSetArg(args[0], GLwNvisualInfo, &vi);
   XtGetValues(widget, args, 1);


   if(id == 0) 
   {  
      /* Main 3D window */
      MainOGLWindow.window = XtWindow(widget);
      MainOGLWindow.glx_context =  
         glXCreateContext(XtDisplay(widget), vi, 0, GL_TRUE);
      glXMakeCurrent(XtDisplay(widget),MainOGLWindow.window,MainOGLWindow.glx_context);
      glDepthRange(0x0000, 0x7fffff);
    

      height = (GLuint)call_data -> height - 1;
      width = (GLuint)call_data -> width - 1;
      aspect = (float)width / (float)height;

      do_lights();
      clear_screen ();

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(45., aspect, .10, 1000.0);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();

      xd = Headfax.xdim * MainOGLWindow.ptr_D_spec->xscale;
      yd = Headfax.ydim * MainOGLWindow.ptr_D_spec->yscale;
      zd = Headfax.zdim * MainOGLWindow.ptr_D_spec->zscale;

    /* pick greatest dimension to use for translation of viewer from origin*/
      if(xd  < yd)
	trd  = yd;
      else
	trd = xd;
      if(trd < zd)
	trd = zd;

      glTranslatef(0.0, 0.0, -trd*1.6);
      glXSwapBuffers(XtDisplay(widget), MainOGLWindow.window);
      initializeFonts();

      MainDlist = glGenLists(1);
      glNewList(MainDlist,GL_COMPILE_AND_EXECUTE);
      glEndList();

      Ball_Init(&Trackball);
      Ball_Place(&Trackball, qOne, .90);


      ProceedStatus++;
   }
   else
   {
      ColormapWindow.window = XtWindow(widget);
      ColormapWindow.glx_context =  
                 glXCreateContext(XtDisplay(widget), vi, 0, GL_TRUE);

      glXMakeCurrent(XtDisplay(widget),ColormapWindow.window,ColormapWindow.glx_context);
      clear_screen ();

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluOrtho2D(0, 100, 0, 1000);
      glMatrixMode(GL_MODELVIEW);
      draw_ctable();
      winset_main();
      ProceedStatus++;
   }
}
/*-------------------------------------------------------------------
  glexposeCB()
  This function is called when the main 3D drawing window is exposed
  -------------------------------------------------------------------
*/
void glexposeCB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   Window wind;
   static int First = 1;
   GLwDrawingAreaCallbackStruct *call_data = 
      (GLwDrawingAreaCallbackStruct *)cdata;
   wind = XtWindow(widget);
   glXMakeCurrent(XtDisplay(widget),wind,MainOGLWindow.glx_context);
   MainOGLWindow.height = (GLuint)(call_data->height);
   MainOGLWindow.width = (GLuint)(call_data->width);
   glViewport(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height)); 
   glScissor(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height));
   glEnable(GL_DEPTH_TEST);

   clear_screen();
   if(First)
   {
      do_draw_immediate_mode();
      First = 0;
   }
   else
   {
      do_draw_with_display_list (MainOGLWindow.ptr_D_spec);
   }

   ProceedStatus++;
}
/*-------------------------------------------------------------------
  glresizeCB()
  This function is called to clear the colomap window prior to redrawing
  -------------------------------------------------------------------
*/
void glresizeCB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   Window wind;
   GLdouble aspect;
   GLwDrawingAreaCallbackStruct *call_data = 
      (GLwDrawingAreaCallbackStruct *)cdata;
   
   wind = XtWindow(widget);
   glXMakeCurrent(XtDisplay(widget),wind,MainOGLWindow.glx_context);

   glViewport(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height)); 
   glScissor(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height));

   MainOGLWindow.height = (GLuint)(call_data->height);
   MainOGLWindow.width = (GLuint)(call_data->width);

   aspect = (float)MainOGLWindow.width / (float)MainOGLWindow.height;

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(45., aspect, .10, 1000.0);
   glMatrixMode(GL_MODELVIEW);
   do_draw_with_display_list (MainOGLWindow.ptr_D_spec);

}

/*-------------------------------------------------------------------
  clear_screen2()
  This function is called to clear the colomap window prior to redrawing
  -------------------------------------------------------------------
*/
void clear_screen2()
{

    glDisable(GL_LIGHTING);
    glClearColor(1.,1.,1.,1.);
    glClear(GL_COLOR_BUFFER_BIT);
    glClearDepth(1.);
    glClear(GL_DEPTH_BUFFER_BIT);
}

/*-------------------------------------------------------------------
  glexpose2CB()
  This function is called when the colormap window gets an expose event
  -------------------------------------------------------------------
*/
void glexpose2CB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   Window wind;
   GLwDrawingAreaCallbackStruct *call_data = 
                  (GLwDrawingAreaCallbackStruct *)cdata;

   wind = XtWindow(widget);
   winset_colortable();

   glXMakeCurrent(XtDisplay(widget),wind,ColormapWindow.glx_context);

   glDisable(GL_DEPTH_TEST);
   ColormapWindow.height = (GLuint)(call_data->height);
   ColormapWindow.width = (GLuint)(call_data->width);

   glViewport(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height)); 
   glScissor(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height));

   clear_screen2();
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0, 100, 0, 1000,-10000.,1000.);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   draw_ctable();

   glXSwapBuffers(XtDisplay(ColormapWindow.widget), ColormapWindow.window);

   ProceedStatus++;
   winset_main();
}
/*-------------------------------------------------------------------
  glresize2CB()
  This function is called when the colormap window is resized 
  -------------------------------------------------------------------
*/
void glresize2CB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   Window wind;
   GLwDrawingAreaCallbackStruct *call_data = 
      (GLwDrawingAreaCallbackStruct *)cdata;
   
   wind = XtWindow(widget);
   glXMakeCurrent(XtDisplay(widget),wind,ColormapWindow.glx_context);
   glViewport(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height)); 
   glScissor(0,  0, (GLuint)(call_data->width), (GLuint)(call_data->height));
   ColormapWindow.height = (GLuint)(call_data->height);
   ColormapWindow.width = (GLuint)(call_data->width);

   clear_screen2();
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0, 100, 0, 1000,-10000.,1000.);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   draw_ctable();
   winset_main();
}

/*-------------------------------------------------------------------
  rotate_model()
  This function is called when the program is put into "Rotation Mode"
  where the user is modifying the view using mouse input.
  -------------------------------------------------------------------
*/
void rotate_model (struct dspec *D_spec)
{
   enableRotation();
   rotateLoop();
   disableRotation();  
}
/*-------------------------------------------------------------------
  SingleMultipleCB()
  -------------------------------------------------------------------
*/
void SingleMultipleCB(Widget widget,XtPointer client_data,XtPointer cdata)
{
   int flag = (int)client_data;
   Arg args[10];
   int n;
   int i;

   if(flag == 1)
   {
      MultipleThresholdFlag = 0;
      n = 0;
      XtSetArg (args[n], XmNradioBehavior, True);	
      n++;
      XtSetValues(ThresholdRadio,args,n);
      XtManageChild(Button_plus);
      XtManageChild(Button_minus);
      XtUnmanageChild(PlotSelected);
      for(i = 1; i <= Headfax.linefax.nthres; i++)
      {
        if(i <= MAXTHRESHOLDS)
           XmToggleButtonSetState(Threshbutton[i-1],False,False);
      }
   }
   else
   {
      MultipleThresholdFlag = 1;
      for(i = 1; i <= Headfax.linefax.nthres; i++)
      {
        if(i <= MAXTHRESHOLDS)
           XmToggleButtonSetState(Threshbutton[i-1],False,False);
      }
      n = 0;
      XtSetArg (args[n], XmNradioBehavior, False);	
      n++;
      XtSetValues(ThresholdRadio,args,n);
      XtUnmanageChild(Button_plus);
      XtUnmanageChild(Button_minus);
      XtManageChild(PlotSelected);
   }
}
/*-------------------------------------------------------------------
  PlotSelectedCB()
  This function is the callback function for the "Plot Selected" button.
  A plot containing thresholds corresponding to all selected Threshold 
  toggle buttons will be drawn.
  -------------------------------------------------------------------
*/
void PlotSelectedCB(Widget widget,XtPointer client_data,XtPointer cdata)
{
  int i;
  int selectedList[200];
  int numSelected=0;
  for(i = 1; i <= Headfax.linefax.nthres; i++)
  {
     if(i <= MAXTHRESHOLDS)
     {
        if(XmToggleButtonGetState(Threshbutton[i-1]) == True)
        {
           selectedList[numSelected] = i;
           numSelected++;
        }
     }
  }
  for(i = 0; i < numSelected; i++)
  {
     MainOGLWindow.ptr_D_spec->t[i] = selectedList[i] - 1;
  }
  MainOGLWindow.ptr_D_spec->nt = numSelected;
  draw_multiple();

}
/*-------------------------------------------------------------------
  setSingleSelectionMode()
  This function causes the Motif GUI to act in Single Selection Mode
  with regard to Threshold levels
  -------------------------------------------------------------------
*/
void setSingleSelectionMode()
{
   Arg args[10];
   int n;
   int i;
   XmToggleButtonSetState(SingleToggle,True,True);
   MultipleThresholdFlag = 0;
   n = 0;
   XtSetArg (args[n], XmNradioBehavior, True);	
   n++;
   XtSetValues(ThresholdRadio,args,n);
   XtManageChild(Button_plus);
   XtManageChild(Button_minus);
   XtUnmanageChild(PlotSelected);
   for(i = 1; i <= Headfax.linefax.nthres; i++)
   {
     if(i <= MAXTHRESHOLDS)
           XmToggleButtonSetState(Threshbutton[i-1],False,False);
   }
}
/*-------------------------------------------------------------------
  setSingleSelectionMode()
  This function causes the Motif GUI to act in Multiple Selection Mode
  with regard to Threshold levels
  -------------------------------------------------------------------
*/
void setMultipleSelectionMode()
{
   Arg args[10];
   int n;
   int i;

   MultipleThresholdFlag = 1;
   XmToggleButtonSetState(MultipleToggle,True,True);
      for(i = 1; i <= Headfax.linefax.nthres; i++)
      {
        if(i <= MAXTHRESHOLDS)
           XmToggleButtonSetState(Threshbutton[i-1],False,False);
      }
      n = 0;
      XtSetArg (args[n], XmNradioBehavior, False);	
      n++;
      XtSetValues(ThresholdRadio,args,n);
      XtUnmanageChild(Button_plus);
      XtUnmanageChild(Button_minus);
      XtManageChild(PlotSelected);
}
/*-------------------------------------------------------------------
  isSingleSelectionMode()
  This function returns 1 if the GUI is currently behaving in Single
  Selection Mode or returns 0 if the GUI is currently behaving in
  Multiple selection mode.
  -------------------------------------------------------------------
*/
int isSingleSelectionMode()
{
  return !MultipleThresholdFlag;
}

