/***************************************************************************\
 *
 *  alignment
 *
\***************************************************************************/

#define INCL_DOSFILEMGR   /* File Manager values */
#define INCL_DOSDEVICES   /* Device values */
#define INCL_DOSSEMAPHORES
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "penei.h"
#include "penioctl.h"
#include "cal.h"

#define FILE_SIZE   0L
#define EABUF 0L
#define MAX_SAMPLE_SIZE 100
#define MIN_SAMPLE_SIZE 10
#define MIN_THRESHOLD   2
#define THRESH_DIV      1000;
//#define SAMPLE_RATE_DIV 2;
  #define SAMPLE_RATE_DIV 4;   //each sample is 1/4 a second
#define BEEP_TONE 300
#define BEEP_TIME 75
#define FAST_TIMEOUT 500
#define SLOW_TIMEOUT 2500

#define ALIGN_START 1
#define ALIGN_CANCEL 2
#define ALIGN_TERMINATE 3

extern char IOName[];
#ifdef DEBUG
extern BOOL  dbfile;
extern FILE  *stream;
#endif
extern BOOL  yinvert;
extern char _DriverName[];
extern char _DeviceName[];

LONG  thresX,thresY;
LONG  sampleSize;
APIRET  rc;
HFILE IOHandle;
LCAP lcap;
SLUSD slusd;
SLUSD slusdOrg;
LONG penUnit = 1;
HEV  hev;
LONG alignCmd;
BOOL abortAlign;
LONG scrX,scrY,iconX,iconY,diconX,diconY;
LONG meaX,meaY,orgX,orgY;
LONG wrtQuery(SLUSD * slusd);
LONG wrtSet(SLUSD * slusd);
LONG getSLUSD(LCAP * lcap,SLUSD * slusd);
LONG setSLUSD(LCAP * lcap,SLUSD * slusd);
#ifdef DEBUG
LONG savSLUSD(LCAP * lcap,SLUSD * slusd);
#endif

void doAlignment (void *arg);
BOOL inbounds(LONG x,LONG y,TARGETAREA * ptargetarea);
LONG collectSample (LONG size,LONG * paveX,LONG * paveY,
                    TARGETAREA * ptargetarea);
LONG getcoord(LONG * pX,LONG * pY,TARGETAREA * ptargetarea);
LONG doIO  (ULONG category, ULONG function,
             PVOID pParams, LONG pcbParmLen,
             PVOID pData, LONG pcbDataLen);
#ifdef DEBUG
void dumpTA (TARGETAREA * ptargetarea);
void dumpArray(char * ptext,LONG num,LONG array[],LONG limit);
#endif

APIRET APIENTRY WrtIQueryDeviceDriverName(PSZ     pszDriverName,
                                          PSZ     pszDDName);

//***************************************************************************** */
//
// T H E S E   R O U T I N E S   A R E   C A L L E D   F R O M   T H E
//         F R O M   T H E   W I N D O W   P R O C E D U R E
//
//***************************************************************************** */

//-----------------------------------------------------------------------------
// Called once per window life to initialize access to the device
//-----------------------------------------------------------------------------
LONG AlignInit(LONG lscrX, LONG lscrY,LONG liconX,LONG liconY)
{
   ULONG Action;
   PQUC  pquc;
   LONG  rc;
   LONG  cnt;
   UCHAR numberUnits;
   DDCAP ddcap;

   iconX  = liconX;
   iconY  = liconY;
   scrX   = lscrX;
   scrY   = lscrY;
#ifdef DEBUG
   if (dbfile) fprintf(stream,"screen scrX=%d scrY=%d icon X=%d icon y=%d\n",
                       scrX,scrY,iconX,iconY);
#endif

// find the device driver

    rc=WrtIQueryDeviceDriverName(_DriverName,&(IOName[5]));
#ifdef DEBUG
    if (rc != 0) {
        if (dbfile) fprintf(stream,"WrtIQueryDDName return code = %d\n", rc);
        return(rc);
    }
#endif

// do the open

#ifdef DEBUG
   if (dbfile) fprintf(stream,"driver=%s\n",IOName);
#endif
   Action = 2;
   rc = DosOpen(IOName,                     /* File path name */
                &IOHandle,                  /* File handle */
                &Action,                    /* Action taken */
                FILE_SIZE,                  /* File primary allocation */
                FILE_NORMAL,                /* File attribute */
                OPEN_ACTION_OPEN_IF_EXISTS, /* Open function type */
                OPEN_SHARE_DENYNONE |       /* Open mode of the file */
                OPEN_ACCESS_READWRITE,
                EABUF);                     /* No extended attributes */

   if (rc != 0) {
#ifdef DEBUG
       if (dbfile) fprintf(stream,"Cannot open device driver\n");
       if (dbfile) fprintf(stream,"DOSOPEN return code = %d\n", rc);
#endif
       return(rc);
   }

// find the first locator device by name

   pquc.unit  = 0;
   pquc.byteCount = sizeof(LCAP);
   rc=doIO(PEN_CAT_DRIVER,PEN_FUNC_QUC,
          &pquc, sizeof(PQUC),
          &ddcap, sizeof(DDCAP));
   if (rc) return (rc);
   numberUnits=ddcap.unitCount;
#ifdef DEBUG
   if (dbfile) fprintf(stream,"number of units=%d\n",numberUnits);
#endif

   for (penUnit=1; penUnit<numberUnits;penUnit++) {
      pquc.unit  = penUnit;
      rc=doIO(PEN_CAT_DRIVER,PEN_FUNC_QUC,
              &pquc, sizeof(PQUC),
              &lcap, sizeof(LCAP));
      if (rc) return rc;
      if (lcap.ccap.device_type==DT_LOCATOR) {
         // find the last non-blank character, then terminate string
         for (cnt=1;cnt<SIZEOF_DEVICE_NAME&&lcap.ccap.device_name[cnt];cnt++);
         for (cnt--;cnt>0&&lcap.ccap.device_name[cnt]==' ';cnt--);
         lcap.ccap.device_name[cnt+1]=0;
         if (!stricmp(_DeviceName,lcap.ccap.device_name)) {
            rc = -1;
            break;
         }
      }
   } /* endfor */
   if (rc==0) {
#ifdef DEBUG
      if (dbfile) fprintf(stream,"device not found\n");
#endif
      return 1;
   }
#ifdef DEBUG
   else {
      if (dbfile) fprintf(stream,"unit number = %d\n",penUnit);
   }
#endif

// calculate limits based on device capabilities

#ifdef DEBUG
   if (dbfile) fprintf(stream,"sampleRate=%d xExent=%d yExent=%d ",
       lcap.sample_rate,lcap.dev_x_extent,lcap.dev_y_extent);
#endif
   diconX=(iconX*lcap.dev_x_extent)/scrX;
   diconY=(iconX*lcap.dev_y_extent)/scrY;
#ifdef DEBUG
   if (dbfile) fprintf(stream,"dicon X=%d dicon y=%d\n",
                       diconX,diconY);
#endif
   sampleSize=lcap.sample_rate/SAMPLE_RATE_DIV;
   if (sampleSize<MIN_SAMPLE_SIZE) sampleSize=MIN_SAMPLE_SIZE;
   if (sampleSize>MAX_SAMPLE_SIZE) sampleSize=MAX_SAMPLE_SIZE;


   thresX=lcap.dev_x_extent/THRESH_DIV;
   if (thresX<MIN_THRESHOLD) thresX=MIN_THRESHOLD;

   thresY=lcap.dev_y_extent/THRESH_DIV;
   if (thresY<MIN_THRESHOLD) thresY=MIN_THRESHOLD;
#ifdef DEBUG
   if (dbfile) fprintf(stream,"sampleSize=%d thresX=%d thresY=%d\n",
                       sampleSize,thresX,thresY);
#endif

// get the command semaphore set up

   rc=DosCreateEventSem(0,&hev,0L,0L);
   if (rc != 0) {
#ifdef DEBUG
       if (dbfile) fprintf(stream,"DosCreateEventSem rc=%d\n", rc);
#endif
       return(rc);
   }

// get the standard locator unit specific data (SLUSD) incase of cancel to
// restore to entry values

   rc = wrtQuery(&slusd);
   if (rc != 0) {
#ifdef DEBUG
       if (dbfile) fprintf(stream,"wrtQuery rc=%d\n", rc);
#endif
       return(rc);
   }
   slusdOrg=slusd;      // save incase of cancel

   rc = getSLUSD( &lcap, &slusd );
   if ( rc != 0 ) {
#ifdef DEBUG
      if ( dbfile ) fprintf( stream, "getSLUSD rc = %d\n", rc );
#endif
      return( rc );
    }
// start the alignment thread

   rc = _beginthread(doAlignment,NULL,8192,NULL);
   if (rc == -1) {
#ifdef DEBUG
       if (dbfile) fprintf(stream,"beginthread rc=%d\n", rc);
#endif
       return(rc);
   }

   return 0;
}

//-----------------------------------------------------------------------------
// Signal the background thread to do an alignment
//-----------------------------------------------------------------------------
void AlignStart(void)
{
#ifdef DEBUG
   if (dbfile) fprintf(stream,"****** S T A R T ******************\n");
#endif
   alignCmd=ALIGN_START;
   DosPostEventSem(hev);
}

//-----------------------------------------------------------------------------
// Sinal the background thread stop alignment and wait for next command
//-----------------------------------------------------------------------------
void AlignStop(void)
{
#ifdef DEBUG
   if (dbfile) fprintf(stream,"****** S T O P ********************\n");
#endif
   alignCmd=ALIGN_CANCEL;
   abortAlign=TRUE;
   DosPostEventSem(hev);
}

//-----------------------------------------------------------------------------
// Cancel and restore original settings
//-----------------------------------------------------------------------------
void AlignCancel(void)
{
#ifdef DEBUG
   if (dbfile) fprintf(stream,"****** C A N C E L ****************\n");
#endif
   rc = setSLUSD(&lcap,&slusdOrg);
#ifdef DEBUG
   if (rc != 0) {
       if (dbfile) fprintf(stream,"setSLUSD rc=%d\n", rc);
   }
#endif
}

//-----------------------------------------------------------------------------
// Set up shipped defaults
//-----------------------------------------------------------------------------
void AlignDefaults(void)
{
#ifdef DEBUG
   if (dbfile) fprintf(stream,"****** D E F A U L T S ************\n");
#endif
   slusd.Xorigin=slusd.XoriginDefault;
   slusd.XmeasuredExtent=lcap.dev_x_extent;
   slusd.Yorigin=slusd.YoriginDefault;
   slusd.YmeasuredExtent=lcap.dev_y_extent;
   rc = setSLUSD(&lcap,&slusd);
#ifdef DEBUG
   if (rc != 0) {
       if (dbfile) fprintf(stream,"setSLUSD rc=%d\n", rc);
   }
#endif
}

//-----------------------------------------------------------------------------
// Save these setting to be persistent
//-----------------------------------------------------------------------------
void AlignCommit(void)
{
#ifdef DEBUG
   if (dbfile) fprintf(stream,"****** C O M M I T ****************\n");
#endif
   rc = wrtSet(&slusd);
#ifdef DEBUG
   if (rc != 0) {
       if (dbfile) fprintf(stream,"wrtSet rc=%d\n", rc);
   }
   savSLUSD(&lcap,&slusd);   //for debug file
#endif
}

//-----------------------------------------------------------------------------
// Called once at end of window life
//-----------------------------------------------------------------------------
void AlignTerminate(void)
{
#ifdef DEBUG
   if (dbfile) fprintf(stream,"****** T E R M I N A T E **********\n");
#endif
   alignCmd=ALIGN_TERMINATE;
   DosPostEventSem(hev);
   DosClose(IOHandle);
}

//***************************************************************************** */
//
// T H E S E   R O U T I N E S   E X E C U T E  U N D E R  T H E
//             B A C K   G R O U N D   T H R E A D
//
//***************************************************************************** */

//-----------------------------------------------------------------------------
// B A C K G R O U N D   T H R E A D   M A I N   L O O P
//-----------------------------------------------------------------------------
// Background thread waits for commands
//  - Drive window proc and do an alignment
//  - Canel out of alignment and wait for next command
//  - Terminate background thread
//-----------------------------------------------------------------------------
void doAlignment (void *arg)
{
   TARGETAREA Stargetarea;
   TARGETAREA Dtargetarea;
   ULONG postCount;
   LONG  x,y;
   APIRET  rc;
   LONG  getrc;
   LONG  aveXlow,aveYlow,aveXhigh,aveYhigh,tempY;
   LONG  meaX,meaY,orgX,orgY;

// wait for command to continue

   do {

      rc=DosWaitEventSem(hev,SEM_INDEFINITE_WAIT);
      if (rc != 0) {
#ifdef DEBUG
          if (dbfile) fprintf(stream,"$$DosWaitEventSem rc=%d\n", rc);
#endif
          return;
      }
      rc=DosResetEventSem(hev,&postCount);
      if (rc != 0) {
#ifdef DEBUG
          if (dbfile) fprintf(stream,"$$DosResetEventSem rc=%d\n", rc);
#endif
          return;

      }

      if (alignCmd == ALIGN_START) {

         abortAlign=FALSE;
         aveXlow=aveYlow=aveXhigh=aveYhigh=0;

// UPPER LEFT

         Stargetarea.xlow=0;
         Stargetarea.xcen=iconX/2;
         Stargetarea.xhigh=iconX;
         Stargetarea.ylow=scrY-iconY;
         Stargetarea.ycen=Stargetarea.ylow+iconY/2;
         Stargetarea.yhigh=scrY;
         notifyPM (PAINT_TARGET,&Stargetarea);
         Dtargetarea.xlow=0;
         Dtargetarea.xcen=diconX/2;
         Dtargetarea.xhigh=diconX;
         if (yinvert) {
            Dtargetarea.ylow=lcap.dev_y_extent-diconY;
            Dtargetarea.ycen=Dtargetarea.ylow+diconY/2;
            Dtargetarea.yhigh=lcap.dev_y_extent;
         } else {
            Dtargetarea.ylow=0;
            Dtargetarea.ycen=diconY/2;
            Dtargetarea.yhigh=diconY;
         } /* endif */
         getrc=getcoord(&x,&y,&Dtargetarea);
         if (getrc) continue;
         aveXlow+=x;
         aveYlow+=y;
         DosBeep(BEEP_TONE,BEEP_TIME);

// UPPER RIGHT

         Stargetarea.xlow=scrX-iconX;
         Stargetarea.xcen=Stargetarea.xlow+iconX/2;
         Stargetarea.xhigh=scrX;
         Stargetarea.ylow=scrY-iconY;
         Stargetarea.ycen=Stargetarea.ylow+iconY/2;
         Stargetarea.yhigh=scrY;
         notifyPM (PAINT_TARGET,&Stargetarea);
         Dtargetarea.xlow=lcap.dev_x_extent-diconX;
         Dtargetarea.xcen=Dtargetarea.xlow+diconX/2;
         Dtargetarea.xhigh=lcap.dev_x_extent;
         if (yinvert) {
            Dtargetarea.ylow=lcap.dev_y_extent-diconY;
            Dtargetarea.ycen=Dtargetarea.ylow+diconY/2;
            Dtargetarea.yhigh=lcap.dev_y_extent;
         } else {
            Dtargetarea.ylow=0;
            Dtargetarea.ycen=diconY/2;
            Dtargetarea.yhigh=diconY;
         } /* endif */
         getrc=getcoord(&x,&y,&Dtargetarea);
         if (getrc) continue;
         aveXhigh+=x;
         aveYlow +=y;
         DosBeep(BEEP_TONE,BEEP_TIME);

// LOWER RIGHT

         Stargetarea.xlow=scrX-iconX;
         Stargetarea.xcen=Stargetarea.xlow+iconX/2;
         Stargetarea.xhigh=scrX;
         Stargetarea.ylow=0;
         Stargetarea.ycen=iconY/2;
         Stargetarea.yhigh=iconY;
         notifyPM (PAINT_TARGET,&Stargetarea);
         Dtargetarea.xlow=lcap.dev_x_extent-diconX;
         Dtargetarea.xcen=Dtargetarea.xlow+diconX/2;
         Dtargetarea.xhigh=lcap.dev_x_extent;
         if (yinvert) {
            Dtargetarea.ylow=0;
            Dtargetarea.ycen=diconY/2;
            Dtargetarea.yhigh=diconY;
         } else {
            Dtargetarea.ylow=lcap.dev_y_extent-diconY;
            Dtargetarea.ycen=Dtargetarea.ylow+diconY/2;
            Dtargetarea.yhigh=lcap.dev_y_extent;
         } /* endif */
         getrc=getcoord(&x,&y,&Dtargetarea);
         if (getrc) continue;
         aveXhigh+=x;
         aveYhigh+=y;
         DosBeep(BEEP_TONE,BEEP_TIME);

// LOWER LEFT

         Stargetarea.xlow=0;
         Stargetarea.xcen=iconX/2;
         Stargetarea.xhigh=iconX;
         Stargetarea.ylow=0;
         Stargetarea.ycen=iconY/2;
         Stargetarea.yhigh=iconY;
         notifyPM (PAINT_TARGET,&Stargetarea);
         Dtargetarea.xlow=0;
         Dtargetarea.xcen=diconX/2;
         Dtargetarea.xhigh=diconX;
         if (yinvert) {
            Dtargetarea.ylow=0;
            Dtargetarea.ycen=diconY/2;
            Dtargetarea.yhigh=diconY;
         } else {
            Dtargetarea.ylow=lcap.dev_y_extent-diconY;
            Dtargetarea.ycen=Dtargetarea.ylow+diconY/2;
            Dtargetarea.yhigh=lcap.dev_y_extent;
         } /* endif */
         getrc=getcoord(&x,&y,&Dtargetarea);
         if (getrc) continue;
         aveXlow +=x;
         aveYhigh+=y;
         DosBeep(BEEP_TONE,BEEP_TIME);

// calculate adjustment - extrapolate measured extents

#ifdef DEBUG
         if (dbfile) fprintf(stream,"$$**** C A L C U L A T E **********\n");
#endif
         aveXlow/=2;aveXhigh/=2;aveYlow/=2;aveYhigh/=2;

         if (yinvert) {
           tempY   = aveYlow;
           aveYlow = aveYhigh;
           aveYhigh= tempY;
         } /* endif */

#ifdef DEBUG
         if (dbfile) fprintf(stream,"$$actual ave pts x=%d %d y=%d %d\n",
                     aveXlow,aveXhigh,aveYlow,aveYhigh);
#endif
         meaX=(lcap.dev_x_extent*(aveXhigh-aveXlow))/(lcap.dev_x_extent-diconX);
         meaY=(lcap.dev_y_extent*(aveYhigh-aveYlow))/(lcap.dev_y_extent-diconY);
#ifdef DEBUG
         if (dbfile) fprintf(stream,"$$extraplated measured extents x=%d y=%d\n",
                             meaX,meaY);
#endif

// calculate origin adjustment

         aveXlow = (aveXlow * lcap.dev_x_extent)/meaX;
         aveXhigh= (aveXhigh* lcap.dev_x_extent)/meaX;
         aveYlow = (aveYlow * lcap.dev_y_extent)/meaY;
         aveYhigh= (aveYhigh* lcap.dev_y_extent)/meaY;
#ifdef DEBUG
         if (dbfile) fprintf(stream,"$$scaled ave pts x=%d %d y=%d %d\n",
                     aveXlow,aveXhigh,aveYlow,aveYhigh);
#endif
         orgX = ((LONG)lcap.dev_x_extent-aveXhigh-aveXlow)/2;
         orgY = ((LONG)lcap.dev_y_extent-aveYhigh-aveYlow)/2;
#ifdef DEBUG
         if (dbfile) fprintf(stream,"$$origin adjustment x=%d y=%d \n",
                     orgX,orgY);
#endif
         slusd.Xorigin=orgX;
         slusd.XmeasuredExtent=meaX;
         slusd.Yorigin=orgY;
         slusd.YmeasuredExtent=meaY;
         rc = setSLUSD(&lcap,&slusd);
#ifdef DEBUG
         if (rc != 0) {
             if (dbfile) fprintf(stream,"$$setSLUSD rc=%d\n", rc);
         }
#endif

// complete

         notifyPM (ALIGNMENT_DONE,NULL);

      } /* endif */

   } while (alignCmd != ALIGN_TERMINATE); /* enddo */

#ifdef DEBUG
   if (dbfile) fprintf(stream,"$$ thread terminating\n");
#endif
   rc=DosCloseEventSem(hev);
#ifdef DEBUG
   if (rc != 0) {
       if (dbfile) fprintf(stream,"$$DosCloseEventSem rc=%d\n", rc);
       return;
   }
#endif
}

//-----------------------------------------------------------------------------
// check if point is in the bounds of the target structure
//-----------------------------------------------------------------------------
BOOL inbounds(LONG x,LONG y,TARGETAREA * ptargetarea)
{
   x += (LONG) slusd.Xorigin;
   y += (LONG) slusd.Yorigin;
#ifdef DEBUG
   if (dbfile) fprintf(stream,"$$X=%d Y=%d \n", x,y); for debugging
   if (dbfile) dumpTA (ptargetarea);
#endif

// check out x

   if (ptargetarea->xlow==0) {
      if (x>ptargetarea->xhigh) {
         return(FALSE);
      } /* endif */
   } else {
      if (x<ptargetarea->xlow) {
         return(FALSE);
      } /* endif */
   } /* endif */

// check out y

   if (ptargetarea->ylow==0) {
      if (y>ptargetarea->yhigh) {
         return(FALSE);
      } /* endif */
   } else {
      if (y<ptargetarea->ylow) {
         return(FALSE);
      } /* endif */
   } /* endif */

   return(TRUE);

}

//-----------------------------------------------------------------------------
// collect a sample point by averageing points in a collection array
//-----------------------------------------------------------------------------
LONG collectSample (LONG size,LONG * paveX,LONG * paveY,
                    TARGETAREA * ptargetarea)
{
   LONG xarray[MAX_SAMPLE_SIZE];
   LONG yarray[MAX_SAMPLE_SIZE];
   int  i;
   LONG temp;
   PQLRC pqlrc;
   DQLRC dqlrc;
   LONG  rc;
   BOOL  stat;

   pqlrc.unit = penUnit;
   if (size>MAX_SAMPLE_SIZE) size=MAX_SAMPLE_SIZE; // protect ourselves

// collect points

   pqlrc.timeout = SLOW_TIMEOUT;
   do {
      for (i=0; i<size; i++) {
         rc=doIO(PEN_CAT_LOCATOR,PEN_FUNC_QLRC,
                 &pqlrc, sizeof(PQLRC),
                 &dqlrc, sizeof(DQLRC));
         if (rc) return rc;
         if (abortAlign) return 1;
         xarray[i]= dqlrc.xRaw;
         yarray[i]= dqlrc.yRaw;
         if (dqlrc.rc != QLRC_VALID) {
#ifdef DEBUG
         if (dbfile) fprintf(stream,"$$ !!! VALID !!!\n"); //for debug
#endif
            pqlrc.timeout = SLOW_TIMEOUT; //slow down again
#ifdef DEBUG
         dumpArray("timX=",dqlrc.rc,xarray,i+1);
         dumpArray("timY ",dqlrc.rc,yarray,i+1);
#endif
            break;
         } /* endif */
         stat = inbounds(dqlrc.xRaw,dqlrc.yRaw,ptargetarea);
         if (!stat) {
#ifdef DEBUG
            if (dbfile) fprintf(stream,"$$ !!! NOT IN BOUNDS !!!\n"); //for debug
#endif
            pqlrc.timeout = SLOW_TIMEOUT;    //slow down again
#ifdef DEBUG
            dumpArray("bndX=",stat,xarray,i+1);
            dumpArray("bndY ",stat,yarray,i+1);
#endif
            break;
         } /* endif */
         pqlrc.timeout = FAST_TIMEOUT;          // don't wait so long
      }
   } while (i<size); /* enddo */

// calculate average

   for (temp=0,i=0; i<size; i++) temp+=xarray[i];
   *paveX=temp/size;
   for (temp=0,i=0; i<size; i++) temp+=yarray[i];
   *paveY=temp/size;

#ifdef DEBUG
   dumpArray("aveX=",*paveX,xarray,size);
   dumpArray("aveY ",*paveY,yarray,size);
#endif

   return 0;
}

//-----------------------------------------------------------------------------
// compare samples until reports settle down
//-----------------------------------------------------------------------------
LONG getcoord(LONG * pX,LONG * pY,TARGETAREA * ptargetarea)
{
   LONG aveX,aveY,lastaveX,lastaveY,lastaveX2,lastaveY2;
   LONG xbound,ybound;

#ifdef DEBUG
   if (dbfile) fprintf(stream,"$$***** G E T C O O R D ***********\n");
   if (dbfile) dumpTA (ptargetarea);
#endif

// collect samples until the result settles within the threshold

   aveX=aveY=lastaveX=lastaveY=-1;
   do {
      lastaveX2=lastaveX;lastaveY2=lastaveY;
      lastaveX=aveX;lastaveY=aveY;
      rc = collectSample (sampleSize,&aveX,&aveY,ptargetarea);
      if (rc) return rc;

// this check makes sure the last three samples are close enough
// }  while ((abs(lastaveX-aveX)>thresX)||(abs(lastaveY-aveY)>thresY)||
//           (abs(lastaveX2-aveX)>thresX)||(abs(lastaveY2-aveY)>thresY) );

// this check makes sure the last two   samples are close enough. This is
// alot quicker, but could be less accurate
   }  while ((abs(lastaveX-aveX)>thresX)||(abs(lastaveY-aveY)>thresY) );

#ifdef DEBUG
   if (dbfile) fprintf(stream,"$$x=%d y=%d\n",aveX,aveY);
#endif
   *pX=aveX;*pY=aveY;
#ifdef DEBUG
   if (dbfile) fprintf(stream,"$$RETURN FROM GETCOORD\n");
#endif
   return 0;
}

//***************************************************************************** */
//  S U P P O R T   R O U T I N E S
//***************************************************************************** */

//-----------------------------------------------------------------------------
// Issue IOCTL to device driver
//-----------------------------------------------------------------------------
LONG doIO  (ULONG category, ULONG function,
             PVOID pParams, LONG pcbParmLen,
             PVOID pData, LONG pcbDataLen)
{
   ULONG   ParmLengthInOut;
   ULONG   DataLengthInOut;
   APIRET  rc;

   ParmLengthInOut = pcbParmLen;
   DataLengthInOut = pcbDataLen;
   rc = DosDevIOCtl(IOHandle, category, function,
                    pParams, pcbParmLen,&ParmLengthInOut,
                    pData, pcbDataLen,&DataLengthInOut);
#ifdef DEBUG
   if (rc != 0)
   {
       if (dbfile) fprintf(stream,"IOCTL failed, return code = %d\n", (SHORT)rc);
   }
#endif
   return(rc);
}

#ifdef DEBUG
//-----------------------------------------------------------------------------
// debug dump of the target area structure
//-----------------------------------------------------------------------------
void dumpTA (TARGETAREA * ptargetarea)
{
   if (dbfile) {
      fprintf(stream,"$$xlow=%d xcen=%d xhigh=%d ylow=%d ycen=%d yhigh=%d\n",
              ptargetarea->xlow,ptargetarea->xcen,ptargetarea->xhigh,
              ptargetarea->ylow,ptargetarea->ycen,ptargetarea->yhigh);
   } /* endif */
}

//-----------------------------------------------------------------------------
// debug dump of collected points in a point collection arrary
//-----------------------------------------------------------------------------
void dumpArray(char * ptext,LONG num,LONG array[],LONG limit)
{
   int  i;

   if (dbfile) {
      fprintf(stream,"$$%s%d",ptext,num);
      for (i=0; i < limit; i++) fprintf(stream," %d",array[i]);
      fprintf(stream,"\n");
   } /* endif */
}
#endif
