#ifdef SCCS
static char sccsid[]="@(#)wks.c	1.10 Oki 93/05/24";
#endif
/*
			Copyright (c) 1992 by 
			Oki Electric Industry Co., Ltd.
			All Rights Reserved
	
	
	This file is under sccs control at Oki in:
	/nfs/sole/root/sccs1.p/X11R5/mit/demos/pexdraw/s.wks.c
*/
/*
 *			Copyright (c) 1992 by
 *			Oki Electric Industry Co., Ltd.
 *			All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of Oki Electric not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission. Oki Electric
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 *************************************************************************
 * wks.c - the methods for using a PHIGS Workstation resource as the
 * render object.
 *
 *  wksInit
 *  wksReDraw
 *  wksMapXtoMC
 *  wksMapMCtoX
 *  wksSetView
 *  wksSetNPCtoDC
 *  wksPost
 *  wksDeleteAll
 *  wksPickOne
 */


#include <stdio.h>

#include <math.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include <X11/PEX5/PEXlib.h>
#include "pexdraw.h"

/*************************************************************************
 * State Variables
 */
static int myMCSeqNo = -1;
static int thePickInitFlag = 0;

/* L O C A L  P r o c s */
/*************************************************************************
 * MapWcPointsToX
 *
 */
static int
MapWcPointsToX( wkid, view, nCoords, wcPoints, xPoints )
     int wkid;
     int view;
     int nCoords;
     PEXCoord *wcPoints;
     XPoint *xPoints;
{
  PEXCoord p, q;
  int i;
  int error;
  unsigned long valueMask[2];
  PEXWorkstationAttributes *wksAttrs;
  float winWidth, winHeight, winX, winY, XX, XY;
  XPoint *xp = xPoints;
  float scale;
  XWindowAttributes winAttrs;
  PEXMatrix WCtoXC, NPCtoXC;

  /*
   * We rely on the fact that we always use the whole window for the
   * Workstation viewport.  Get the information we want, incase this
   * scratch area gets creamed.
   */
  valueMask[0] = valueMask[1] = 0;

  PEXSetPWAttributeMask(valueMask,PEXPWCurViewport);
  PEXSetPWAttributeMask(valueMask,PEXPWCurNPCSubVolume);
  
  if (!(wksAttrs = PEXGetWorkstationAttributes(theDisplay, wkid,
					       valueMask ))) {
    return (-1);
  }

  XGetWindowAttributes(theDisplay, theWindow, &winAttrs );


    /* ???
   if (wksAttrs->cur_workstation_viewport.use_drawable) {
    wksAttrs->cur_workstation_viewport.max.x = winAttrs.width;
    wksAttrs->cur_workstation_viewport.max.y = winAttrs.height;
   }
     */


  error = PEXNPCToXCTransform( &wksAttrs->cur_npc_subvolume, 
		    (PEXDeviceCoord *)&wksAttrs->cur_workstation_viewport, 
                            winAttrs.height, NPCtoXC );
  if(error != 0) printf("imagine a Motif alarm, NPCtoXC err = %d\n",error);

  PEXMatrixMult( theMatMap, theMatOri, WCtoXC );
  PEXMatrixMult( NPCtoXC, WCtoXC, WCtoXC );

  for (i = 0; i < nCoords; i++, xp++ ) {
    PEXTransformPoints(WCtoXC, 1, &wcPoints[i], &p);
    xp->x = (int)p.x;
    xp->y = (int)p.y;
  }
  PEXFreeWorkstationAttributes(wksAttrs);
  return (0);
}


/*************************************************************************
 * InitPickDevice
 */
PEXPickMeasure
InitPickDevice()
{
  PEXNameSet incl, excl;
  PEXName names[3];
  PEXBitmask  pickDevMask;
  PEXPDAttributes pickDevValues;
  PEXPickMeasure pmID;
/*
 * - first we need to setup the device. This is an appendage to the PHIGS
 *     Workstation. DC_Hit box is required, assume it is there.
 *   - pick inclusion & pick exclusion NameSets (need to create)
 *      then we free them, so they just live in the pick device.
 *   - echo switch, set to no echo.
 */
  incl = PEXCreateNameSet(theDisplay);
  names[0] = 1; names[1] = 2; names[2] = 3; /* do an extra, for the heck */
  PEXChangeNameSet(theDisplay, incl, PEXNSAdd, 3, names);
  excl = PEXCreateNameSet(theDisplay);


  pickDevMask = PEXPDPickIncl | PEXPDPickExcl | PEXPDEchoSwitch;
  pickDevValues.inclusion = incl;
  pickDevValues.exclusion = excl;
  pickDevValues.echo_switch = 0;
  PEXChangePickDevice(theDisplay, theRenderObj, PEXPickDeviceDCHitBox,
		       pickDevMask, &pickDevValues);
  /* hmm... should free nameSets now...*/

  return (pmID);
}

/*************************************************************************
 *
 */
XID  wksInit( dpy,  w, colorLUT, lightLUT, textLUT, depthCueLUT,colorApproxLUT)
     Display *dpy;
     Window w;
     PEXLookupTable colorLUT;
     PEXLookupTable lightLUT;
     PEXLookupTable textLUT;
     PEXLookupTable depthCueLUT;
     PEXLookupTable colorApproxLUT;
{
  XID                           renderObj;
  PEXLookupTable		lineBT, markerBT, textBT, interiorBT, edgeBT;
  PEXLookupTable		patT;
  PEXNameSet			incl, excl;
  int                           dbflag;

  lineBT = PEXCreateLookupTable( dpy, w, PEXLUTLineBundle );
  markerBT = PEXCreateLookupTable( dpy, w, PEXLUTMarkerBundle );
  textBT = PEXCreateLookupTable( dpy, w, PEXLUTTextBundle );
  interiorBT = PEXCreateLookupTable( dpy, w, 
				    PEXLUTInteriorBundle );
  edgeBT = PEXCreateLookupTable( dpy, w, PEXLUTEdgeBundle );
  patT = PEXCreateLookupTable( dpy, w, PEXLUTPattern );
  incl = PEXCreateNameSet(dpy);
  excl = PEXCreateNameSet(dpy);

  if (CheckImpDepInteger(PEXIDDoubleBufferingSupported)) {
    dbflag = PEXDoubleBuffered;
}  else  {
    dbflag = PEXSingleBuffered;
}

  renderObj =  PEXCreateWorkstation(dpy, w,
					      lineBT, markerBT, textBT,
					      interiorBT, edgeBT,
					      colorLUT,
					      patT, textLUT,
					      depthCueLUT,
					      lightLUT,
					      colorApproxLUT,
					      incl, excl, incl, excl,
					      dbflag );

  if (CheckEnumType(PEXETHLHSRMode,PEXHLHSRZBuffer)) {
    PEXSetWorkstationHLHSRMode(dpy, renderObj,
			       PEXHLHSRZBuffer);
  }

  PEXSetWorkstationDisplayUpdateMode(dpy, renderObj,
				     PEXVisualizeNone);
  return (renderObj);
}

/*************************************************************************
 * ReDraw - does a redraw for the workstation case.
 */
void  wksReDraw()
{
  PEXRedrawAllStructures(theDisplay, theRenderObj);
  XFlush(theDisplay);
}

/*************************************************************************
 * MapPoints 
 */
int  wksMapXToMC(mc, nCoords, xPoints, mcPoints )
     MCMatrix *mc;
     int nCoords;
     XPoint *xPoints;
     PEXCoord **mcPoints;
{
  PEXCoord *p, *wcPts; 
  int i, error;
  unsigned int viewIndex;
  unsigned long nPts;
  PEXDeviceCoord *pdc;
  PEXMatrix mci;
  PEXCoord q;
  Status status;

/* hack, hack - this should be in wksSetView but it gets a proto error there ?
 * ?
 */
  PEXSetWorkstationViewPriority(theDisplay, theRenderObj, 1, 0, PEXHigher);

  /* should just do this, but ... */

  pdc = (PEXDeviceCoord *)malloc(nCoords*sizeof(PEXDeviceCoord));
  if (!pdc) return(0);

  for  (i = 0; i < nCoords; i++ ) {
    pdc[i].x = xPoints[i].x;
    pdc[i].y = theWinHeight - xPoints[i].y;
    pdc[i].z = theDepth;
  }

  status = PEXMapDCToWC(theDisplay, theRenderObj, nCoords, pdc,
		  &viewIndex, &nPts, mcPoints );
  if (!status || (nPts != nCoords )) {
    printf("need %d points, got %d\n", nCoords, nPts );
    if (*mcPoints) XFree((char *)*mcPoints);
    *mcPoints = (PEXCoord *)0;
    return (0);
  }

  error = PEXInvertMatrix( mc->matrix, mci );
  if(error != 0) printf("imagine a Motif alarm, Invert err = %d\n",error);

  /* take to Model Space BUG!!! 
    PEXTransformPoints(mci, nCoords, *mcPoints, *mcPoints);
   */
  for (i = 0; i < nCoords; i++ ) {
    PEXTransformPoints(mci, 1, &((*mcPoints)[i]), &q);
    (*mcPoints)[i] = q;
  }

  return (nCoords);
}

/*************************************************************************
 * MapMCToX
 *
 * MC xform struct.
 * count
 * points in
 * returns points out.
 * functions returns count
 */
int wksMapMCToX( mc, inCount, mcPoints, xPoints )
     MCMatrix *mc;
     int inCount;
     PEXCoord *mcPoints;
     XPoint **xPoints;
{
  int error;

  *xPoints = (XPoint *)malloc(inCount*sizeof(XPoint));
  if (!xPoints) { return (900); }

  /* take to Model Space */
  PEXTransformPoints(mc->matrix, inCount, mcPoints, mcPoints);

  if (error = MapWcPointsToX(theRenderObj, 1, inCount, mcPoints, *xPoints )) {
    printf("dream on wksMapMCToX\n");
    return (0);
  }

  return(inCount);
}

/*************************************************************************
 *
 */
void  wksSetView( viewNumber, view)
     int viewNumber;
     PEXViewEntry *view;
{
  PEXSetWorkstationViewRep( theDisplay, theRenderObj, viewNumber, view);

/*
 * we want to work in view 1, so when points get mapped make sure they 
 * have a preference for view 1.
 * pset_view_tran_in_pri(thePHIGSWorkstation, 1, 0, PPRI_HIGHER );
 */
/*
  PEXSetWorkstationViewPriority(theDisplay, theRenderObj, viewNumber, 0,
				PEXHigher);
*/
}

void  wksSetNPCToDC( volume, viewport )
     PEXNPCSubVolume *volume;
     PEXViewport *viewport;
{
   PEXSetWorkstationViewport(theDisplay, theRenderObj, viewport);

   PEXSetWorkstationWindow(theDisplay, theRenderObj, volume);
 }

void  wksPost( struxid )
     long struxid;
{
  PEXPostStructure(theDisplay, theRenderObj, struxid, 0.0);
}

void  wksUnpost( struxid )
     long struxid;
{
  PEXUnpostStructure(theDisplay, theRenderObj, struxid);
}

/*************************************************************************
 *  DeleteAllStrux
 *
 *  PHIGS Workstation Version, get all of the posted structres and delete them
 *  Should probably do a PEXGetDescendants, to be really thorough.
 */
void  wksDeleteAll()
{
  int i;
  PEXBitmask valueMask[2];
  PEXWorkstationAttributes *wksAttrs;

  /*
   * 
   */
  valueMask[0] = valueMask[1] = 0;
  PEXSetPWAttributeMask(valueMask,PEXPWPostedStructures);
  if (!(wksAttrs = PEXGetWorkstationAttributes(theDisplay, theRenderObj,
					       valueMask ))) {
    return;
  }

  theNewStrux = 0;
  theSelectedElement = -1;

  /*
   * Clear by doing a fill rect, since the SI does not seem to clear when
   * there are no structures posted, a bug, I think.
   */
  for ( i = 0; i < wksAttrs->posted_structures.count; i++ ) {
    PEXDestroyStructures(theDisplay, 1,
			 &wksAttrs->posted_structures.structures[i].sid);
  }
  PEXFreeWorkstationAttributes(wksAttrs);
}


/*************************************************************************
 * PickSomething - returns structure and element, else element == -1
 */
#if NeedFunctionPrototypes
/* need to do this to avoid warning about initialization, due to shorts */
void  wksPickOne( short x, short y, long *strux, int *element )
#else
void  wksPickOne( x, y, strux, element )
     short x;
     short y;
     long *strux;
     int *element;
#endif
{
  PEXPDDCHitBox hitInfo;
  PEXPMAttributes *pickReturn;
  int i;
  PEXPickMeasure pmID;

  if (!thePickInitFlag) {
    InitPickDevice();
    thePickInitFlag = 1;
  }

  pmID = PEXCreatePickMeasure( theDisplay, theRenderObj,
			      PEXPickDeviceDCHitBox);

  hitInfo.position.x = x;
  hitInfo.position.y = theWinHeight - y;
  hitInfo.distance = 10;
  PEXUpdatePickMeasure( theDisplay, pmID,  PEXPickDeviceDCHitBox,
		       (PEXPickRecord *)&hitInfo);

  pickReturn = PEXGetPickMeasure(theDisplay, pmID, (PEXPMStatus|PEXPMPath));

  if (pickReturn->status) {
    /*
     * Get the last struxure/elementent in the pick path
     */
    *strux = pickReturn->pick_path.elements[pickReturn->pick_path.count-1].sid;
    *element = 
      pickReturn->pick_path.elements[pickReturn->pick_path.count-1].offset;
  } else {
    *element = -1;
  }
  PEXFreePMAttributes(pickReturn);
  /*
   * No way to set the status and path on this guy, free it */
  PEXFreePickMeasure(theDisplay, pmID);
}

/*************************************************************************
 * wksPick all not there yet. do a picOne
 */
#if NeedFunctionPrototypes
/* need to do this to avoid warning about initialization, due to shorts */
void wksPickAll( short x, short y, short x2, short y2,
		int *nFound, long **struxArray, int **elemArray )
#else
void wksPickAll( x, y, x2, y2, nFound, struxArray, elemArray )
     short x;
     short y;
     short x2;
     short y2;
     int *nFound;
     long **struxArray;
     int **elemArray;
#endif
{
  long s;
  int e;

  printf("we don't do no stinkin' pick all in wks\n");
  wksPickOne( x, y, &s, &e );
  if (e != -1) {
    *nFound = 1;
    *struxArray = (long *)malloc(sizeof(long));
    *elemArray = (int *)malloc(sizeof(int));
    (*struxArray)[0] = s;
    (*elemArray)[0] = e;
  } else *nFound = 0;
}


int wksMapXToNPC( count, points)
     int count;
     PEXCoord *points;
{
  int i;
  PEXMatrix XCtoNPC;
  unsigned long valueMask[2];
  PEXWorkstationAttributes *wksAttrs;

  /*
   * We rely on the fact that we always use the whole window for the
   * Workstation viewport.  Get the information we want, incase this
   * scratch area gets creamed.
   */
  valueMask[0] = valueMask[1] = 0;

  PEXSetPWAttributeMask(valueMask,PEXPWCurViewport);
  PEXSetPWAttributeMask(valueMask,PEXPWCurNPCSubVolume);
  
  if (!(wksAttrs = PEXGetWorkstationAttributes(theDisplay, theRenderObj, valueMask ))) {
    return (-1);
  }

  i = PEXXCToNPCTransform( &wksAttrs->cur_npc_subvolume, 
		    (PEXDeviceCoord *)&wksAttrs->cur_workstation_viewport, 
			  theWinHeight, XCtoNPC );
  PEXFreeWorkstationAttributes( wksAttrs );
  if (i!=0) {printf("rdrPickAll bad XCtoNPC %d\n", i); return (0); }

  PEXTransformPoints(XCtoNPC, count, points, points);
  return (1);
}

int wksReconfigureWindow( wkid, width, height)
     long wkid;
     int width;
     int height;
{
   if (theWimpyWindow)
     XResizeWindow(theDisplay, theWindow, width, height );
}

/*************************************************************************
 *
 */
int wksGetColorFromIndex(cIndex, rgb)
     int cIndex;
     PEXColorRGB *rgb;
{
  int table_type;
  int defined;
  PEXColorSpecifier *entry;
  PEXBitmask valueMask[2];
  PEXWorkstationAttributes *wksAttrs;

  valueMask[0] = valueMask[1] = 0;
  PEXSetPWAttributeMask(valueMask,PEXPWColorTable);
  if (!(wksAttrs = PEXGetWorkstationAttributes(theDisplay, theRenderObj,
					       valueMask ))) {
    return (0);
  }

  entry = (PEXColorSpecifier *)PEXGetTableEntry(theDisplay, wksAttrs->color_table,
						cIndex, PEXSetValue, &defined,
						&table_type);
  if (table_type != PEXLUTColor) {
    printf("rdrGetColorFromIndex fix it!!!\n");
  }
  if (entry->type != PEXColorTypeRGB) printf("RGB NOT %d\n");
  *rgb = entry->value.rgb;

  PEXFreeTableEntries(table_type, 1,(PEXPointer)entry);
  PEXFreeWorkstationAttributes(wksAttrs);
}

renderProcs wksProcs = { wksInit, wksReDraw, wksMapXToMC, wksMapMCToX, 
			   wksSetView, wksSetNPCToDC, wksPost, wksDeleteAll,
			   wksPickOne, wksPickAll, wksMapXToNPC,
			   wksReconfigureWindow, wksGetColorFromIndex,
			   wksUnpost };

