/*  OpenChess.c -- PalmOS  display interface for GNU Chess2
 *
 *  OpenChess version 1.0, is a CHESS game for Palm Pilot, adapted 
 *  from GNU Chess 2, for Unix/DOS environments
 *  Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc.
 *  Copyright (C) 2002, Son Altesse.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <PalmOS.h>

#ifdef SONYCLIE             // Include Sony CLIE SDK
    #include "SonyClie.h"
#endif

#include "OpenChess.h"
#include "OpenChess_res.h"

#define     sysFileCOpenChess     'OpCh'  // Creator ID for OpenChess
 
#define     OpenChessDBName       "OpenChessDB"
#define     OpenChessDBType       'Data'
#define     VERSION_NUM           10
#define     LAST_GAME             0
#define			NoLevel								-1

// Parameters for the board and pieces bitmap
#ifndef SONYCLIE
  #define     offSqWhite            86                    // X offset of the Wh square
  #define     szSq                  18                    // Board square size
  #define     offSqBlack            offSqWhite    // X offset of the Bl square
  #define     offSqMark             offSqWhite + szSq     // X Mark offset
  #define     offSqMask             offSqMark + szSq      // X Mark mask offset
  #define     offPawn               0                     // X pawn
  #define     wdPawn                13                    // Width of pieces
  #define     wdKnight              15
  #define     wdBishop              15
  #define     wdRook                13
  #define     wdQueen               15
  #define     wdKing                15
  #define     PieceHeight           15
  #define     TitleHeight           13
  #define			XOffPrgBkg						86
  #define			YOffPrgBkg						2 * szSq
	#define 		XOffPrgMtr						XOffPrgBkg + szSq
  #define 		PrgHight							10
  #define			Xprg									7 * szSq 
  #define			Yprg									8 * szSq + 3 
#else           // For SONY CLIE
  #define     offSqWhite            180                   // X offset of the Wh square
  #define     szSq                  36                    // Board square size
  #define     offSqBlack            offSqWhite    // X offset of the Bl square
  #define     offSqMark             offSqWhite + szSq     // X Mark offset
  #define     offSqMask             offSqMark + szSq      // X Mark mask offset
  #define     offPawn               0                     // X pawn
  #define     wdPawn                30                    // Width of pieces
  #define     wdKnight              30
  #define     wdBishop              30
  #define     wdRook                30
  #define     wdQueen               30
  #define     wdKing                30
  #define     PieceHeight           30
  #define     TitleHeight           26
  #define			XOffPrgBkg						180
  #define			YOffPrgBkg						2 * szSq
	#define 		XOffPrgMtr						XOffPrgBkg + szSq
  #define 		PrgHight							20
  #define			Xprg									7 * szSq 
  #define			Yprg									8 * szSq + 3 
#endif

#define     NoSqSelected          -1
#define     X_MOVE                2							// X of move text 
#define     X_TEXT                30						// X of message
#define     Y_TEXT                146						// Y of move text and message
#define     EX_TEXT               94	//Xprg - X_TEXT - 2	// Width of message 
#define     EY_TEXT               160 - Y_TEXT	// height of message
#define			HBMPEDIT							80						// Height of Edit buttons bitmap
#define 		SoundLevel						64

#define     BLACKMATES            "Black mates!"
#define     WHITEMATES            "White mates!"
#define     SOONWHITEMATES        "White soon mates!"
#define     SOONBLACKMATES        "Black soon mates!"
#define     WHITECHECKS           "White checks!"
#define     BLACKCHECKS           "Black checks!"
#define			NOHINT								"No time to think"

// Macros
#define     WAIT_UNTIL_NO_PEN_EVENT   {\
            Int16   ScreenX, ScreenY; \
            do EvtGetPen(&ScreenX, &ScreenY, &PenDown); while (PenDown); } 
    
// Alert messages
#define     MES_NO_GAME_LOAD        "Cannot load previous game"
#define     ERRORSONY								"Cannot enter Sony HiRes mode...\n\nLeaving !"

// Program globals 
DmOpenRef               OpenChessDB;
WinHandle               DispWindowH, OffscreenWinH;
UInt16                  PiecesWidth[] =
    {0, wdPawn, wdKnight, wdBishop, wdRook, wdQueen, wdKing}; 
UInt16                  PiecesOffset[] = 
    {0, 
     offPawn,  
     offPawn + wdPawn,
     offPawn + wdPawn + wdKnight,
     offPawn + wdPawn + wdKnight + wdBishop,
     offPawn + wdPawn + wdKnight + wdBishop + wdRook,
     offPawn + wdPawn + wdKnight + wdBishop + wdRook + wdQueen
     };  
Boolean                 IsNewGame = false;
Boolean                 PenDown, abortedMove, playingMode;
UInt16                  *BookData, OpenCnt;
MemHandle               BookResH;
struct BookEntry        *BookPtr;
UInt16                  HRrefNum;             // For Sony Clie

struct TsqSel {
  Int16   start;
  Int16   end;
} sqSel;
     
// Program prototypes 
Boolean frmMain_HandleEvent(EventPtr event);
Boolean frmAbout_HandleEvent(EventPtr event);
Boolean frmEditBrd_HandleEvent(EventPtr event);
Boolean frmPrefs_HandleEvent(EventPtr event);
Boolean frmPieces_HandleEvent(EventPtr event);
static Boolean ApplicationHandleEvent(EventPtr event);

// Structures
struct HeaderGameList {
  short computer;
  short opponent;
  short Game50;
  short castld[2];
  short kingmoved[2];
  short TCflag;
  short OperatorTime;
  struct TimeControlRec  TimeControl;
  short board[64];
  short GameCnt;
  };

struct TOpenChessPrefs {
  UInt16             level;
  Boolean            beep;
  UInt16             opponent;
  UInt16             computer;
  UInt16             player;
  UInt16             dither;
  UInt16             contempt;
  Boolean            reverse;
  Boolean            bothsides;
  Boolean            force;     // Used for Human <-> Human games
  Boolean						 mate;
  struct TsqSel      sqSel;
  };
  
typedef struct {
	UInt16 						nNotes;
  UInt16						param123[15][2];
	} Tsound;
              
Tsound opponentMvSnd = {1, {{440, 40}}}; 
Tsound computerMvSnd = {1, {{660, 40}}}; 
Tsound opponentTkSnd = {2, {{440, 80}, {527, 40}}}; 
Tsound computerTkSnd = {2, {{660, 80}, {527, 40}}}; 
Tsound chessSnd = {7, {{527, 200}, {527, 200}, {527, 200}, {702, 200},
											 {0, 100}, {527, 200}, {702, 400}}}; 
Tsound mateSnd = {15, {{527, 400}, 	// Mozart's funeral march
											 {0, 100},
                       {527, 400}, 
                       {527, 200},
                       {527, 200}, 
                       {0, 400}, 
                       {626,400},
                       {591, 200},
                       {591, 200},
                       {0, 100},
                       {527, 200},
                       {527, 200},
                       {0, 100},
                       {470, 200},
                       {527, 200}}};

  
/****************************************************************************/
void * GetCtrlPtr(const FormType *formP, UInt16 objIndex) 
{
  return FrmGetObjectPtr(formP, FrmGetObjectIndex (formP, objIndex));
}

/****************************************************************************/

void SetDBBackupBit( DmOpenRef dbP )
{
LocalID     dbID;
UInt16      cardNo;
UInt16      attributes;
UInt16      versionP;

DmOpenDatabaseInfo( dbP, &dbID, NULL, NULL, &cardNo, NULL );
DmDatabaseInfo( cardNo, dbID, NULL, &attributes, &versionP, NULL,
                NULL, NULL, NULL, NULL, NULL, NULL, NULL);
versionP = VERSION_NUM;
attributes |= dmHdrAttrBackup;
DmSetDatabaseInfo( cardNo, dbID, NULL, &attributes, &versionP, NULL,
                  NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    
}

/****************************************************************************/

Err OpenChessGetDatabase ( DmOpenRef *dbPP, UInt16 mode )
{
Err             error = false;
DmOpenRef       dbP;

*dbPP = NULL;
dbP = DmOpenDatabaseByTypeCreator (OpenChessDBType, sysFileCOpenChess, mode);
if (!dbP) {
    error = DmCreateDatabase ( 0, OpenChessDBName, sysFileCOpenChess, OpenChessDBType, false );
    if (error) return error;
    dbP = DmOpenDatabaseByTypeCreator( OpenChessDBType, sysFileCOpenChess, mode );
    if (!dbP) return ( true );
    SetDBBackupBit( dbP ); // Set the backup bit to enable hotsyncs 
    }
*dbPP = dbP;
return false;
}

/****************************************************************************/
void GetGame(MemPtr  GamePtr)   // Adapted Form uxdsp.c     
{
short                   sq;
unsigned short          m;
struct HeaderGameList   hdr;
short                   i;
MemPtr                  p;
  
  MemMove(&hdr, GamePtr, sizeof(hdr)); 
  computer = hdr.computer; opponent = hdr.opponent; Game50 = hdr.Game50;
  castld[white] = hdr.castld[white]; castld[black] = hdr.castld[black];
  kingmoved[white] = hdr.kingmoved[white]; kingmoved[black] = hdr.kingmoved[black];
  TCflag = hdr.TCflag; OperatorTime = hdr.OperatorTime;
  TimeControl = hdr.TimeControl;
  for (sq = 0; sq < 64; sq++) {
       m = hdr.board[sq];
       board[sq] = (m >> 8); color[sq] = (m & 0xFF);
       }
  GameCnt = hdr.GameCnt;    
  p = GamePtr + sizeof(hdr); 
  for (i = 0; i <= GameCnt; i++, p += sizeof(struct GameRec)) 
      MemMove(&GameList[i], p, sizeof(struct GameRec));
  if (TimeControl.clock[white] > 0) TCflag = true;
  computer--; opponent--;
  InitializeStats();
  Sdepth = 0;
}

/****************************************************************************/
void SaveGame(UInt16 index)   // Adapted Form uxdsp.c
{
short         sq, i;
MemHandle     RecHandle;
MemPtr        RecPtr;
struct HeaderGameList   hdr;
UInt16        RecSz;

  RecHandle = DmGetRecord(OpenChessDB, index);
  RecPtr = MemHandleLock(RecHandle);
  hdr.computer = computer + 1; hdr.opponent = opponent + 1; hdr.Game50 = Game50;
  hdr.castld[white] = castld[white]; hdr.castld[black] = castld[black];
  hdr.kingmoved[white] = kingmoved[white]; hdr.kingmoved[black] = kingmoved[black];
  hdr.TCflag = TCflag; hdr.OperatorTime = OperatorTime;
  hdr.TimeControl = TimeControl;
  for (sq = 0; sq < 64; sq++) hdr.board[sq] = 256 * board[sq] + color[sq];
  hdr.GameCnt = GameCnt;    
  DmWrite(RecPtr, 0, &hdr, sizeof(hdr));
  RecSz = sizeof(hdr);
  for (i = 0; i <= GameCnt; i++, RecSz += sizeof(struct GameRec)) 
      DmWrite(RecPtr, RecSz, &GameList[i], sizeof(struct GameRec));
  MemHandleUnlock(RecHandle);
}     

/****************************************************************************/
void ReadGameFromDB(UInt16 index)
{
MemHandle   RecHand;
MemPtr      RecPtr;

  RecHand = DmQueryRecord(OpenChessDB, index);    // # of Game to load
  RecPtr = MemHandleLock(RecHand);
  if (RecPtr == 0) FrmCustomAlert(AlertError, MES_NO_GAME_LOAD, " ", " ");
  else GetGame(RecPtr);   // Process the record as in GNUchess's uxdsp.c GetGame
  MemHandleUnlock(RecHand);
}

/****************************************************************************/
void SaveGameToDB(UInt16 index)
{
  if (DmNumRecords(OpenChessDB) > 0) DmRemoveRecord (OpenChessDB, index);
  DmNewRecord(OpenChessDB, &index, 
    sizeof(struct HeaderGameList) + (GameCnt + 1) * sizeof(struct GameRec));    
  DmReleaseRecord(OpenChessDB, index, true); 
  SaveGame(index);
}

/****************************************************************************/
void LoadPrefs(void)
{
UInt16  PrefsSz = 0;
Int16   versionN;
struct TOpenChessPrefs  prefs;

  versionN = PrefGetAppPreferences(sysFileCOpenChess, 0, NULL, &PrefsSz, false);
  if ((versionN == VERSION_NUM) && (PrefsSz == sizeof(prefs)))  {
    PrefGetAppPreferences(sysFileCOpenChess, 0, &prefs, &PrefsSz, false);
    dither = prefs.dither;
    contempt = prefs.contempt;
    opponent = prefs.opponent;
    computer = prefs.computer;
    player = prefs.player;
    beep = prefs.beep;
    reverse = prefs.reverse;
    bothsides = prefs.bothsides;
    Level = prefs.level;
    force = prefs.force;
    mate = prefs.mate;
    sqSel = prefs.sqSel;
    }
}

/****************************************************************************/
void SavePrefs(void)
{
struct TOpenChessPrefs  prefs;

  prefs.dither = dither;
  prefs.contempt = contempt;
  prefs.opponent = opponent;
  prefs.computer = computer;
  prefs.player = player;
  prefs.beep = beep;
  prefs.reverse = reverse;
  prefs.bothsides = bothsides;
  prefs.force = force;
  prefs.level = Level;
  prefs.mate = mate;
  prefs.sqSel = sqSel;
  PrefSetAppPreferences(sysFileCOpenChess, 0, VERSION_NUM, 
                        &prefs, sizeof(prefs), false);
}

/****************************************************************************/
void SelectLevel(void)
{
  FrmGotoForm(frmPrefs);
}

#ifdef SONYCLIE
/****************************************************************************/
Err SonyClieHRmode(void)	// Most of code here comes from Sony's dev kit...
{
SonySysFtrSysInfoP sonySysFtrSysInfoP;
UInt32          width, height;

// Check for a Sony Clié device
Err error = 0;

if ((error = FtrGet(sonySysFtrCreator, sonySysFtrNumSysInfoP, (UInt32*)&sonySysFtrSysInfoP))) {
    // In this example the device is checked whether it's the CLIÉ™ ,however there is no guarantee that the CLIÉ™
    // has long been the only device that is accessible to the High-resolution.
    /* Not CLIE: maybe not available */
    error = true;
    }
else {
    if (sonySysFtrSysInfoP->libr & sonySysFtrSysInfoLibrHR) { /* HR available */
        if ((error = SysLibFind(sonySysLibNameHR, &HRrefNum))){
            if (error == sysErrLibNotFound) { /* couldn't find lib */
                error = SysLibLoad( 'libr', sonySysFileCHRLib, &HRrefNum );
                }
            }
        if (!error ) {      /* Now we can use HR lib. Executes Open library. */
            error = HROpen(HRrefNum);
            if (!error) {        /* Not error processing */
                width = hrWidth; height = hrHeight;
                error = HRWinScreenMode ( HRrefNum, winScreenModeSet, &width, &height, NULL, NULL );
                /* high-resolution mode entered from here if no error*/
                }
            }
        }
    }
return error;
}
#endif

/****************************************************************************/
Err SetColors(void)
{
UInt32          width, height, depth, romVersion;
Boolean         color;
Err 						err = false;

  FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
  if (romVersion >= 0x03500000) {     // OS > 3.5
    WinScreenMode(winScreenModeGet, &width, &height, &depth, &color);
    if (!color && (depth && 8)) {
      depth = 4;
      WinScreenMode(winScreenModeSet , NULL, NULL, &depth, NULL);
      }
    #ifdef SONYCLIE
    {
    SonySysFtrSysInfoP sonySysFtrSysInfoP;

    err = FtrGet(sonySysFtrCreator, sonySysFtrNumSysInfoP, (UInt32*)&sonySysFtrSysInfoP);
    if (!err)
	    if (sonySysFtrSysInfoP->libr & sonySysFtrSysInfoLibrHR)         // HR ?
      err = SonyClieHRmode(); 
			if (err) FrmCustomAlert(AlertError, ERRORSONY, " ", " ");
    }
    #endif
    }   // OS is 3.5
    return err;
} 

/****************************************************************************/
void GetOpenings(void)          // Adapted from uxdsp.c, GetOpenings
{
Int16               i, *p;
struct BookEntry    *b, *b1;

  // Set up the book structures, using the OpenBook resource data
  b = Book = BookPtr;
  p = BookData; p++;    
  for (i = 0; i < OpenCnt; i++) {
    b->mv = p;
    b1 = b; b1++;       // Oops ! This Stuff needed when gcc optimization is on 
    b->next = b1; b++;
    while (*(p++) != 0);        // Seek next opening 
    }
  b--;  b->next = NULL;     // Last one.
}

/****************************************************************************/
static int StartApplication(void)
{
Err         err;
MemHandle   resH;
BitmapPtr   resP;
    
  err = OpenChessGetDatabase(&OpenChessDB, dmModeReadWrite);    // Open the OpenChessDB database
  if (err) return err;

  err = SetColors();      // Set the color mode
  if (err) return err;
  
  BookResH = DmGetResource('Data', OpenBookRes);    // open Opening Book
  BookData = (UInt16 *)MemHandleLock(BookResH);
  OpenCnt = *BookData;
  BookPtr = (struct BookEntry *)MemPtrNew(OpenCnt * sizeof(struct BookEntry));
  
  sqSel.start = sqSel.end = NoSqSelected; 
  OpenChessMain();  // General GnuChess init
	Level = NoLevel;
  if (DmNumRecords(OpenChessDB) > 0) ReadGameFromDB(LAST_GAME);   // If not first time, read last game
  LoadPrefs();        // Read the preferences

  // Create offscreen window, and load pieces bitmap
  resH = DmGetResource(bitmapRsc, BmpPieces);
  resP = MemHandleLock(resH);
  OffscreenWinH = WinCreateOffscreenWindow(resP->width, resP->height, screenFormat, &err);
  if (OffscreenWinH) {
    DispWindowH = WinSetDrawWindow(OffscreenWinH);
    WinDrawBitmap (resP, 0, 0);
    MemPtrUnlock(resP);
    DmReleaseResource(resH);
    }
  WinSetDrawWindow(DispWindowH);          // Restore the current draw window

  if (Level == NoLevel) SelectLevel();    // First level selected here, not in NewGame() as GnuChess
  else FrmGotoForm(frmMain);        // Go to start screen
  return 0;
}

/****************************************************************************/
void ShowMessageXY(char *s, Coord x, Coord y, Coord ex, Coord ey) 
{
RectangleType   rect;

  rect.topLeft.x = x;
  rect.topLeft.y = y;
  rect.extent.x = ex; 
  rect.extent.y = ey;
  FntSetFont(boldFont);
  WinEraseRectangle (&rect, 0);           // Erase text area
  WinDrawChars(s, StrLen(s), x, y);
}

/****************************************************************************/
void ShowMessage(char *s)       // Adapted from uxdsp.c 
{
    ShowMessageXY(s, X_TEXT, Y_TEXT, EX_TEXT, EY_TEXT);
}

/****************************************************************************/
void GiveHint()   // Adapted from uxdsp.c, with bug correction in reverse mode
{
char s[40];
Int16   f, t;

if (hint > 0) {		// Added, as there's no hint on the first move
	f = (short)(hint>>8); t = (short)(hint & 0xFF);
  if (reverse) {	// Added, to correct GNU Chess bug when in reverse mode
  	f = 63 - f; t = 63 - t;
    }
  algbr(f,t,false);
  StrCopy(s,"try: ");
  StrCat(s,mvstr1);
  ShowMessage(s);
  }
  else ShowMessage(NOHINT);
}

/****************************************************************************/
void Copy2screen(Coord xs, Coord ys, Coord ex, Coord ey, 
                 Coord xd, Coord yd, WinDrawOperation mode)
{
RectangleType   rect;

  rect.topLeft.x = xs;
  rect.topLeft.y = ys;
  rect.extent.x = ex; 
  rect.extent.y = ey;
  #ifndef SONYCLIE
    WinCopyRectangle(OffscreenWinH, DispWindowH, &rect, xd, yd, mode);
  #else
    HRWinCopyRectangle(HRrefNum, OffscreenWinH, DispWindowH, &rect, xd, yd, mode);
  #endif
}
  
/****************************************************************************/
void DrawPiece(Int16 sq)
{
short r,c, offset;
Coord x, y;

  if (sq < 0) return;
  
  if (reverse) r = 7-row[sq]; else r = row[sq];
  if (reverse) c = 7-column[sq]; else c = column[sq];

  r = 7-r;
  
  offset = ((c + r) % 2) ? szSq : 0;      // Draw the board square
  x = c * szSq; y = r * szSq;
  Copy2screen(offSqWhite, offset, szSq, szSq, x, y, winPaint);
  
  if (sqSel.start == sq) {    // Display the start selection mark
    Copy2screen(offSqMask, 0, szSq, szSq, x, y, winMask);
    Copy2screen(offSqMark, szSq, szSq, szSq, x, y, winOverlay);
    }
        
  if (sqSel.end == sq) {    // Display the end selection mark
    Copy2screen(offSqMask, 0, szSq, szSq, x, y, winMask);
    Copy2screen(offSqMark, 0, szSq, szSq, x, y, winOverlay);
    }
        
  if (board[sq] > 0) {    // Draw the piece
    x = c * szSq + (szSq - PiecesWidth[board[sq]]) / 2;
    y = r * szSq + (szSq - PieceHeight) / 2;
    Copy2screen(PiecesOffset[board[sq]], PieceHeight * 2, 
                PiecesWidth[board[sq]], PieceHeight, x, y, winMask);
    Copy2screen(PiecesOffset[board[sq]], (color[sq] == white) ? PieceHeight : 0, 
                PiecesWidth[board[sq]], PieceHeight, x, y, winOverlay);
    }
}

/****************************************************************************/
void UpdateDisplay(short f, short t, short flag, short iscastle)    // adapted from uxdsp.c
{
UInt16    i;

  if (flag) for (i = 0; i < 64; i++) {
    DrawPiece(i);
    }
  else { 
    DrawPiece(f); DrawPiece(t);
    if (iscastle) { 
      if (t > f)  { DrawPiece(f + 3); DrawPiece(t - 1); }
      else        { DrawPiece(f - 4); DrawPiece(t + 1); }
      }
    }
}

/****************************************************************************/
Boolean getsq(Int16 ScreenX, Int16 ScreenY, UInt16 *sq)
{
#ifdef SONYCLIE
	ScreenX *= 2; ScreenY *= 2;
#endif
  if ((ScreenX < 8 * szSq) && (ScreenY < 8 * szSq)) {
    *sq = 8 * (7 - (ScreenY / szSq)) + (ScreenX / szSq);
    if (reverse) *sq = 63 - *sq;
    return true;
    }
  else return false;
}

/****************************************************************************/
void UnselectPieces(void)
{
Int16   f, t;

  f = sqSel.start; t = sqSel.end;
  sqSel.start = sqSel.end = NoSqSelected;
  DrawPiece(f); DrawPiece(t);
}
 
/****************************************************************************/
void SwitchSides(void)      // Adapted from uxdsp.c
{
  computer = otherside[computer];
  opponent = otherside[opponent];
  force = false;
  Sdepth = 0;
}

/****************************************************************************/
void ElapsedTime(short iop)       // adapted from uxdsp.c
/*
   Determine the time that has passed since the search was started. If
   the elapsed time exceeds the target (ResponseTime+ExtraTime) then set
   timeout to true which will terminate the search.
*/
{
Int16     ScreenX, ScreenY, mtr;
Int32			RplEtime;

  et = TimGetTicks()- time0;
  if (et < 0) et = 0;
  ETnodes += 50;
  if (et > et0 || iop == 1)
    {	
    	RplEtime = ResponseTime+ExtraTime;
      if (et > RplEtime && Sdepth > 1) timeout = true;
      et0 = et;
      // Added : display the progress meter
      mtr = (RplEtime == 0) ? szSq : (szSq * et) / RplEtime;
      if (mtr > szSq) mtr = szSq; 
	    Copy2screen(XOffPrgMtr+1, YOffPrgBkg+1, mtr, PrgHight-2, Xprg+1, Yprg+1, winPaint);
		  // End added	
      if (iop == 1)
        {
          time0 = TimGetTicks() /*time((long *)0)*/ ; et0 = 0;
          // Added : display the progress Background
			    Copy2screen(XOffPrgBkg, YOffPrgBkg, szSq, PrgHight, Xprg, Yprg, winPaint);
				  // End added	
        }
      ETnodes = NodeCnt + 50;
    }
  // Added: check for user pen or kbd input. In this case, aborts current computer move.
  EvtResetAutoOffTimer();		// Reset auto off timer during computer move
  EvtGetPen(&ScreenX, &ScreenY, &PenDown);
  if ((PenDown || !EvtKeyQueueEmpty()) && (Sdepth > 1)) {
    timeout = true;             // Abort move in case on PenDown or key pressed
    abortedMove = true;         // signifies an aborted move due to user evt
    } 
}

/****************************************************************************/
void SetTimeControl(void)   // Adapted from uxdsp.c
{
  if (TCflag) 
    {
      TimeControl.moves[white] = TimeControl.moves[black] = TCmoves;
      TimeControl.clock[white] = TimeControl.clock[black] = 60*(long)TCminutes;
    }
  else
    {
      TimeControl.moves[white] = TimeControl.moves[black] = 0;
      TimeControl.clock[white] = TimeControl.clock[black] = 0;
    }
  et = 0;
  ElapsedTime(1);
}

/****************************************************************************/
void PlaySound(Int16 side, Boolean take)
{
UInt16 	i;
Tsound 	sound;
SndCommandType	cmd = {sndCmdFreqDurationAmp, 0, 0, 0 ,0};

	if (!beep) return;
	if (side == white) sound = take ? opponentTkSnd : opponentMvSnd;
	if (side == black) sound = take ? computerTkSnd : computerMvSnd;
	if (SqAtakd(PieceList[black][0],white) ||		  // Chess attack 
  		SqAtakd(PieceList[white][0],black)) sound = chessSnd; 
  if (mate) sound = mateSnd;		// Mate sound
        
	for (i = 0; i < sound.nNotes; i++) { 
  	cmd.param1 = (Int32)sound.param123[i][0];
  	cmd.param2 = sound.param123[i][1];
  	cmd.param3 = SoundLevel;
    if (cmd.param1 == 0) 			// Wait, no sound
    		SysTaskDelay((SysTicksPerSecond() * cmd.param2) / 1000);		
  	else SndDoCmd (NULL, &cmd, 0);
		SysTaskDelay((SysTicksPerSecond() * 30) / 1000);		
    }
}

/****************************************************************************/
void CheckAtak(void)
{
if (SqAtakd(PieceList[black][0],white)) ShowMessage(WHITECHECKS);
if (SqAtakd(PieceList[white][0],black)) ShowMessage(BLACKCHECKS);
}

/****************************************************************************/
void OutputMove(void)   // Adapted from uxdsp.c
{
Char * matesStr[] = {WHITEMATES, BLACKMATES};
Char * soonMateStr[] = {SOONWHITEMATES, SOONBLACKMATES};

  if (abortedMove) return;  // Added, do not draw anything if the move is
                           // interrupted by a user input 
  
  if (root->flags & epmask) UpdateDisplay(0,0,1,0);
  else {
    UnselectPieces();
    sqSel.start = root->f; sqSel.end = root->t;
    UpdateDisplay(root->f,root->t,0,root->flags & cstlmask);
    }   
//  gotoXY(50,17); printz("My move is: %s",mvstr1);
  ShowMessageXY(mvstr1, X_MOVE, Y_TEXT, X_TEXT + EX_TEXT, EY_TEXT);

//  if (beep) putchar(7);
//  ClrEoln();

//  gotoXY(50,24);
// ADDED for Chess attack
  CheckAtak();
// END ADDED
  if (root->flags & draw) ShowMessage("Draw game!");
  else if (root->score == -9999) ShowMessage(matesStr[otherside[player]]);
  else if (root->score == 9998) ShowMessage(matesStr[player]);
  else if (root->score < -9000) ShowMessage(soonMateStr[otherside[player]]);
  else if (root->score > 9000)  ShowMessage(soonMateStr[player]);

/*  if (post && player == computer)
    {
      gotoXY(50,22); printz("Nodes=   %6ld",NodeCnt); ClrEoln();
      gotoXY(50,23); printz("Nodes/Sec= %4ld",evrate); ClrEoln();
    }
    */
}

/****************************************************************************/
void Undo(void)     // Copied from uxdsp.c, Undo()
/*
   Undo the most recent half-move.
*/
{
short f,t;

  if (GameCnt < 0) return;    // Added here, although originately in 
                              // uxdsp.c, InputCommand()
  f = GameList[GameCnt].gmove>>8;
  t = GameList[GameCnt].gmove & 0xFF;
// ADDED : Selection square display
	sqSel.start = f; sqSel.end = t;
// END ADDED
  if (board[t] == king && distance(t,f) > 1)
    castle(GameList[GameCnt].color,f,t,2);
  else
    {
      board[f] = board[t]; color[f] = color[t];
// ADDED : Selected square display
			sqSel.start = f; sqSel.end = t;
// END ADDED
      board[t] = GameList[GameCnt].piece;
// ADDED for Pawn promotion undo bug in GNU Chess !
			if (GameList[GameCnt].pawnpromote == promote) board[f] = pawn;
// END ADDED      
      color[t] = GameList[GameCnt].color;
      if (board[f] == king) --kingmoved[color[f]];
    }
  if (TCflag) ++TimeControl.moves[color[f]];
  GameCnt--; mate = false; Sdepth = 0;
  UpdateDisplay(0,0,1,0);
  InitializeStats();
}


/****************************************************************************/
void ComputerPlay(short side)
{
  if ((DispWindowH == WinGetDrawWindow()) && playingMode) {
    WAIT_UNTIL_NO_PEN_EVENT             
    ElapsedTime(1);
    SelectMove(side,1);  // Gnuchess.c, main()
    if (abortedMove) {  // Restart search
      Undo();
      abortedMove = false;
      player = side;
      }
    else PlaySound(side, (root->flags & capture));
    }
}

/****************************************************************************/
static Boolean ApplicationHandleEvent(EventPtr event)
{
UInt16 formID;
FormPtr form;
Boolean handled = false;
  
  switch (event->eType)  {          // Application event loop
    case frmLoadEvent:             // Handle form load events
        formID = event->data.frmLoad.formID;
        form = FrmInitForm(formID);
        FrmSetActiveForm(form);
        playingMode = false;     
  
        switch (formID) {
          case frmPrefs:              // Set event handler for frmEditBrd
            FrmSetEventHandler(form, (FormEventHandlerPtr) frmPrefs_HandleEvent);
            break;
          case frmEditBrd:            // Set event handler for frmEditBrd
            FrmSetEventHandler(form, (FormEventHandlerPtr) frmEditBrd_HandleEvent);
            break;
          case frmAbout:            // Set event handler for frmAbout
            FrmSetEventHandler(form, (FormEventHandlerPtr) frmAbout_HandleEvent);
            break;
          case frmPieces:            // Set event handler for frmPieces
            FrmSetEventHandler(form, (FormEventHandlerPtr) frmPieces_HandleEvent);
            break;
          case frmMain:            // Set event handler for frmMain
            FrmSetEventHandler(form, (FormEventHandlerPtr) frmMain_HandleEvent);
            playingMode = true;
            break;
        }
        handled = true;
        break;
  
    default:
      break;
    }

  return handled;
}

/****************************************************************************/
Boolean DoEvents(void)      // yeah, even better than in VB...
{
UInt16 error;
EventType event;

   do { 
    if ((bothsides || (player == computer)) && !mate)
      EvtGetEvent(&event, SysTicksPerSecond() / 4); // 250 ms
    else
      EvtGetEvent(&event, evtWaitForever);
    if (!SysHandleEvent(&event))  {            
      if (!MenuHandleEvent(0, &event, &error)) {
        if (!ApplicationHandleEvent(&event)) 
          FrmDispatchEvent(&event);
        }   // if (!MenuHandleEvent(...
      }     // if (!SysHandleEvent(...
    }       // while (EvtEventAvail...
  while (EvtEventAvail() || EvtSysEventAvail(false));
  return(event.eType == appStopEvent);
}

/****************************************************************************/
static void EventLoop(void)     // does almost the same as in gnuchess.c, main()
{
  while (!DoEvents()) {
    if ((player == opponent) && bothsides && !mate ) ComputerPlay(opponent);
    if (DoEvents()) return;
    if ((player == computer) && !(quit || mate || force)) ComputerPlay(computer);
    }
}

/****************************************************************************/
static void StopApplication(void)
{
  MemPtrUnlock(BookData);               // Close opening Book
  DmReleaseResource(BookResH);

  WinDeleteWindow(OffscreenWinH, false);  // Delete offscreenwindow
  SavePrefs();    // Save the preferences and last game
  SaveGameToDB(LAST_GAME);
  DmCloseDatabase (OpenChessDB);
  MemPtrFree(BookPtr);
  MemPtrFree(ttable);
  MemPtrFree(history);
  FrmCloseAllForms();
}

/****************************************************************************/
UInt32 PilotMain(UInt16 cmd, void *cmdPBP, UInt16 launchFlags)
{
int error;
   
  if (cmd == sysAppLaunchCmdNormalLaunch) {
    error = StartApplication();      
    if (error) return error;
    EventLoop();    
    StopApplication();       
    }
    return 0;
}

/****************************************************************************/
Boolean frmMain_HandleEvent(EventPtr event)
{
FormPtr form;
Boolean ok, isCastle, handled = false;
UInt16  sq, mv, nPiece;
Char    s[5];

  switch (event->eType)
  {
    case frmOpenEvent:
      form = FrmGetActiveForm();
      FrmDrawForm(form);
      DispWindowH = WinGetDrawWindow(); // Used then to detect if the menu is called  
      UpdateDisplay(0, 0, true, false);
      handled = true;
      break;
    
    case keyDownEvent:
			if (event->data.keyDown.chr == vchrPageUp) {
        Undo(); Undo();
        handled = true;
        }
        break;

//    case penMoveEvent:
//      if ((sqSel.start >= 0) && (sqSel.end >= 0)) break;
    case penDownEvent:    // select piece
      if (bothsides) break;
      if (!force && (player == computer)) break;
      if (getsq(event->screenX, event->screenY, &sq)) {
				if (color[sq] == opponent) { // selection of an opponent piece ?
          if (sqSel.start >= 0) UnselectPieces();   // First, unselect previous selections
          sqSel.start = sq;       // Select new
          DrawPiece(sq);
	        }
        else if ((sqSel.start >= 0) && (sqSel.end == NoSqSelected)) { // try a move ?
          sqSel.end = sq;
          player = opponent;
          // Translate to algebric move. Test for castle, then verify move, as in uxdsp.c
          isCastle = (board[sqSel.start] == king && distance(sqSel.start,sqSel.end) > 1);
          algbr(sqSel.start,sqSel.end,isCastle);
          StrCopy(s,mvstr1);  // uxdsp.c, InputCommand()
          WAIT_UNTIL_NO_PEN_EVENT
          nPiece = PieceCnt[otherside[player]];		// The actual other side piece cnt
          ok = VerifyMove(s,0,&mv);
          if (ok && mv != hint) { 
            Sdepth = 0;
            ft = 0;
            }
          if (ok) {
            CheckAtak();
            PlaySound(opponent, (nPiece != PieceCnt[otherside[player]]));
            ElapsedTime(1);
            player = computer;    // Added : computer's turn
            if (force) {
              computer = opponent; opponent = otherside[computer]; // uxdsp.c, InputCommand()
              }
            }
          }
	      handled = true;
        }
      break;

    case ctlSelectEvent:		// transform buttons into menu commands
    	switch(event->data.ctlSelect.controlID) {
      	case btnNew:			event->data.menu.itemID = mnuNew; break;
        case btnOptions: 	event->data.menu.itemID = mnuPrefs; break;
      	case btnLoad: 		event->data.menu.itemID = mnuLoad; break;
      	case btnSave: 		event->data.menu.itemID = mnuSave; break;
      	case btnEdit:			event->data.menu.itemID = mnuEdit; break;
      	case btnUndo:			event->data.menu.itemID = mnuUndo; break;
      	case btnHint:			event->data.menu.itemID = mnuHint; break;
      	case btnReverse:	event->data.menu.itemID = mnuReverse; break;
      	case btnSwitch:		event->data.menu.itemID = mnuSwitchSides; break;
				default:					event->data.menu.itemID = 0;
      	}
      
    case menuEvent:
      switch(event->data.menu.itemID) {
        case mnuPrefs:
          IsNewGame = false;
          SelectLevel();
          handled = true;
          break;
          
        case mnuNew:
          IsNewGame = true;
          SelectLevel();
          handled = true;
          break;
          
        case mnuEdit:
        	FrmGotoForm(frmEditBrd);
          handled = true;
          break;

        case mnuLoad:
        case mnuSave:
        	FrmAlert(NotImplemented);
          handled = true;
          break;
          
        case mnuUndo:
        	MenuEraseStatus(NULL); 	// Clears the menu command bar. 
          Undo(); Undo();
          handled = true;
          break;
          
        case mnuHint:
        	MenuEraseStatus(NULL); 	// Clears the menu command bar. 
          GiveHint();
          handled = true;
          break;
          
        case mnuReverse:
          reverse = !reverse;
          UpdateDisplay(0,0,1,0);
          handled = true;
          break;
          
        case mnuSwitchSides:
          SwitchSides();
          handled = true;
          break;
                    
        case mnuHelp:
        	FrmHelp(HelpString);
          handled = true;
          break;

        case mnuAbout:
          FrmGotoForm(frmAbout);
          handled = true;
          break;
        }
          
    default:
      break;
  }

  return handled;
}

/****************************************************************************/
Boolean frmEditBrd_HandleEvent(EventPtr event)
{
FormPtr form;
Boolean handled = false;
UInt16  sq;

	switch (event->eType) {
		case frmOpenEvent:
      form = FrmGetActiveForm();
      FrmDrawForm(form);
      DispWindowH = WinGetDrawWindow(); // Used then to detect if the menu is called  
      UnselectPieces();
      UpdateDisplay(0, 0, true, false);
      handled = true;
      break;

    case penDownEvent:    // select piece
      if (getsq(event->screenX, event->screenY, &sq)) { // a square is tapped
				if (sqSel.start == sq) UnselectPieces();	// Dual tap : unselect square
				else if (board[sqSel.start] != no_piece) { // if a piece is previously selected      	
        	board[sq] = board[sqSel.start]; color[sq] = color[sqSel.start]; // move it there 				 
        	board[sqSel.start] = no_piece; color[sqSel.start] = neutral;
			    UpdateDisplay(sqSel.start,sq,0,false);
          UnselectPieces();
          }
				else {        	       // Select new
          UnselectPieces();
        	sqSel.start = sq;
 	      	DrawPiece(sq);
   	      }
        handled = true;
        }
      else if (event->screenX < 8 * 18 || event->screenY > HBMPEDIT) {	// try to remove a piece
        if (sqSel.start != NoSqSelected) { // a square was selected
        	board[sqSel.start] = no_piece; color[sqSel.start] = neutral;
 	        DrawPiece(sqSel.start);
          }
        handled = true;
        }
	    break;

    case ctlSelectEvent:		// transform buttons into menu commands
    	switch(event->data.ctlSelect.controlID) {
      	case btnClear:			event->data.menu.itemID = mnuClear; break;
        case btnAdd: 				event->data.menu.itemID = mnuAdd; break;
      	case btnRemove: 		event->data.menu.itemID = mnuRemove; break;
      	case btnExit: 			event->data.menu.itemID = mnuClose; break;
				default:						event->data.menu.itemID = 0;
      	}
      
		case menuEvent:
      switch(event->data.menu.itemID) {
      	case mnuClose:		// uxdsp.c, EditBoard()
  			  if (board[4] != king) kingmoved[white] = 10;
  			  if (board[60] != king) kingmoved[black] = 10;
  			  GameCnt = -1; Game50 = 0; Sdepth = 0;
  			  InitializeStats();
          UnselectPieces();
        	FrmGotoForm(frmMain);
  	      handled = true;
    	    break;

        case mnuClear:
        	if (FrmAlert(ConfClearBd) == 0) {	// OK pressed
            for (sq = 0; sq < 64; sq++) {		// uxdsp.c, EditBoard()
              board[sq] = no_piece; color[sq] = neutral;
            	}
	        	UpdateDisplay(0,0,1,0);
          	}
          handled = true;
          break;

        case mnuAdd:
	        if (sqSel.start != NoSqSelected) { // a square was selected
          	FrmPopupForm(frmPieces);					// Display Pieces select form
          	}
          else FrmAlert(AlertSelectSq);
          handled = true;
          break;
        	
        case mnuRemove:
	        if (sqSel.start != NoSqSelected) { // a square was selected
	        	board[sqSel.start] = no_piece; color[sqSel.start] = neutral;
  	        DrawPiece(sqSel.start);
            }
    	    handled = true;
          break;

        case mnuHelpEdit:
        	FrmHelp(StrHelpEdit);
          handled = true;
          break;

        case mnuAbout:
          FrmGotoForm(frmAbout);
          handled = true;
          break;
        }
      
    default:
      break;
    }

    return handled;
}

/****************************************************************************/
Boolean frmPrefs_HandleEvent(EventPtr event)
{
FormPtr form;
ListPtr ListP;
Boolean handled = false;
UInt16  lst, l, d, c;
  
  form = FrmGetActiveForm();
  switch (event->eType)
  {
    case frmOpenEvent:
      form = FrmGetActiveForm();
      if (bothsides) {
        CtlSetValue(GetCtrlPtr(form, btnWhiteCom), true);
        CtlSetValue(GetCtrlPtr(form, btnBlackCom), true);
      }
      else if (force) {
        CtlSetValue(GetCtrlPtr(form, btnWhiteHum), true);
        CtlSetValue(GetCtrlPtr(form, btnBlackHum), true);
      }
      else { 
        CtlSetValue(GetCtrlPtr(form, (opponent == white) ? btnWhiteHum : btnBlackHum), true);
        CtlSetValue(GetCtrlPtr(form, (computer == white) ? btnWhiteCom : btnBlackCom), true);
      }
      CtlSetValue(GetCtrlPtr(form, chkSound), beep);
      if (Level == NoLevel) Level = 0;    // first time OpenChess is run
      l = Level / SysTicksPerSecond();
      lst = 1;
      while ((l * 2) >= (1 << lst)) lst++;
      lst--;
      ListP = GetCtrlPtr(form, lstCompTime);		// Computer time
      LstSetSelection(ListP, lst); LstMakeItemVisible(ListP, lst);
      CtlSetLabel(GetCtrlPtr(form, trgCompTime), LstGetSelectionText(ListP, lst));

      ListP = GetCtrlPtr(form, lstDither);		// Dither
      d = dither / 20;
      LstSetSelection(ListP, d); LstMakeItemVisible(ListP, d);
      CtlSetLabel(GetCtrlPtr(form, trgDither), LstGetSelectionText(ListP, d));

      ListP = GetCtrlPtr(form, lstContempt);		// Contempt
      c = contempt / 20;
      LstSetSelection(ListP, c); LstMakeItemVisible(ListP, c);
      CtlSetLabel(GetCtrlPtr(form, trgContempt), LstGetSelectionText(ListP, c));

      FrmDrawForm(form);
      handled = true;
      break;
      
    case ctlSelectEvent:
      if (event->data.ctlSelect.controlID == btnOK) {
        opponent = white; computer = black; 
        if (IsNewGame) { // Select, between Options and New Game commands
          NewGame();  
          player = opponent;
          sqSel.start = sqSel.end = NoSqSelected; 
          }
        if (CtlGetValue(GetCtrlPtr(form, btnBlackHum)) &&  
            CtlGetValue(GetCtrlPtr(form, btnWhiteCom))) SwitchSides();
        bothsides = (CtlGetValue(GetCtrlPtr(form, btnBlackCom)) &&  
            CtlGetValue(GetCtrlPtr(form, btnWhiteCom)));
        force = (CtlGetValue(GetCtrlPtr(form, btnBlackHum)) &&  
            CtlGetValue(GetCtrlPtr(form, btnWhiteHum)));
        beep = CtlGetValue(GetCtrlPtr(form, chkSound));
        Level = (1 << LstGetSelection(GetCtrlPtr(form, lstCompTime))) >> 1;
     		Level *= SysTicksPerSecond();

				dither = 20 * LstGetSelection(GetCtrlPtr(form, lstDither));
				contempt = 20 * LstGetSelection(GetCtrlPtr(form, lstContempt));
        
        handled = true;
        }
      if (event->data.ctlSelect.controlID == btnCancel) handled = true;
      if (handled) FrmGotoForm(frmMain);
      break;
  
    default:
      break;
  }

  return handled;
}

/****************************************************************************/
Boolean frmPieces_HandleEvent(EventPtr event)
{
FormPtr form;
Boolean handled = false;
short 	SelColor, SelPiece, x, y;

  switch (event->eType) {
    case frmOpenEvent:
      form = FrmGetActiveForm();
      FrmDrawForm(form);
      DispWindowH = WinGetDrawWindow();  
			for (SelColor = white; SelColor <= black; SelColor++)		// Draw the pieces
      	for (SelPiece = pawn; SelPiece <= king; SelPiece++) {
        	x = (SelPiece - 1) * szSq;
          y = SelColor * szSq + TitleHeight;
    			Copy2screen(PiecesOffset[SelPiece], SelColor * PieceHeight, 
          						PiecesWidth[SelPiece], PieceHeight, x, y, winPaint);
					}
      handled = true;
      break;
      
    case penDownEvent:    // select piece
    #ifdef SONYCLIE
     	event->screenX *= 2; event->screenY *= 2;
    #endif
    	color[sqSel.start] = (event->screenY < TitleHeight + szSq) ? black : white;
      board[sqSel.start] = (event->screenX / szSq) + 1;
      FrmReturnToForm(0);
      DispWindowH = WinGetDrawWindow();
			UnselectPieces();      
      DrawPiece(sqSel.start);  
      handled = true;
      break;
      
    default:
      break;
    }

  return handled;
}

/****************************************************************************/
Boolean frmAbout_HandleEvent(EventPtr event)
{
FormPtr form;
Boolean handled = false;

  switch (event->eType) {
    case frmOpenEvent:
      form = FrmGetActiveForm();              // Repaint form on open
      FrmDrawForm(form);
      handled = true;
      break;
      
    case ctlEnterEvent:
      if (event->data.ctlSelect.controlID == cmdClose) FrmGotoForm(frmMain);
      handled = true;
      break;
      
    default:
      break;
    }

  return handled;
}


// FROM uxdsp.c : not processed

void ShowSidetomove(void){}
void ShowDepth(char ch){}
void ShowResults(short score,unsigned short bstline[],char ch){}
void SearchStartStuff(short side){}
void ShowCurrentMove(short pnt, short f, short t){}

/****************************************************************************/
/*void UpdateClocks(void)   // Copied from uxdsp.c, but not used in this version
{
short m,s;
  m = et/60; s = (et - 60*m);
  if (TCflag)
    {
      m = (TimeControl.clock[player] - et) / 60;
      s = TimeControl.clock[player] - et - 60*m;
    }
  if (m < 0) m = 0;
  if (s < 0) s = 0;

}
*/
