#include <stdio.h>
#include <string.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#ifdef __GNUC__
#include "clib.h"
#endif
#include "textwindow.h"
#include "othello.h"
#include "global.h"
#include "io.h"


void pass_proc();
void undo_proc();
void newgame_proc();
void quit_proc();
void resign_proc();
void level_proc();
void color_proc();
void draw_piece();
void print_state();
void make_move();

void resetstate();

/************************ MUST BE CHANGED *********************************/

void
do_nothing()
{
}


static Frame     frame;
static Canvas    board_canvas;
static Canvas    message_canvas;
static Canvas    moves_canvas;
static Canvas    status_canvas;
static Panel     panel;
static Panel_item   pass_button;
static Panel_item   undo_button;
static Panel_item   newgame_button;
static Panel_item   quit_button;
static Panel_item   resign_button;
static Panel_item   color_choice;
static Panel_item   level_choice;
static Pixfont   *smallfont;
static Pixfont   *bigfont;
static Pixfont   *bigboldfont;

static Textwindow   *message_tw;
static Textwindow   *moves_tw;
static Textwindow   *status_tw;

#define Bitmap Pixrect
#define make_bitmap(width, height, array) \
    mem_point(width, height, 1, array)


Bitmap   *nopiece_bm;
Bitmap   *blackpiece_bm;
Bitmap   *whitepiece_bm;


void   board_event_handler();	/* Forward declarations */


unsigned short nopiece_arr[] = {
#include "nopiece.icon"
};

unsigned short blackpiece_arr[] = {
#include "blackpiece.icon"
};

unsigned short whitepiece_arr[] = {
#include "whitepiece.icon"
};
#define BM_WIDTH   64


/* The icon */
static short icon_image[] = {
#include "othellotool.icon"
};
DEFINE_ICON_FROM_IMAGE(the_icon, icon_image);

#define FONTHEIGHT 15
#define FONTWIDTH 7
#define MESSAGE_WIDTH   40
#define MESSAGE_HEIGHT  11
#define MOVES_WIDTH     15
#define MOVES_HEIGHT    26


static int    moveno;
static bool   blacks_move = TRUE;

static Square   movesquare;
static bool     movesquarevalid = FALSE;


/* ================================================================ */
/*                         Window functions                         */


void
init_windows(argc, argv)
int    *argc;
char   *argv[];
{
    smallfont = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.r.12");
    bigfont = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.r.14");
    bigboldfont = pf_open("/usr/lib/fonts/fixedwidthfonts/screen.b.14");

    if (smallfont == NULL) {
	fprintf(stderr, "smallfont was not found\n");
	exit(1);
    }
    if (bigfont == NULL) {
	fprintf(stderr, "bigfont was not found\n");
	exit(1);
    }
    nopiece_bm = make_bitmap(BM_WIDTH, BM_WIDTH, nopiece_arr);
    blackpiece_bm = make_bitmap(BM_WIDTH, BM_WIDTH, blackpiece_arr);
    whitepiece_bm = make_bitmap(BM_WIDTH, BM_WIDTH, whitepiece_arr);

    /* Main window */
    frame = window_create(NULL, FRAME,
			  FRAME_LABEL,        "OTHELLOTOOL 1.0 by Inge Wallin",
			  FRAME_ICON,         &the_icon,
			  FRAME_ARGS,         *argc, argv,
			  FRAME_SUBWINDOWS_ADJUSTABLE, FALSE,
			  0);

    board_canvas = window_create(frame, CANVAS,
                  /* set default size */
		  WIN_WIDTH,                8 * BM_WIDTH,
		  WIN_HEIGHT,               8 * BM_WIDTH,		 

                  /* default, but do it anyway for clarity */	
		  WIN_CONSUME_PICK_EVENT,   MS_LEFT,
		  WIN_CONSUME_PICK_EVENT,   MS_MIDDLE,
		  WIN_CONSUME_PICK_EVENT,   MS_RIGHT,
		  WIN_CONSUME_KBD_EVENT,    WIN_ASCII_EVENTS,

                  /* ... and don't let the drawing area shrink below it */
		  CANVAS_AUTO_SHRINK,       FALSE,
				 
		  WIN_EVENT_PROC,           board_event_handler,
		  0);

    moves_canvas = window_create(frame, CANVAS,
				 WIN_WIDTH,           MOVES_WIDTH * FONTWIDTH,
				 WIN_HEIGHT,          MOVES_HEIGHT * FONTHEIGHT
                                                      + 5,
				 WIN_RIGHT_OF,        board_canvas,
				 WIN_Y,               0,
				 WIN_EVENT_PROC,      do_nothing,
				 0);
    
    status_canvas = window_create(frame, CANVAS,
				  WIN_WIDTH,     MOVES_WIDTH * FONTWIDTH,
				  WIN_HEIGHT,    8 * BM_WIDTH 
                                                 - MOVES_HEIGHT *FONTHEIGHT
                                                 - 10,
				  WIN_RIGHT_OF,  board_canvas,
				  WIN_BELOW,     moves_canvas,
				  WIN_EVENT_PROC,do_nothing,
				  0);
	
    message_canvas = window_create(frame, CANVAS,
				   WIN_WIDTH,      MESSAGE_WIDTH * FONTWIDTH,
				   WIN_HEIGHT,     MESSAGE_HEIGHT * FONTHEIGHT,
				   WIN_X,          0,
				   WIN_BELOW,      board_canvas,
				   WIN_EVENT_PROC, do_nothing,
				   0);
	
    panel = window_create(frame, PANEL,
			  WIN_BELOW,            board_canvas,
			  WIN_RIGHT_OF,         message_canvas,
			  PANEL_LABEL_FONT,     bigboldfont,
			  PANEL_LABEL_BOLD,     TRUE,
			  0);

    pass_button = panel_create_item(panel, PANEL_BUTTON,
				    PANEL_LABEL_IMAGE,
				    panel_button_image(panel, "Pass",
						       8, bigboldfont),
				    PANEL_NOTIFY_PROC,    pass_proc,
				    PANEL_ITEM_X,         ATTR_COL(1),
				    PANEL_ITEM_Y,         ATTR_ROW(0),
				    0);

    undo_button = panel_create_item(panel, PANEL_BUTTON,
				    PANEL_LABEL_IMAGE,
				    panel_button_image(panel, "Undo",
						       8, bigboldfont),
				    PANEL_NOTIFY_PROC,    undo_proc,
				    PANEL_ITEM_X,         ATTR_COL(1),
				    PANEL_ITEM_Y,         ATTR_ROW(2),
				    0);

    newgame_button = panel_create_item(panel, PANEL_BUTTON,
				       PANEL_LABEL_IMAGE,
				       panel_button_image(panel, "New Game",
							  8, bigboldfont),
				       PANEL_NOTIFY_PROC,    newgame_proc,
				       PANEL_ITEM_X,         ATTR_COL(15),
				       PANEL_ITEM_Y,         ATTR_ROW(0),
				       0);

    quit_button = panel_create_item(panel, PANEL_BUTTON,
				    PANEL_LABEL_IMAGE,
				    panel_button_image(panel, "Quit",
						       8, bigboldfont),
				    PANEL_NOTIFY_PROC,    quit_proc,
				    PANEL_ITEM_X,         ATTR_COL(15),
				    PANEL_ITEM_Y,         ATTR_ROW(2),
				    0);

    resign_button = panel_create_item(panel, PANEL_BUTTON,
				      PANEL_LABEL_IMAGE,
				      panel_button_image(panel, "Resign",
							 8, bigboldfont),
				      PANEL_NOTIFY_PROC,    resign_proc,
				      PANEL_ITEM_X,         ATTR_COL(29),
				      PANEL_ITEM_Y,         ATTR_ROW(0),
				      0);

    color_choice = panel_create_item(panel, PANEL_CHOICE,
				     PANEL_LABEL_STRING,     "Computer Plays:",
				     PANEL_CHOICE_STRINGS,   "White",
				                             "Black",
				                             0,
				     PANEL_CHOICE_XS, ATTR_COL(1),
				                      ATTR_COL(10),
				                      0,
				     PANEL_CHOICE_YS, ATTR_ROW(5),
						      ATTR_ROW(5),
				                      0,
				     PANEL_DISPLAY_LEVEL,    PANEL_ALL,
				     PANEL_FEEDBACK,         PANEL_INVERTED,
				     PANEL_LAYOUT,           PANEL_HORIZONTAL,
				     PANEL_NOTIFY_PROC,      color_proc,
				     PANEL_ITEM_X,           ATTR_COL(1),
				     PANEL_ITEM_Y,           ATTR_ROW(4),
				     0);

    level_choice = panel_create_item(panel, PANEL_CHOICE,
				     PANEL_LABEL_STRING,     "Level:",
				     PANEL_CHOICE_STRINGS,
				       " 1 ", " 2 ", " 3 ",
				       " 4 ", " 5 ", " 6 ",
				       " 7 ", " 8 ", " 9 ",
				       0,
				     PANEL_CHOICE_XS,        
				      ATTR_COL(27), ATTR_COL(32), ATTR_COL(37),
				      ATTR_COL(27), ATTR_COL(32), ATTR_COL(37),
				      ATTR_COL(27), ATTR_COL(32), ATTR_COL(37),
				      0,
				     PANEL_CHOICE_YS,
				       ATTR_ROW(4), ATTR_ROW(4), ATTR_ROW(4),
				       ATTR_ROW(5), ATTR_ROW(5), ATTR_ROW(5),
				       ATTR_ROW(6), ATTR_ROW(6), ATTR_ROW(6),
				       0,
				     PANEL_DISPLAY_LEVEL,    PANEL_ALL,
				     PANEL_FEEDBACK,         PANEL_INVERTED,
				     PANEL_LAYOUT,           PANEL_HORIZONTAL,
				     PANEL_NOTIFY_PROC,      level_proc,
				     PANEL_ITEM_X,           ATTR_COL(20),
				     PANEL_ITEM_Y,           ATTR_ROW(4),
				     0);

    window_fit(frame);
}



/* Event handler for the board window. */
/*ARGSUSED*/
void board_event_handler(canvas, event, arg)
Canvas  canvas;
Event   *event;
caddr_t arg;
{
    int      col;
    int      row;
    int      evid;

    evid = event_id(event);
    col = 1 + event_x(event) / BM_WIDTH;
    row = 1 + event_y(event) / BM_WIDTH;

    if (evid < 128) {
	/* Character event */
	if (evid == '0')
	    make_move(PASS);
    } else {
	switch (evid) {
	  case MS_LEFT:
	    if (event_is_down(event)) {
		/* NOTHING */;
	    } else if (event_is_up(event))
		make_move(SQ(col, row));
	    break;

	  default:
	    break;
	}
    }
}



/*
 * Or a bitmap into the current canvas.
 */

static void
window_put_bitmap(bitmap, x, y, width, height)
struct pixrect *bitmap;
int            x, y;
int            width, height;
{
    pw_write(canvas_pixwin(board_canvas), x, y, width, height,
	     PIX_SRC, bitmap, 0, 0);
}



void
undo_proc()
{
    make_move(UNDO);
}


void
pass_proc()
{
    make_move(PASS);
}


void
quit_proc()
{
    exit(0);
}


void
resign_proc()
{
    if (game_in_progress) {
	print_message("Giving up, eh? Chicken.\n");
	movesquarevalid = TRUE;
	movesquare = RESIGN;
    } else {
	print_message("There is no game to resign.\n");
    }

}


void
level_proc(item, value, event)
Panel_item   item;
int          value;
Event        *event;
{
    char   buff[100];

    level = value + 1;
    sprintf(buff, "Playing level reset to %d.\n", level);
    print_message(buff);
}


void
color_proc(item, value, event)
Panel_item   item;
int          value;
Event        *event;
{
    if (game_in_progress && value == (mycolor == WHITE)) {
	print_message("You can't change color during a game!\n");
	panel_set(color_choice, PANEL_VALUE, 1 - value, 0);

    } else {
	if (value == 0) {
	    mycolor = WHITE;
	    yourcolor = BLACK;
	} else {
	    mycolor = BLACK;
	    yourcolor = WHITE;
	}
    }
}



/*********************** Game functions ******************************/


void
init_io(argc, argv)
int    *argc;
char   *argv[];
{
    moveno = 1;

    init_windows(argc, argv);

    message_tw = textwindow_create(message_canvas, smallfont,
				   FONTWIDTH, FONTHEIGHT,
				   MESSAGE_WIDTH, MESSAGE_HEIGHT);
    textwindow_set_scrollregion(message_tw, 1, MESSAGE_HEIGHT - 1);

    moves_tw = textwindow_create(moves_canvas, smallfont,
				 FONTWIDTH, FONTHEIGHT,
				 MOVES_WIDTH, MOVES_HEIGHT);
    textwindow_set_scrollregion(moves_tw, 2, MOVES_HEIGHT - 1);

    status_tw = textwindow_create(status_canvas, smallfont,
				  FONTWIDTH, FONTHEIGHT,
				  MOVES_WIDTH, 6);

    panel_set(level_choice, PANEL_VALUE, level - 1, 0);
    panel_set(color_choice, PANEL_VALUE, mycolor, 0);

    {
	State   state;

	resetstate(&state);
	print_state(&state);
	init_io_for_one_game();
    }

    window_set(frame, WIN_SHOW, TRUE, 0);
    notify_dispatch();
}



void
init_io_for_one_game()
{
    int   i;

    textwindow_putcur(message_tw, 0, 0);
    textwindow_print(message_tw, "       M E S S A G E   A R E A\n");
    textwindow_putcur(message_tw, 1, 0);
    for (i = 0; i < MESSAGE_HEIGHT - 1; ++i)
	textwindow_print(message_tw, "                                \n");
    textwindow_putcur(message_tw, 1, 0);

    textwindow_putcur(moves_tw, 0, 0);
    textwindow_print(moves_tw,"    M O V E S\n");
    if (mycolor == BLACK)
	textwindow_print(moves_tw, "     Me  You\n");
    else
	textwindow_print(moves_tw, "    You   Me\n");

    textwindow_putcur(moves_tw, 2, 0);
    for (i = 0; i < MOVES_HEIGHT - 2; ++i)
	textwindow_print(moves_tw, "            \n");
    textwindow_putcur(moves_tw, 2, 0);
    moveno = 1;
    blacks_move = TRUE;

    textwindow_putcur(status_tw, 0, 0);
    textwindow_print(status_tw, "  S T A T U S");
}



void
draw_piece(col, row, color)
int   col;
int   row;
int   color;
{
    Bitmap   *bm;

    switch (color) {
      case EMPTY:
	bm = nopiece_bm;
	break;
      case MYCOLOR:
	bm = (mycolor == BLACK ? blackpiece_bm : whitepiece_bm);
	break;
      case YOURCOLOR:
	bm = (mycolor == BLACK ? whitepiece_bm : blackpiece_bm);
      default:
	break;
    }
    window_put_bitmap(bm, (col - 1) * BM_WIDTH,
		      (row - 1) * BM_WIDTH,
		      BM_WIDTH, BM_WIDTH);
}



void
print_board(board)
Board   board;
{
    int   col;
    int   row;

    for (col = 1; col < 9; ++col)
	for (row = 1; row < 9; ++row)
	    draw_piece(col, row, board[SQ(col, row)]);
}



void
print_statistics(state)
State   *state;
{
    char  buff[100];
    int   mypieces;
    int   yourpieces;
void count_pieces();

    count_pieces(state, &mypieces, &yourpieces);
    if (mypieces == yourpieces) {
	sprintf(buff, "The game ended %d - %d. A tie.\n",
		mypieces, yourpieces);

    } else {
	sprintf(buff, "%s won with %d - %d\n",
		(mypieces > yourpieces) ? "I" : "You",
		mycolor == BLACK ? mypieces : yourpieces,
		mycolor == BLACK ? yourpieces : mypieces);
    }
    textwindow_print(message_tw, buff);
}



void
print_my_move(sq)
Square   sq;
{
    print_move(sq);
}


void
print_your_move(sq)
Square   sq;
{
    print_move(sq);
}



void
print_move(sq)
Square   sq;
{
    static char   movestr[3];
    static char   buf[20];

    if (sq == PASS)
	sprintf(movestr, "--");
    else
	sprintf(movestr, "%c%c", COL(sq) + 'a' - 1, ROW(sq) + '0');

    if (blacks_move) {
	sprintf(buf, "%2d.%4s", moveno, movestr);
	textwindow_print(moves_tw, buf);
    } else {
	sprintf(buf,  " - %s\n", movestr);
	textwindow_print(moves_tw, buf);
    }

    if (sq != PASS)
	++moveno;

    blacks_move = !blacks_move;
}



void
print_message(message, force)
char   *message;
bool   force;
{
    textwindow_print(message_tw, message);
}



void
print_state(state)
State   *state;
{
    static char   buf[100];
    int   my;
    int   yours;
    
    print_board(&(state->board[0]));

    count_pieces(state, &my, &yours);
    sprintf(buf, "You have  %2d  \nI have    %2d  ",
	    yours, my);
    textwindow_putcur(status_tw, 2, 0);
    textwindow_print(status_tw, buf);
}



void
make_move(sq)
Square   sq;
{
    movesquare = sq;
    movesquarevalid = TRUE;
}



Square
get_users_move(state)
State   *state;
{
    Sqlist   legal_moves;
    int      num_moves;
bool   can_move();
void   get_legal_moves();

    get_legal_moves(state, YOURCOLOR, &legal_moves, &num_moves);
    while (TRUE) {
	notify_dispatch();
	if (movesquarevalid) {
	    movesquarevalid = FALSE;
	    if (movesquare == UNDO || movesquare == RESIGN)
		return movesquare;
	    if (movesquare == PASS && num_moves == 0) {
		return PASS;
	    } else if (can_move(state, movesquare, YOURCOLOR)) {
		return movesquare;
	    } else {
		print_message("Illegal move\n");
	    }
	} else
	    usleep(100000);
    }
}



void
show_status(status)
int   status;
{
    textwindow_putcur(status_tw, 6, 0);
    switch (status) {
      case MYMOVE:
	textwindow_print(status_tw, "Thinking...");
	break;

      case YOURMOVE:
	textwindow_print(status_tw, "Your move  ");
	break;

      case NOGAME:
	textwindow_print(status_tw, "           ");
	break;

      default:
	fprintf(stderr, "BUG: show_status() got funny status: %d\n",
		status);
    }
}



bool  start_game_flag = FALSE;

static void
newgame_proc()
{
    if (game_in_progress) {
	print_message("You can't start a new game while\n");
	print_message("another one is in progress.\n");
    } else
	start_game_flag = TRUE;
}


void
start_game()
{
    while (TRUE) {
	notify_dispatch();
	if (start_game_flag)
	    return;
	else
	    usleep(100000);
    }
}


bool
another_game()
{
    start_game_flag = FALSE;
    print_message("Want another game?\nPress the \"New Game\" button\n");
    start_game();

    return TRUE;
}
