/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
#define INCL_WIN
#define INCL_GPI
#define INCL_DOSMODULEMGR
#include <os2.h>
#include <stdio.h>
#include <cal.h>
#include <calres.h>
#include <penpm.h>

#define PENPMMRI  "PENPMMRI"  /* name of NLS DLL */

#ifdef DEBUG
extern FILE * stream;
extern BOOL dbfile;
#endif
extern HAB  habMain;
extern ULONG scrWidth,scrHeight;
extern HWND hwndClient;
extern FONTMETRICS    fm;
HMODULE hmodMRI = 0;

HWND hwndAdjust;
HPS  hpsAdjust;
HDC  hdcAdjust;
char adjName[] = "ADJ";
HPOINTER     hptrTarget;              //pointer to target icon
HPOINTER     hptrSYS;                 //pointer to system pointer
ULONG        maxLineSize;

//-----------------------------------------------------------------------------
// These global variables are used to control the mode of the window
// procedure.
//-----------------------------------------------------------------------------
#define MODE_ALIGN   1
#define MODE_TEST    2
#define MODE_IDLE    3

TARGETAREA * ptargetarea;             //pointer to target area to paint target
TARGETAREA   nocursorarea;            //target area where there is nocursor
BOOL         areaChangePending;       //change nocusor area is pending
BOOL         nocursorValid;           //the nocursor target area is valid
HPOINTER     hptrMSE;                 //pointer that the MOUSEMOVE should use
CHAR       * boxText;                 //pointer to text in the box

//-----------------------------------------------------------------------------
// got to move out this text to the resource file
//-----------------------------------------------------------------------------
#define MAX_LINE_SIZE 100
#define MAX_BUT_SIZE 30
CHAR  calText[MAX_LINE_SIZE*3 + 5];
CHAR  tesText[MAX_LINE_SIZE*5 + 8];
CHAR  deftext[MAX_LINE_SIZE+2];
CHAR  deftitle[MAX_LINE_SIZE+2];
CHAR  DDerrText[MAX_LINE_SIZE+2];
CHAR  szButtonLabel[6][MAX_BUT_SIZE];

//-----------------------------------------------------------------------------
// local procedures
//-----------------------------------------------------------------------------
MRESULT APIENTRY AdjustWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
void draw_text( HPS hps, char *text, RECTL *text_box, FONTMETRICS *pfm);
BOOL landingArea(POINTL *ptl,TARGETAREA * ptargetarea);
void notifyPM (LONG notifyCmd,TARGETAREA * ptargetarea);
void setMode(LONG mode);
void initMode();
void updateNocursorArea(POINTL * pptl);
void updatearea(void);

//*****************************************************************************
// Window proc for alignment
//*****************************************************************************
MRESULT APIENTRY AdjustWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
{
   #define  NUMBUTTONS 6
   BOOL           pmrc;
   static   HWND  hwndButton[NUMBUTTONS];
   static   RECTL textBox;
   SIZEL          sizlPage;
   SHORT          id;
   USHORT         mesRC;
   static   LONG  bitCenX;
   static   LONG  bitCenY;
   HELPINIT       helpInit;
   static   CHAR  szWindowTitle[1];
   static   HWND  hwndHelpInstance = NULLHANDLE;
   ULONG          butPos;
   ULONG          butSize;

   switch (msg) {
   case WM_CREATE:

      hdcAdjust = WinOpenWindowDC (hwnd);
      sizlPage.cx = 0;
      sizlPage.cy = 0;
      hpsAdjust = GpiCreatePS (habMain,hdcAdjust,&sizlPage,PU_PELS|GPIA_ASSOC);
      hptrTarget=WinLoadPointer(HWND_DESKTOP,0L,ID_TAR);
      hptrSYS=WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW,FALSE);

      {
         POINTERINFO ptrinfo;
         BITMAPINFOHEADER2 bminfo;

         pmrc=WinQueryPointerInfo(hptrTarget,&ptrinfo);
         bminfo.cbFix=sizeof(BITMAPINFOHEADER2);
         pmrc=GpiQueryBitmapInfoHeader(ptrinfo.hbmPointer,&bminfo);
         bitCenX=bminfo.cx/2;
         bitCenY=bminfo.cx/2;

         if (AlignInit(scrWidth,scrHeight,bminfo.cx,bminfo.cx)) {
            WinMessageBox(HWND_DESKTOP,hwnd,DDerrText,"",0,
                       MB_OK | MB_ERROR | MB_SYSTEMMODAL | MB_MOVEABLE);
            WinDestroyWindow(hwnd);
            break;
         }
      }
      initMode();
      setMode(MODE_TEST);

      GpiSetBackMix(hpsAdjust,BM_OVERPAINT);

// do some size figuring

      maxLineSize += 10; //add some fudge factor
      if (scrWidth>=fm.lAveCharWidth*maxLineSize) {
         textBox.xLeft=(scrWidth-fm.lAveCharWidth*maxLineSize)/2;
         textBox.xRight=(scrWidth+fm.lAveCharWidth*maxLineSize)/2;
      } else {
         textBox.xLeft=1;
         textBox.xRight=scrWidth-1;
      } /* endif */
#ifdef DEBUG
      if (dbfile) fprintf(stream,"xLeft=%d xRight=%d\n",textBox.xLeft,textBox.xRight);
#endif
      textBox.yTop=(scrHeight+fm.lMaxBaselineExt*7)/2 ;
      textBox.yBottom=(scrHeight-fm.lMaxBaselineExt*7)/2;

// set up the rest of the window

      butSize=0;
      for (id=0; id<(NUMBUTTONS); id++) {
         butSize+=fm.lAveCharWidth*(strlen(szButtonLabel[id])+5);
      } /* endfor */
      butPos = (scrWidth-butSize)/2;

      for (id=0; id<(NUMBUTTONS-1); id++) {
         butSize=fm.lAveCharWidth*(strlen(szButtonLabel[id])+5);
#ifdef DEBUG
         //if (dbfile) fprintf(stream,"butPos = %d, butSize = %d\n",butPos,butSize);
#endif
         hwndButton[id]=WinCreateWindow(
            hwnd,
            WC_BUTTON,
            szButtonLabel[id],
            WS_VISIBLE|BS_PUSHBUTTON,
            butPos,scrHeight/5,
            butSize,2*fm.lMaxBaselineExt,
            hwnd,
            HWND_BOTTOM,
            IDM_BASE+id,
            NULL,
            NULL);

            butPos+=butSize;
      } /* endfor */

      butSize=fm.lAveCharWidth*(strlen(szButtonLabel[5])+5);
#ifdef DEBUG
      //if (dbfile) fprintf(stream,"butPos = %d, butSize = %d\n",butPos,butSize);
#endif
      hwndButton[5]=WinCreateWindow(
         hwnd,
         WC_BUTTON,
         szButtonLabel[5],
         WS_VISIBLE|BS_PUSHBUTTON|BS_HELP,
         butPos,scrHeight/5,
         butSize,2*fm.lMaxBaselineExt,
         hwnd,
         HWND_BOTTOM,
         IDM_BASE+5,
         NULL,
         NULL);

// inititalize help init structure

      helpInit.cb = sizeof(HELPINIT);
      helpInit.ulReturnCode = 0L;

      helpInit.pszTutorialName = (PSZ)NULL;   /* if tutorial added,             */
                                              /* add name here                  */
      helpInit.phtHelpTable = (PHELPTABLE)MAKELONG(HELPTABLE_RESID,
                               0xFFFF);
      helpInit.hmodHelpTableModule = NULLHANDLE;
      helpInit.hmodAccelActionBarModule = NULLHANDLE;
      helpInit.idAccelTable = 0;
      helpInit.idActionBar = 0;

      szWindowTitle[0] = '\0';
      helpInit.pszHelpWindowTitle = (PSZ)szWindowTitle;
      helpInit.fShowPanelId = CMIC_HIDE_PANEL_ID;
      helpInit.pszHelpLibraryName = "psetup.hlp";

      hwndHelpInstance = WinCreateHelpInstance((HAB)0, &helpInit);

#ifdef DEBUG
      if (!hwndHelpInstance || helpInit.ulReturnCode)
      {
         if (dbfile) fprintf(stream,"HwndHelpInstance is Bad\n");

         WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,"HwndHelpInstance is Bad",
                   "",0,
                   MB_OK | MB_INFORMATION | MB_SYSTEMMODAL | MB_MOVEABLE);
      }

      if (!WinAssociateHelpInstance(hwndHelpInstance, hwnd))
      {
       WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,"WinAssociateHelp is Bad",
                 "",0,
                 MB_OK | MB_INFORMATION | MB_SYSTEMMODAL | MB_MOVEABLE);
      }
#else
      WinAssociateHelpInstance( hwndHelpInstance, hwnd );
#endif
      break;

   case WM_TOUCHDOWN:                                  // Touchdown ocurred
      return  (MRESULT) ( TDN_IMMEDIATE                // Never Issue BUTxDOWN
                        + TDN_NO_INK_STROKE);          // Don't Ink Stroke
      break;

// We want to hide the cursor when the pen is in the targetarea. The targetarea
// is the icon that the pen should be placed on during alignment mode.
// The nocursorArea is the rectl where we don't want a cursor to show.
//
// Thats the easy part. When a targetarea moves to a new spot, we want to keep
// the old nocursor area til the pen moves out of the old target area. This way
// the old target area is blank and it encourages the pen to move to the new
// target area. This is indicated by the areaChangePending flag.
case WM_MOUSEMOVE:
      {
         POINTL ptl;
         WinQueryPointerPos(HWND_DESKTOP,&ptl);

         if (areaChangePending) {
            updateNocursorArea(&ptl);   //update area if pen has moved
         } /* endif */

         if (nocursorValid) {        //we have a nocursor area
            if (landingArea(&ptl,&nocursorarea)) {
               WinSetPointer(HWND_DESKTOP,(HPOINTER)NULL);  //hide the cursor
            } else {
               WinSetPointer(HWND_DESKTOP,hptrMSE);     //outside, OK to show
            } /* endif */
         } else {
            WinSetPointer(HWND_DESKTOP,hptrMSE); // no nocusor area, OK to show
         } /* endif */
      }
      break;

   case WM_COMMAND:
      switch (COMMANDMSG(&msg)->cmd) {
      case IDM_DEFAULTS:
         AlignStop();
         setMode(MODE_IDLE);
         WinPostMsg(hwndAdjust,DEFAULTS_PART2,0,0);
         break;
      case IDM_CALIBRATE:
         AlignStop();
         setMode(MODE_ALIGN);
         AlignStart();
         break;
      case IDM_TEST:
         AlignStop();
         setMode(MODE_TEST);
         break;
      case IDM_OK:
         AlignStop();
         AlignCommit();
         WinDestroyWindow(hwnd);
         break;
      case IDM_CANCEL:
         AlignStop();
         AlignCancel();
         WinDestroyWindow(hwnd);
         break;
      default:
         return WinDefWindowProc(hwnd,msg,mp1,mp2);
      } /* endswitch */
      WinInvalidateRect (hwnd, NULL, FALSE);
      break;

   case WM_BUTTON1DOWN:
      WinSetFocus (HWND_DESKTOP, hwnd);
      break;

// these next two case are messages from the align program background thread
   case PAINT_TARGET :              //move to a new collection point
      ptargetarea=mp2;              //select a target area
      areaChangePending=TRUE;       //nocursor area change pending
      WinInvalidateRect (hwnd, NULL, FALSE);  //repaint
      break;
   case ALIGNMENT_DONE:             //done
      ptargetarea=0;                //no target to paint, no new nocursor area
      areaChangePending=TRUE;       //keep nocursor area till its moved from
      setMode(MODE_TEST);
      WinInvalidateRect (hwnd, NULL, FALSE);  //repaint
      break;

// I send this to myself to do the second part of default comand
   case DEFAULTS_PART2:             //done
      mesRC=WinMessageBox(HWND_DESKTOP,hwnd,deftext,deftitle,0,MB_OKCANCEL|MB_NOICON);
      if (mesRC==MBID_OK) {
          AlignDefaults();
      } /* endif */
      setMode(MODE_TEST);
      WinInvalidateRect (hwnd, NULL, FALSE);  //repaint
      break;

   case WM_PAINT:
      {
         RECTL rcl;

         WinBeginPaint(hwnd,hpsAdjust,NULL);
         WinQueryWindowRect(hwnd,&rcl);
         WinFillRect(hpsAdjust,&rcl,CLR_BACKGROUND);
         if (ptargetarea) {
            WinDrawPointer(hpsAdjust,(ptargetarea->xcen)-bitCenX,
               (ptargetarea->ycen)-bitCenY,
               hptrTarget,DP_NORMAL);
         } /* endif */
         if (boxText) {
            draw_text(hpsAdjust,boxText,&textBox,&fm);
         } /* endif */
         WinEndPaint(hpsAdjust);
      }
      break;

   case WM_DESTROY:
      AlignTerminate();
      GpiDestroyPS(hpsAdjust);
      WinSendMsg(hwnd,WM_CLOSE,NULL,NULL);
      break;

   default:
      return WinDefWindowProc(hwnd,msg,mp1,mp2);
   } /* endswitch */

   return FALSE;
}
//***************************************************************************** */
// S U P P O R T   R O U T I N E S
//*****************************************************************************

//-----------------------------------------------------------------------------
// draw text in the box
//-----------------------------------------------------------------------------
void draw_text( HPS hps, char *text, RECTL *text_box, FONTMETRICS *pfm)
{
   RECTL  line_box;
   char   line[MAX_LINE_SIZE+2];
   char   *lp;

   WinDrawBorder( hps, text_box, 1, 1,
                  CLR_NEUTRAL, CLR_BACKGROUND,
                  DB_STANDARD );

   line_box.xLeft   = text_box->xLeft   + pfm->lAveCharWidth;
   line_box.xRight  = text_box->xRight  - pfm->lAveCharWidth;
   line_box.yTop    = text_box->yTop    - pfm->lMaxBaselineExt;
   line_box.yBottom = line_box.yTop     - pfm->lMaxBaselineExt;

   for ( lp=line; TRUE; text++ ) {
      if ( *text == '\n' || *text == '\0' ) {
         *lp = '\0';
         WinDrawText (hps, -1, line, &line_box,
                     CLR_NEUTRAL, CLR_BACKGROUND,
                     DT_CENTER | DT_ERASERECT) ;
         lp=line;
         line_box.yBottom -= pfm->lMaxBaselineExt;
         line_box.yTop    -= pfm->lMaxBaselineExt;
         if ( *text == '\0' ) {
            break;
         }
      }
      else {
         *lp++ = *text;
      }
   }
}

//-----------------------------------------------------------------------------
// see if point is in target area
//-----------------------------------------------------------------------------
BOOL landingArea(POINTL *ptl,TARGETAREA * ptargetarea)
{
   if (ptargetarea) {
      return ( (ptl->x >= ptargetarea->xlow ) &&
               (ptl->x <= ptargetarea->xhigh) &&
               (ptl->y >= ptargetarea->ylow ) &&
               (ptl->y <= ptargetarea->yhigh) );
   } else {
      return 0;
   } /* endif */
}
//-----------------------------------------------------------------------------
// select new mode
//-----------------------------------------------------------------------------
void setMode(LONG mode)
{
   switch (mode) {
   case MODE_ALIGN:
#ifdef DEBUG
      if (dbfile) fprintf(stream,"-- ALIGN MODE ----\n");
#endif
      hptrMSE=hptrSYS;       //use system pointer for mouse moves
      boxText=calText;       //select box text
      break;
   case MODE_TEST:
#ifdef DEBUG
      if (dbfile) fprintf(stream,"-- TEST  MODE ----\n");
#endif
      hptrMSE=hptrTarget;    //use target pointer for mouse moves
      boxText=tesText;       //select box text
      break;
   default:
#ifdef DEBUG
      if (dbfile) {
         fprintf(stream,"setMode: invalid mode=%d\n",mode);
      } /* endif */
#endif
   case MODE_IDLE:
#ifdef DEBUG
      if (dbfile) fprintf(stream,"-- IDLE  MODE ----\n");
#endif
      hptrMSE=hptrSYS;       //use system pointer for mouse moves
      boxText=0;             //no text
      break;
   } /* endswitch */
   ptargetarea=0;         //cancel a possible painted target icon
   WinSetPointer(HWND_DESKTOP,hptrMSE);
}
//-----------------------------------------------------------------------------
// Initialize variables used in toState procdure
//-----------------------------------------------------------------------------
void initMode(void)
{
   hptrMSE=hptrSYS;
   ptargetarea = 0;
   areaChangePending=FALSE;
   nocursorValid = FALSE;
}
//-----------------------------------------------------------------------------
// update the nocursor area if the locator has moved out of the old
// nocursor area. If there currently isn't a nocursor area, then just
// set it to the current targetarea.
//-----------------------------------------------------------------------------
void updateNocursorArea(POINTL * pptl)
{
   if (nocursorValid) {
      if (!(landingArea(pptl,&nocursorarea))) {
         updatearea();
      } /* endif */
   } else {
      updatearea();
   } /* endif */
}
//-----------------------------------------------------------------------------
// update the nocursor area if their is a new area to update to
// nocursor area
//-----------------------------------------------------------------------------
void updatearea(void)
{
   if (ptargetarea) {
      nocursorarea=*ptargetarea;
      nocursorValid=TRUE;
   } else {
      nocursorValid = FALSE;
   } /* endif */
   areaChangePending=FALSE;
}
//***************************************************************************** */
//   E X T E R N A L   R O U T I N E S
//*****************************************************************************

//-----------------------------------------------------------------------------
// called by alignment thread to control window proc
//-----------------------------------------------------------------------------
void notifyPM (LONG notifyCmd,TARGETAREA * ptargetarea)
{
   BOOL pmrc;
   MPARAM mp1,mp2;

   pmrc=WinPostMsg(hwndAdjust,notifyCmd,mp1,ptargetarea);

}

//-----------------------------------------------------------------------------
// called to register the window class
//-----------------------------------------------------------------------------
void registerAlign(void)
{
   WinRegisterClass(habMain,adjName,(PFNWP)AdjustWndProc,
                         CS_SIZEREDRAW+CS_CLIPCHILDREN,0);
}

//-----------------------------------------------------------------------------
// called to create the window
//-----------------------------------------------------------------------------
LONG createAlign (HWND owner)
{
   HWND hwndclient;
   ULONG createflags=FCF_ICON|FCF_TASKLIST;
   CHAR  szFile[CCHMAXPATH];
   APIRET rc;
   LONG i,l;
   LONG strSize;

// load strings

   strcpy(szFile,PENPMMRI);
   rc=DosLoadModule(szFile,CCHMAXPATH,szFile,&hmodMRI);
#ifdef DEBUG
   if (rc) {
      if (dbfile) fprintf(stream,"DosLoadModule for %s rc=%d\n",szFile,rc);
   } else {
#else
   if ( !rc ) {
#endif

      for (i=0,l=0;i<3;i++) {   //glue all CALTEXT messages together
         strSize=WinLoadString(habMain,hmodMRI,IDS_CALTEXT+i,MAX_LINE_SIZE,&calText[l]);
#ifdef DEBUG
         //if (dbfile) fprintf(stream,"%d %s\n",strSize,&calText[l]);
#endif
         if (maxLineSize < strSize) {
            maxLineSize = strSize;
         } /* endif */
         l+=strSize;
         calText[l++]='\n';
      } /* endfor */
      calText[--l]='\0';

      for (i=0,l=0;i<5;i++) {   //glue all TESTEXT messages together
         strSize=WinLoadString(habMain,hmodMRI,IDS_TESTEXT+i,MAX_LINE_SIZE,&tesText[l]);
#ifdef DEBUG
         //if (dbfile) fprintf(stream,"%d %s\n",strSize,&tesText[l]);
#endif
         if (maxLineSize < strSize) {
            maxLineSize = strSize;
         } /* endif */
         l+=strSize;
         tesText[l++]='\n';
      } /* endfor */
      tesText[--l]='\0';

      WinLoadString (habMain,hmodMRI,IDS_DEFTEXT,MAX_LINE_SIZE,deftext);
      WinLoadString (habMain,hmodMRI,IDS_DEFTITLE,MAX_LINE_SIZE,deftitle);
      WinLoadString (habMain,hmodMRI,IDS_ERR1,MAX_LINE_SIZE,DDerrText);
      WinLoadString (habMain,hmodMRI,IDS_OK,MAX_BUT_SIZE,szButtonLabel[0]);
      WinLoadString (habMain,hmodMRI,IDS_ALIGN,MAX_BUT_SIZE,szButtonLabel[1]);
      WinLoadString (habMain,hmodMRI,IDS_DEFAULTS,MAX_BUT_SIZE,szButtonLabel[2]);
      WinLoadString (habMain,hmodMRI,IDS_TEST,MAX_BUT_SIZE,szButtonLabel[3]);
      WinLoadString (habMain,hmodMRI,IDS_CANCEL,MAX_BUT_SIZE,szButtonLabel[4]);
      WinLoadString (habMain,hmodMRI,IDS_HELP,MAX_BUT_SIZE,szButtonLabel[5]);
   } /* endif */

// do PM stuff

   hwndAdjust=WinCreateStdWindow(
      HWND_DESKTOP,        //parent
      FS_TASKLIST,         //frame window style
      &createflags,        // frame creation flags
      adjName,             //class
      NULL,                //text
      0L,                  //style
      NULLHANDLE,          //resource
      ID_APP,              //resource ID
      &hwndclient);        //client hwnd

   WinSetWindowPos(
      hwndAdjust,          //window handle
      HWND_TOP,            //sibling
      0,0,                 //position
      scrWidth,scrHeight,  //size
      SWP_SIZE|SWP_ZORDER|SWP_SHOW|SWP_ACTIVATE); //options


   return 0;
}

//-----------------------------------------------------------------------------
// called to destroy the window
//-----------------------------------------------------------------------------
void destroyAlign(void)
{
   WinDestroyWindow(hwndAdjust);
}
