#include <stdio.h>
#include <string.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include "textwindow.h"

#ifndef bool
#define bool   int
#endif
#ifndef FALSE
#define FALSE  0
#endif
#ifndef TRUE
#define TRUE   1
#endif

#define OTHELLOPROG "/net/u1/info/ingwa/src/iwothello/new/othello"


typedef   int Color;


typedef Color   Board[100];
#define EMPTY 0
#define BLACK 1
#define WHITE 2
#define BORDER  (BLACK | WHITE)
#define TURN(color)   (3 - (color))


typedef   int Position;
#define   PASS   -1

#define POS(col, row)  ((row) * 10 + (col))
#define ROW(pos)       ((pos) / 10)
#define COL(pos)       ((pos) % 10)


int   all_directions[] = {
    -11, -10, -9, -1, 1, 9, 10, 11
};

Board board;


/* The fildescriptors for the pipes to and from the othello program. */
#define WRITEEND  1
#define READEND   0
int   tool2prog[2];
int   prog2tool[2];


int   i_start = TRUE;


void pass_proc();
void quit_proc();
void draw_piece();
void print_status();
void make_move();

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

#define mycolor BLACK
#define yourcolor WHITE

/************************ Window stuff *********************************/

void null_proc()
{
}


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   start_choise;
static Panel_item   color_choise;
static Panel_item   level_choise;
static Pixfont   *smallfont;
static Pixfont   *bigfont;
static Pixfont   *bigboldfont;

Textwindow   *message_tw;
Textwindow   *moves_tw;
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  10
#define MOVES_WIDTH     15
#define MOVES_HEIGHT    26

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,      null_proc,
				 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,null_proc,
				  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, null_proc,
				   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,    null_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,    null_proc,
				       PANEL_ITEM_X,         ATTR_COL(1),
				       PANEL_ITEM_Y,         ATTR_ROW(4),
				       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(1),
				    PANEL_ITEM_Y,         ATTR_ROW(6),
				    0);

    start_choise = panel_create_item(panel, PANEL_CHOICE,
				     PANEL_LABEL_STRING,     "First move:   ",
				     PANEL_CHOICE_STRINGS,   "Human",
				                             "Computer",
				                             0,
				     PANEL_DISPLAY_LEVEL,    PANEL_ALL,
				     PANEL_FEEDBACK,         PANEL_INVERTED,
				     PANEL_LAYOUT,           PANEL_HORIZONTAL,
				     PANEL_NOTIFY_PROC,      null_proc,
				     PANEL_ITEM_X,           ATTR_COL(15),
				     PANEL_ITEM_Y,           ATTR_ROW(0),
				     0);

    color_choise = panel_create_item(panel, PANEL_CHOICE,
				     PANEL_LABEL_STRING,     "Human's Color:",
				     PANEL_CHOICE_STRINGS,   "White",
				                             "Black",
				                             0,
				     PANEL_DISPLAY_LEVEL,    PANEL_ALL,
				     PANEL_FEEDBACK,         PANEL_INVERTED,
				     PANEL_LAYOUT,           PANEL_HORIZONTAL,
				     PANEL_NOTIFY_PROC,      null_proc,
				     PANEL_ITEM_X,           ATTR_COL(15),
				     PANEL_ITEM_Y,           ATTR_ROW(2),
				     0);

    level_choise = 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(29), ATTR_COL(34), ATTR_COL(39),
				      ATTR_COL(29), ATTR_COL(34), ATTR_COL(39),
				      ATTR_COL(29), ATTR_COL(34), ATTR_COL(39),
				      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,      null_proc,
				     PANEL_ITEM_X,           ATTR_COL(15),
				     PANEL_ITEM_Y,           ATTR_ROW(4),
				     0);

    window_fit(frame);

    pw_text(canvas_pixwin(message_canvas), 0, -1 + 1 * FONTHEIGHT, PIX_SRC,
	    smallfont, "       M E S S A G E   A R E A");
    pw_text(canvas_pixwin(message_canvas), 0, -1 + 2 * FONTHEIGHT, PIX_SRC,
	    smallfont, "foobar");
    pw_text(canvas_pixwin(message_canvas), 0, -1 + 3 * FONTHEIGHT, PIX_SRC,
	    smallfont, "ny rad igen. Hur ser det ut?");
}



/* Event handler for the board window. */
/*ARGSUSED*/
void board_event_handler(canvas, event, arg)
Canvas  canvas;
Event   *event;
caddr_t arg;
{
    Position   pos;
    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)) {
		pos = POS(col, row);
		make_move(pos);
	    } else if (event_is_up(event))
		;
	    break;
	  default:
	    break;
	}
    }
}



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

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 pass_proc()
{
    make_move(PASS);
}

void quit_proc()
{
    exit(0);
}


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


void open_game()
{
    /* Open the pipes to the othello program and fork & exec. */
    /* Make stdout go directly to the othello program, and */
    /* stdin come directly from it. */
    pipe(tool2prog);
    pipe(prog2tool);
    if (fork() == 0) {
	/* Child */
	dup2(prog2tool[WRITEEND], 1); /* stdout of othello program */
	dup2(tool2prog[READEND], 0);  /* stdin of othello program  */
	close(prog2tool[READEND]);
	close(tool2prog[WRITEEND]);
	execl(OTHELLOPROG,
	      OTHELLOPROG, "-q", "-O", 0);
	perror("othellotool");
	exit(1);
    } else {
	/* Parent */
	dup2(prog2tool[READEND], 0);  /* stdin of othellotool  */
	dup2(tool2prog[WRITEEND], 1); /* stdout of othellotool */
	close(prog2tool[WRITEEND]);
	close(tool2prog[READEND]);
    }
}



void init_game()
{
    int   i;
    int   col;
    int   row;
    
    /* Reset the board. */
    /* Start with the border, ... */
    for (i = 0; i < 10; ++i) {
	board[POS(i, 0)] = BORDER;
	board[POS(i, 9)] = BORDER;
	board[POS(0, i)] = BORDER;
	board[POS(9, i)] = BORDER;
    }
    /* ...continue with the interiors... */
    for (col = 1; col < 9; ++col)
	for (row = 1; row < 9; ++row)
	    board[POS(col, row)] = EMPTY;
    /* ...and end with the starting position. */
    board[POS(4, 4)] = WHITE;
    board[POS(5, 4)] = BLACK;
    board[POS(5, 5)] = WHITE;
    board[POS(4, 5)] = BLACK;

    message_tw = textwindow_create(message_canvas, smallfont,
				   FONTWIDTH, FONTHEIGHT,
				   MESSAGE_WIDTH, MESSAGE_HEIGHT);
    moves_tw = textwindow_create(moves_canvas, smallfont,
				 FONTWIDTH, FONTHEIGHT,
				 MOVES_WIDTH, MOVES_HEIGHT);
    textwindow_print(moves_tw,"    M O V E S\n");
    if (i_start)
	textwindow_print(moves_tw, "     Me  You\n");
    else
	textwindow_print(moves_tw, "    You   Me\n");
    textwindow_set_scrollregion(moves_tw, 2, MOVES_HEIGHT - 1);
    status_tw = textwindow_create(status_canvas, smallfont,
				  FONTWIDTH, FONTHEIGHT,
				  MOVES_WIDTH, 6);
    textwindow_print(status_tw, "  S T A T U S");
    print_status(board);
    open_game();
}



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

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



void draw_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[POS(col, row)]);
}



void    print_move(pos)
Position   pos;
{
    static bool   left = TRUE;
    static int    moveno = 1;
    static char   movestr[3];
    static char   buf[20];

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

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


    left = !left;
}



void    print_status(board)
Board   board;
{
    static char   buf[100];
    
    sprintf(buf, "You have  %2d  \nI have    %2d  ",
	    count_pieces(board, yourcolor), 
	    count_pieces(board, mycolor));
    textwindow_putcur(status_tw, 2, 0);
    textwindow_print(status_tw, buf);
}



bool  do_move(board, pos, color)
Board      board;
Position   pos;
Color      color;
{
    Position   pos1;
    bool       legal_move;
    Color      piece;
    int        dir;
    int        i;

    legal_move = FALSE;
    for (i = 0; i < 8; ++i) {
	dir = all_directions[i];
	pos1 = pos + dir;
	if (board[pos1] != TURN(color))
	    continue;

	/* Check a direction for turnable pieces. */
	while (TRUE) {
	    pos1 += dir;
	    piece = board[pos1];
	    if (piece == color) {
		/* We have found the end of a turnable row!! */
		legal_move = TRUE;
		dir = -dir;
		pos1 += dir;
		
		/* turn the pieces on the way back */
		while (pos1 != pos) {
		    board[pos1] = color;
		    pos1 += dir;
		}
		break;
	    }
	    if (piece != TURN(color))
		break;
	}
    }

    if (legal_move)
	board[pos] = color;
    
    return legal_move;
}



int   count_pieces(board, color)
Board   board;
Color   color;
{
    int   i;
    int   num;

    num = 0;
    for (i = 0; i < 100; ++i)
	if (board[i] == color)
	    ++num;

    return num;
}



void make_move(pos)
Position   pos;
{
    static char   buffer[100];

    if (pos == PASS)
	printf("0\n");
    else
	printf("%c%c\n", COL(pos) + 'a' - 1, ROW(pos) + '0');
    fflush(stdout);

    /* Check if move was accepted by the othello program. */
    /* If so, wait for it's reply. */
    fgets(buffer, 100, stdin);
    if (strcmp(buffer, "\n") == 0) {
	if (pos != PASS) {
	    if (!do_move(board, pos, yourcolor)) {
		fprintf(stderr,
			"BUG: Move was not deemed correct by do_move()\n");
		exit(1);
	    }
	    draw_board(board);
	    print_status(board);
	}
	print_move(pos);

	pos = get_prog_move();
	if (pos != PASS) {
	    if (!do_move(board, pos, mycolor)) {
		fprintf(stderr, "Can't happen\n");
		exit(1);
	    }
	}
	print_move(pos);
	draw_board(board);
	print_status(board);
    }
}



Position   get_prog_move()
{
    static char   buffer[100];
    
    textwindow_putcur(status_tw, 6, 0);
    textwindow_print(status_tw, "Thinking...");
    fgets(buffer, 100, stdin);
    textwindow_putcur(status_tw, 6, 0);
    textwindow_print(status_tw, "           ");

    if (!strcmp(buffer, "pass\n"))
	return PASS;
    else
	return POS(buffer[0] - 'a' + 1, buffer[1] - '0');
}



void main_loop()
{
    Position   pos;

    if (i_start) {
	pos = get_prog_move();
	if (pos == PASS || !do_move(board, pos, mycolor)) {
	    fprintf(stderr, "Can't happen\n");
	    exit(1);
	}
	print_move(pos);
    }
    draw_board(board);
    print_status(board);
    window_main_loop(frame);
}


main(argc, argv)
int    argc;
char   *argv[];
{
    init_windows(argc, argv);
    init_game();
    draw_board(board);
    main_loop();
    exit(0);
}
