/* $Id$ */

#ifndef INCLUDE_CHESS_H
#define INCLUDE_CHESS_H

#define CHESS_DRAWING_TIME_ROW   128
#define CHESS_DRAWING_TURN_ROW   129
#define CHESS_DRAWING_WARN_ROW   130
#define CHESS_DRAWING_STEP_ROW   131
#define CHESS_DRAWING_HELP_ROW   132

#define CHESS_PHOTO_LINE      15
#define CHESS_PHOTO_COLUMN    (256 + 25)

struct ChessBroadcastList;
struct ChessActions;
struct ChessConstants;

#define const
#define static
#define private

typedef struct {
    char    userid[IDLEN + 1];
    int	    win;
    int     lose;
    int     tie;
    unsigned short rating;
    unsigned short orig_rating; // 原始 rating, 因為遊戲開始先算輸一場, rating 值就跑掉了
} ChessUser;

private typedef struct {
    int   used;
    int   size;
    void *body;
} ChessHistory;

/*   棋類觀戰
 *
 * 雙人對戰時，雙方都會有一個 broadcast_list 的 linked-list，紀錄著每下一
 * 步棋，必須將這個訊息丟給那些人（sock）。
 * 每當一個觀棋者加入（觀棋可以從紅方或黑方的觀點進行），其中一方的下棋者
 * 的 broadcast_list 就會多一筆記錄，之後就會將下的或收到對方下的每一步棋
 * 傳給 broadcast_list 中所有需要的人，達到觀棋的效果。
 */
private typedef struct ChessBroadcastListNode {
    int    sock;
    struct ChessBroadcastListNode *next;
} ChessBroadcastListNode;

private typedef struct ChessBroadcastList {
    struct ChessBroadcastListNode head; /* dummy node */
} ChessBroadcastList;


typedef struct {
    int     limit_hand;
    int     limit_time;
    int     free_time;
    enum {
	CHESS_TIMEMODE_MULTIHAND, /* 限時限步 */
	CHESS_TIMEMODE_COUNTING   /* 讀秒 */
    } time_mode;
} ChessTimeLimit;

typedef enum {
    CHESS_MODE_VERSUS,   /* 對奕 */
    CHESS_MODE_WATCH,    /* 觀棋 */
    CHESS_MODE_PERSONAL, /* 打譜 */
    CHESS_MODE_REPLAY    /* 看譜 */
} ChessGameMode;

typedef enum {
    CHESS_RESULT_CONTINUE,
    CHESS_RESULT_WIN,
    CHESS_RESULT_LOST,
    CHESS_RESULT_TIE,
    CHESS_RESULT_END   /* watching or replaying */
} ChessGameResult;

typedef struct ChessInfo {
    private const static
	struct ChessActions* actions; /* vtable */
    private const static
	struct ChessConstants* constants;

    rc_t cursor;

    /* 計時用, [0] = mine, [1] = his */
    int	    lefttime[2];
    int     lefthand[2]; /* 限時限步時用, = 0 表為自由時間或非限時限步模式 */
    const ChessTimeLimit* timelimit;

    const ChessGameMode mode;
    const ChessUser user1;
    const ChessUser user2;
    const char myturn;   /* 我方顏色 */

    char       turn;
    char       pass[2];
    char       warnmsg[64];
    char       last_movestr[36];
    char      *photo;

    void      *board;
    void      *tag;

    private int    sock;
    private ChessHistory history;
    private ChessBroadcastList broadcast_list;
    private ChessGameResult (*play_func[2])(struct ChessInfo* info);

    private int  current_step;  /* used by watch and replay */
    private char step_tmp[0];
} ChessInfo;

#undef const
#undef static
#undef private

typedef struct ChessActions {
    /* initial */
    void (*init_user)   (const userinfo_t* uinfo, ChessUser* user);
    void (*init_user_rec)(const userec_t* uinfo, ChessUser* user);
    void (*init_board)  (void* board);

    /* playing */
    void (*drawline)    (const ChessInfo* info, int line);
    void (*movecur)     (int r, int c);
    int  (*prepare_play)(ChessInfo* info);
    int  (*process_key) (ChessInfo* info, int key, ChessGameResult* result);
    int  (*select)      (ChessInfo* info, rc_t location,
	    ChessGameResult* result);
    void (*prepare_step)(ChessInfo* info, const void* step);
    ChessGameResult (*apply_step)(void* board, const void* step);
    void (*drawstep)    (ChessInfo* info, const void* step);
    ChessGameResult (*post_game)(ChessInfo* info);

    /* ending */
    void (*gameend)    (ChessInfo* info, ChessGameResult result);
    void (*genlog)     (ChessInfo* info, FILE* fp, ChessGameResult result);
} ChessActions;

typedef struct ChessConstants {
    int   step_entry_size;
    int   traditional_timeout;
    int   board_height;
    int   board_width;
    int   pass_is_step;
    const char *chess_name;
    const char *photo_file_name;
    const char *log_board;
    const char *turn_color[2];
    const char *turn_str[2];
} ChessConstants;

typedef enum {
    CHESS_STEP_NORMAL, CHESS_STEP_PASS,
    CHESS_STEP_DROP, CHESS_STEP_FAILURE,
    CHESS_STEP_SPECIAL, /* chesses' special steps */
    CHESS_STEP_NOP,     /* for wake up */

    CHESS_STEP_TIE,  /* tie request */
    CHESS_STEP_TIE_ACC,  /* accept */
    CHESS_STEP_TIE_REJ,  /* reject */

    CHESS_STEP_UNDO, /* undo request */
    CHESS_STEP_UNDO_ACC,  /* accept */
    CHESS_STEP_UNDO_REJ,  /* reject */
} ChessStepType;


int ChessTimeCountDown(ChessInfo* info, int who, int length);
void ChessStepMade(ChessInfo* info, int who);

ChessStepType ChessStepReceive(ChessInfo* info, void* step); /* should this be public? */
int ChessStepSend(ChessInfo* info, const void* step);
int ChessMessageSend(ChessInfo* info, ChessStepType type);

void ChessHistoryAppend(ChessInfo* info, void* step);
const void* ChessHistoryRetrieve(ChessInfo* info, int n);

void ChessPlay(ChessInfo* info);
int ChessStartGame(char func_char, int sig, const char* title);
int ChessWatchGame(void (*play)(int, ChessGameMode),
	int game, const char* title);
int ChessReplayGame(const char* fname);

ChessInfo* NewChessInfo(const ChessActions* actions,
	const ChessConstants* constants, int sock, ChessGameMode mode);
void DeleteChessInfo(ChessInfo* info);

void ChessEstablishRequest(int sock);
void ChessAcceptingRequest(int sock);
void ChessShowRequest(void);

void ChessRedraw(const ChessInfo* info);
void ChessDrawLine(const ChessInfo* info, int line);
void ChessDrawExtraInfo(const ChessInfo* info, int line, int space);

#endif /* INCLUDE_CHESS_H */
