/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* 
 * Copyright 1997 - 2000 by Eric House (fixin@peak.org).  All rights reserved.
 *
 * 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.
 */

#ifndef _MODEL_H_
#define _MODEL_H_

#include "comtypes.h"
/* #include "xptypes.h" */
#include "dictnry.h"
#include "mempool.h"

#ifdef CPLUS
extern "C" {
#endif

#define MAX_ROWS 16
#define MAX_COLS 16
#define NUMCOLS_NBITS 4

#ifdef EIGHT_TILES
#define MAX_TRAY_TILES 8
#define NTILES_NBITS 4
#else
#define MAX_TRAY_TILES 7
#define NTILES_NBITS 3
#endif

#define MIN_TRADE_TILES MAX_TRAY_TILES
/* apply to CellTile */
#define TILE_VALUE_MASK 0x001F 
#define TILE_BLANK_BIT 0x0020
#define IS_BLANK(t)  (((t)&TILE_BLANK_BIT)!= 0)
#define TILE_EMPTY_BIT 0x0040
#define TILE_PENDING_BIT 0x0080
#define PREV_MOVE_BIT 0x100

#define CELL_OWNER_MASK 0x0600
#define CELL_OWNER_OFFSET 9
#define CELL_OWNER(t) (((t)&CELL_OWNER_MASK) >> CELL_OWNER_OFFSET)

#define MAX_UNIQUE_TILES 32	/* max tile non-blank faces */
#define MAX_NUM_BLANKS 4

/* Used by scoring code and engine as fast representation of moves. */
typedef struct MoveInfoTile {
    XP_U8 varCoord;		/* 5 bits ok (0-16 for 17x17 board) */
    Tile tile;			/* 5 bits will do */
} MoveInfoTile;

typedef struct MoveInfo {
    XP_U8 nTiles;		/* 4 bits: 0-7 */
    XP_U8 commonCoord;		/* 5 bits: 0-16 if 17x17 possible */
    XP_Bool isHorizontal;	/* 1 bit */
    /* If this is to go on an undo stack, we need player num here, or the code
       has to keep track of it *and* there must be exactly one entry per
       player per turn. */
    MoveInfoTile tiles[MAX_TRAY_TILES];
} MoveInfo;

typedef XP_U8 TrayTile;
typedef struct TrayTileSet {
    XP_U8 nTiles;
    TrayTile tiles[MAX_TRAY_TILES];
} TrayTileSet;

typedef struct BlankQueue {
    XP_U16 nBlanks;
    XP_U8 col[MAX_NUM_BLANKS];
    XP_U8 row[MAX_NUM_BLANKS];
} BlankQueue;

typedef XP_U8 TileBit;	    /* bits indicating selection of tiles in tray */
#define ALLTILES ((TileBit)~(0xFF<<(MAX_TRAY_TILES)))

#define ILLEGAL_MOVE_SCORE (-1)

#define EMPTY_TILE TILE_EMPTY_BIT
#define TILE_IS_EMPTY(t) (((t)&TILE_EMPTY_BIT)!=0)
#define REVERSED_TILE TILE_PENDING_BIT /* reuse that bit for tile drawing
					  only */


ModelCtxt* model_make( MPFORMAL DictionaryCtxt* dict, XW_UtilCtxt* util,
		       XP_U16 nCols, XP_U16 nRows );

ModelCtxt* model_makeFromStream( MPFORMAL XWStreamCtxt* stream, 
				 DictionaryCtxt* dict, XW_UtilCtxt* util );

void model_writeToStream( ModelCtxt* model, XWStreamCtxt* stream );

void model_init( ModelCtxt* model, XP_U16 nCols, XP_U16 nRows );
void model_destroy( ModelCtxt* model );
void model_setNPlayers( ModelCtxt* model, XP_U16 numPlayers );

void model_setDictionary( ModelCtxt* model, DictionaryCtxt* dict );
DictionaryCtxt* model_getDictionary( ModelCtxt* model );

XP_Bool model_getTile( ModelCtxt* model, XP_U16 col, XP_U16 row,
		       XP_Bool getPending, XP_S16 turn,
		       Tile* tile, XP_Bool* isBlank, 
		       XP_Bool* isPending, XP_Bool* isRecent );

void model_listPlacedBlanks( ModelCtxt* model, XP_U16 turn,
			     XP_Bool includePending, BlankQueue* bcp );

XP_U16 model_getCellOwner( ModelCtxt* model, XP_U16 col, XP_U16 row );

void model_assignPlayerTiles( ModelCtxt* model, XP_S16 turn, 
			      TrayTileSet* tiles );
Tile model_getPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );

Tile model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index,
			  Tile tile );

/* As an optimization, return a pointer to the model's array of tiles for a
   player.  Don't even think about modifying the array!!!! */
const TrayTileSet* model_getPlayerTiles( ModelCtxt* model, XP_S16 turn );

XP_U16 model_getNumTilesInTray( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesTotal( ModelCtxt* model, XP_S16 turn );
void model_moveBoardToTray( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col, 
			    XP_U16 row, XP_S16 tileIndex, Tile blankFace );
XP_S16 model_trayContains( ModelCtxt* model, XP_S16 turn, Tile tile );


/* void model_setTile( ModelCtxt* model, XP_U16 col, XP_U16 row,  */
/* 		    Tile tile, XP_Bool isBlank ); */
XP_U16 model_numRows( ModelCtxt* model );
XP_U16 model_numCols( ModelCtxt* model );

/* XP_U16 model_numTilesCurrentTray( ModelCtxt* model ); */
/* Tile model_currentTrayTile( ModelCtxt* model, XP_U16 index ); */
void model_addToCurrentMove( ModelCtxt* model, XP_S16 turn, 
			     XP_U16 col, XP_U16 row, 
			     Tile tile, XP_Bool isBlank );
XP_U16 model_getCurrentMoveCount( ModelCtxt* model, XP_S16 turn );

void model_getCurrentMoveTile( ModelCtxt* model, XP_S16 turn, XP_S16* index,
			       Tile* tile, XP_U16* col, XP_U16* row, 
			       XP_Bool* isBlank );

void model_commitTurn( ModelCtxt* model, XP_S16 player, 
		       TrayTileSet* newTiles );
void model_commitRejectedPhony( ModelCtxt* model, XP_S16 player );
void model_makeTileTrade( ModelCtxt* model, XP_S16 player,
			  TrayTileSet* oldTiles, TrayTileSet* newTiles );

XP_Bool model_undoLatestMoves( ModelCtxt* model, PoolContext* pool, 
                               XP_U16 nMovesSought, XP_U16* turn, 
                               XP_S16* moveNum );
void model_rejectPreviousMove( ModelCtxt* model, PoolContext* pool,
                               XP_U16* turn );

void model_trayToStream( ModelCtxt* model, XP_S16 turn, 
                         XWStreamCtxt* stream );
void model_currentMoveToStream( ModelCtxt* model, XP_S16 turn, 
                                XWStreamCtxt* stream);
void model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
                               XWStreamCtxt* stream );
void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum, 
                                 MoveInfo* newMove );

void model_resetCurrentTurn( ModelCtxt* model, XP_S16 turn );

/********************* notification ********************/
typedef void (*BoardListener)(void* data, XP_U16 turn, XP_U16 col, 
                              XP_U16 row, XP_Bool added );
void model_setBoardListener( ModelCtxt* model, BoardListener bl, 
                             void* data );
typedef void (*TrayListener)(void* data, XP_U16 turn, TileBit bits );
void model_setTrayListener( ModelCtxt* model, TrayListener bl, 
                            void* data );
void model_foreachPendingCell( ModelCtxt* model, XP_S16 turn,
                               BoardListener bl, void* data );
void model_foreachPrevCell( ModelCtxt* model, BoardListener bl, void* data );

void model_writeGameHistory( ModelCtxt* model, XWStreamCtxt* stream,
                             ServerCtxt* server, /* for player names */
                             XP_Bool gameOver );

/* for the tile values dialog: total all the tiles in players trays and
   tentatively placed on the board. */
void model_countAllTrayTiles( ModelCtxt* model, XP_U16* counts );

/********************* scoring ********************/

typedef XP_Bool (*WordNotifierProc)( XP_UCHAR* word, void* closure );
typedef struct WordNotifierInfo {
    WordNotifierProc proc;
    void* closure;
} WordNotifierInfo;

XP_Bool getCurrentMoveScoreIfLegal( ModelCtxt* model, XP_S16 turn, 
                                    XWStreamCtxt* stream, XP_S16* score );
XP_S16 model_getPlayerScore( ModelCtxt* model, XP_S16 player );
XP_Bool model_checkMoveLegal( ModelCtxt* model, XP_S16 player, 
                              XWStreamCtxt* stream,
                              WordNotifierInfo* notifyInfo );

void model_figureFinalScores( ModelCtxt* model, XP_S16* scores,
                              XP_S16* tilePenalties );

/* figureMoveScore is meant only for the engine's use */
XP_U16 figureMoveScore( ModelCtxt* model, MoveInfo* moveInfo, 
                        EngineCtxt* engine, XWStreamCtxt* stream, 
                        XP_Bool silent, WordNotifierInfo* notifyInfo );

/********************* persistence ********************/
#ifdef INCLUDE_IO_SUPPORT
void model_load( ModelCtxt* model, XP_Stream* inStream );
void model_store( ModelCtxt* model, XP_Stream* outStream );
#endif


#ifdef CPLUS
}
#endif

#endif

