/*
 * File: curses.c
 *
 * This file contains the functions to let the user play GNU Othello 
 * on a terminal using curses.
 *
 * Author:  Inge Wallin
 */

#include <stdio.h>
#include <string.h>
#include <curses.h>
#ifdef __GNUC__
/*#include "clib.h"*/
#endif
#include "textwindow.h"
#include "othello.h"
#include "global.h"
#include "io.h"
#include "version.h"


/* These should be declared in curses.h but aren't */

extern wmove();
extern waddstr();
extern waddch();
extern printw();
extern wrefresh();
extern endwin();


/* ================================================================ */
/*        Coordinates of a number of things on the screen.          */

#define BOARDSTART_ROW     4
#define BOARDSTART_COL     4
#define INFOSTART_ROW      4
#define INFOSTART_COL     44
#define MESSAGESTART_ROW  15
#define MESSAGESTART_COL  44
#define MESSAGE_WIDTH     37
#define MESSAGE_HEIGHT     9
#define MOVESSTART_ROW     1
#define MOVESSTART_COL    63
#define MOVES_WIDTH       15
#define MOVES_HEIGHT      13


/* ================================================================ */
/*         Functions that work together with textwindow.[ch]        */


typedef struct {
    int   startrow;
    int   startcol;
} Subwindow;


void
window_write_char(win, row, col, ch)
Subwindow  * win;
int          row;
int          col;
int          ch;
{
    mvaddch(win->startrow + row, win->startcol + col, ch);
}


void
window_write_text(win, row, col, str)
Subwindow  * win;
int          row;
int          col;
char       * str;
{
    move(win->startrow + row, win->startcol + col);
    printw("%s", str);
}


void
window_flush()
{
    refresh();
}


/* ================================================================ */
/*                  Local variables in curses.c                     */


static Subwindow   message_window;
static Subwindow   moves_window;

static Textwindow  * message_tw;
static Textwindow  * moves_tw;


/* ================================================================ */
/*                        Internal functions                        */


static void
cleanup_screen()
{
    move(23, 0);
    refresh();
    endwin();
}


/* 
 * This function displays the color of both players.
 */

static void
print_color()
{
    mvaddstr(INFOSTART_ROW, INFOSTART_COL, "My color:");
    mvaddch(INFOSTART_ROW, INFOSTART_COL + 12, mycolor == WHITE ? 'O' : 'X');
    mvaddstr(INFOSTART_ROW + 1, INFOSTART_COL, "Your color:");
    mvaddch(INFOSTART_ROW + 1, INFOSTART_COL + 12, 
	    mycolor == WHITE ? 'X' : 'O');
    refresh();
}



/* 
 * This function displays the level of play.
 */

static void
print_level()
{
    move(INFOSTART_ROW + 3, INFOSTART_COL);
    printw("Level: %d", level);
    refresh();
}



/* ================================================================ */
/*                     Initialization functions                     */


/*
 * The following functions are the ones that are called from the
 * Othello program.  The ones above are all local to sunview.c
 */


/*
 * Init the sunview interface. This includes creating all the 
 * subwindows, initializing the headlines in the text windows,
 * drawing the starting position of the board and drawing
 * the border and legend around the board.
 *
 * A few default values are also displayed in the choises in
 * the panel.
 */

void
init_io(argc, argv)
int   * argc;
char  * argv[];
{
    State   state;

    /* Initialize curses. */
    initscr();
    if (LINES < 24 || COLS < 80) {
	fprintf(stderr, "%s %s", "Sorry, you need at least 24", 
		"rows and 80 columns to run GNU Othello.\n");
	endwin();
	exit(1);
    }
    noecho();

    mvaddstr(1, 28, VERSIONSTRING);

    message_window.startrow = MESSAGESTART_ROW;
    message_window.startcol = MESSAGESTART_COL;
    message_tw = textwindow_create( (void *) &message_window,
				    MESSAGE_WIDTH, MESSAGE_HEIGHT);
    textwindow_set_scrollregion(message_tw, 1, MESSAGE_HEIGHT - 1);

    moves_window.startrow = MOVESSTART_ROW;
    moves_window.startcol = MOVESSTART_COL;
    moves_tw = textwindow_create( (void *) &moves_window,
				  MOVES_WIDTH, MOVES_HEIGHT);
    textwindow_set_scrollregion(moves_tw, 2, MOVES_HEIGHT - 1);

    /* Draw the initial board position on the board canvas and */
    /* write the legend around it. */
    resetstate(&state);
    print_state(&state);
    print_color();
    print_level();
/*    init_io_for_one_game();*/
}



/*
 * This functions is called once before each game starts. It empties
 * the text subwindows and writes the headlines in them.
 */

void
init_io_for_one_game()
{
    int   i;

    /* Create the message text window. */
    textwindow_putcur(message_tw, 0, 0);
    textwindow_print(message_tw, "     M E S S A G E   A R E A\n");
    for (i = 0; i < MESSAGE_HEIGHT - 1; ++i)
	textwindow_print(message_tw, "                                     \n");
    textwindow_putcur(message_tw, 1, 0);

    /* Create the moves text window. */
    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");
}


/* ================================================================ */
/*                          Output functions                        */


/*
 * Draw the board and configuration in BOARD on the screen.
 */

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

    for (i = 0; i < 9; ++i) {
	mvaddstr(BOARDSTART_ROW + i * 2, BOARDSTART_COL,
		 "+---+---+---+---+---+---+---+---+");
	if (i < 8) {
	    mvaddch(BOARDSTART_ROW + i * 2 + 1, BOARDSTART_COL - 2, i + '1');
	    addstr(" |   |   |   |   |   |   |   |   | ");
	    addch(i + '1');
	}
    }
    mvaddstr(BOARDSTART_ROW - 1, BOARDSTART_COL + 2,
	     "A   B   C   D   E   F   G   H");
    mvaddstr(BOARDSTART_ROW + 17, BOARDSTART_COL + 2,
	     "A   B   C   D   E   F   G   H");

    for (col = 1; col < 9; ++col) {
	for (row = 1; row < 9; ++row) {
	    move(BOARDSTART_ROW + row * 2 - 1, BOARDSTART_COL + col * 4 - 2);
	    if (mycolor == BLACK)
		addch(" XO"[board[SQ(col, row)]]);
	    else
		addch(" OX"[board[SQ(col, row)]]);
	}
    }
    
    refresh();
}



/*
 * Print the outcome of the game in the message window.
 */

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

    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);
    }

    print_message(buff);
}



/*
 * Print the move with number MOVENO in the moves window.
 */

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

    if (moveno == 1) {
	/* Clean the first row in case we just undid a move. */
	textwindow_putcur(moves_tw, 2, 0);
	textwindow_print(moves_tw, "              ");
	last_moveno = 0;

	textwindow_putcur(moves_tw, 2, 0);
    }

    /* If this is the first move after an undo, delete all */
    /* rows that were undone. */
    while (last_moveno != moveno - 1) {
	if (last_moveno & 1)
	    textwindow_print(moves_tw, "\r              \r");
	else
	    textwindow_putcur(moves_tw, 
			      textwindow_get_currow(moves_tw) - 1, 7);
	--last_moveno;
    }

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

    /* If moveno is odd, this is blacks move. */
    if (moveno & 1) {
	sprintf(buf, "%2d.%4s", moveno, movestr);
	textwindow_print(moves_tw, buf);
    } else {
	sprintf(buf,  " - %s\n", movestr);
	textwindow_print(moves_tw, buf);
    }

    last_moveno = moveno;
}



/*
 * Print the computers move.
 */

void
print_my_move(moveno, sq)
int      moveno;
Square   sq;
{
    print_move(moveno, sq);
}



/*
 * Print the humans move.
 */

void
print_your_move(moveno, sq)
int      moveno;
Square   sq;
{
    print_move(moveno, sq);
}



/*
 * Print a message to the user in the message window.
 */

void
print_message(msg)
char  * msg;
{
    textwindow_print(message_tw, msg);
}



/*
 * Print the score in STATE in the status window and update the 
 * image of the board.
 */

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

    count_pieces(state, &my, &yours);
    move(INFOSTART_ROW + 5, INFOSTART_COL);
    printw("You have:  %2d", yours);
    move(INFOSTART_ROW + 6, INFOSTART_COL);
    printw("I have:    %2d", my);

    refresh();
}



/*
 * Show the user whose turn it is in the status window.
 */

void
show_status(status)
int   status;
{
    move(INFOSTART_ROW + 8, INFOSTART_COL);
    switch (status) {
      case MYMOVE:
	printw("Thinking...");
	break;

      case YOURMOVE:
	printw("Your move: ");
	break;

      case NOGAME:
	printw("           ");
	break;

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


/* ================================================================ */
/*                         Input functions                          */


/*
 * Wait for the user to make a move and return it if it was legal.
 * If not, say so in the message window and wait for another one.
 */

Square
get_users_move(state)
State   *state;
{
    Sqlist   legal_moves;
    int      num_moves;
    int      movesquare;

    get_legal_moves(state, YOURCOLOR, &legal_moves, &num_moves);
    while (TRUE) {
	if (num_moves == 0)
	    movesquare = PASS;
	else
	    movesquare = SQ((random() % 8) + 1, (random() % 8 + 1));

	/* Process accumulated events. */
	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");
	}
    } 
}


/*
 * Wait for the user to press the `New Game' button.
 */

void
start_game()
{
/*    while (!start_game_flag) {
	usleep(100000);
    }*/
}



/*
 * Return TRUE since don't want to stop the program any other way than
 * the user pressing the `Quit' Button.
 */

bool
another_game()
{
    print_message("Another game (y/n)?\n");

    cleanup_screen();
    return FALSE;
}
