/*
 * Othello
 *
 * Copyright (C) Evan Harris, 1994, 1995.
 *
 * Permission is granted to freely redistribute and modify this code,
 * providing the author(s) get credit for having written it.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <vga.h>
#if !defined(NOMOUSE)
#include <vgamouse.h>
#endif

#include "othello.h"
#include "otsquare.h"
#include "vga16.h"
#include "mouse.h"
#include "key.h"


#define SCREENMODE G640x480x16
#define SCREENWIDTH 640
#define SCREENHEIGHT 480

#define SQUARESIZE 48
#define BOARDLEFT 0
#define BOARDRIGHT (BOARDLEFT + 8 * SQUARESIZE)
#define BOARDTOP 50
#define BOARDBOTTOM (BOARDTOP + 8 * SQUARESIZE)

#define VERSION "OTHELLO"
#define VERSIONLEFT (SCREENWIDTH - 68)
#define VERSIONBOTTOM 200
#define VERSIONNUM "v1.1"
#define VERSIONNUMLEFT (VERSIONLEFT + 12)
#define VERSIONNUMBOTTOM (VERSIONBOTTOM + 16)

#define QUITLEFT (SCREENWIDTH - 80)
#define QUITBOTTOM 76
#define QUITTOP (QUITBOTTOM - 15)
#define QUITRIGHT (QUITLEFT + 79)

#define NEWGAMELEFT QUITLEFT
#define NEWGAMEBOTTOM 48
#define NEWGAMETOP (NEWGAMEBOTTOM - 15)
#define NEWGAMERIGHT (NEWGAMELEFT + 79)

#define WINLEFT (SCREENWIDTH - 68)
#define WINBOTTOM 280
#define SCORELEFT (WINLEFT + 8)
#define SCOREBOTTOM (WINBOTTOM + 16)
#define TIMELEFT (WINLEFT + 8)
#define TIMEBOTTOM WINBOTTOM
#define THINKLEFT (WINLEFT - 4)
#define THINKBOTTOM WINBOTTOM

#define MOUSEKEYMOVE 8

#define PALETTESIZE 8

int palette[PALETTESIZE * 3] = {
    0x00, 0x20, 0x00,		/* dark green */
    0x00, 0x30, 0x00,		/* green */
    0x00, 0x00, 0x00,		/* black */
    0x3f, 0x3f, 0x3f,		/* white */
    0x20, 0x20, 0x20,		/* grey */
    0x00, 0x00, 0x30,		/* blue */
    0x30, 0x00, 0x00,		/* red */
    0x3f, 0x3f, 0x00,		/* yellow */
};

#define TEXTFG 3
#define TEXTBG 0
#define BUTTONFG 2
#define BUTTONBG 4
#define BOARDFG 0
#define BOARDBG 1
#define THINKFG 7
#define THINKBG 0
#define MOUSEFG 6
#define MOUSEDARKFG 7

int entryfg[3] = {
    1, 2, 3,
};

#ifndef MOUSESAMPLERATE
#define MOUSESAMPLERATE MOUSE_DEFAULTSAMPLERATE
#endif

#define USLEEP_TIME 30000


void
NewGame()
{
    vga16_text(WINLEFT, WINBOTTOM, "        ", TEXTFG, TEXTBG);
    vga16_text(SCORELEFT, SCOREBOTTOM, "     ", TEXTFG, TEXTBG);
}


void
InitDisplay(int reverse)
{
    int i, l, t;

    vga_disabledriverreport();
    vga_init();
#if !defined(USEMOUSEFUNCS) && !defined(NOMOUSE)
    vga_setmousesupport(1);
#endif

    if (vga_setmode(SCREENMODE) != 0) {
	fprintf(stderr, "Mode %s not available!\n",
		vga_getmodename(SCREENMODE));
	exit(1);
    }

#if defined(USEMOUSEFUNCS) && !defined(NOMOUSE)
    mouse_init("/dev/mouse", vga_getmousetype(), MOUSESAMPLERATE);
    mouse_setxrange(0, SCREENWIDTH - 1);
    mouse_setyrange(0, SCREENHEIGHT - 1);
    mouse_setwrap(MOUSE_NOWRAP);
#endif

    vga16_init();

    vga_setpalvec(0, PALETTESIZE, &palette[0]);

    vga16_text(NEWGAMELEFT, NEWGAMEBOTTOM, " NEW GAME ", BUTTONFG, BUTTONBG);
    vga16_text(QUITLEFT, QUITBOTTOM, "   QUIT   ", BUTTONFG, BUTTONBG);
    vga16_text(VERSIONLEFT, VERSIONBOTTOM, VERSION, TEXTFG, TEXTBG);
    vga16_text(VERSIONNUMLEFT, VERSIONNUMBOTTOM, VERSIONNUM, TEXTFG, TEXTBG);

    NewGame();

    if (reverse) {
	for (i = 0; i < 30; i++) {
	    for (l = 0; l < SQUARESIZE; l++) {
		if (squarelines[i][l] == entryfg[1]) {
		    squarelines[i][l] = entryfg[2];
		} else if (squarelines[i][l] == entryfg[2]) {
		    squarelines[i][l] = entryfg[1];
		}
	    }
	}
	t = entryfg[1];
	entryfg[1] = entryfg[2];
	entryfg[2] = t;
    }
}


void
EndDisplay()
{
    vga_setmode(TEXT);
#if defined(USEMOUSEFUNCS) && !defined(NOMOUSE)
    mouse_close();
#endif    
}


static int oldx = -1, oldy, oldcolour[40];

void
PutBoardStart()
{
    if (oldx != -1) {
	RestoreUnderMousePointer(oldx, oldy, SCREENWIDTH, SCREENHEIGHT,
				 oldcolour);
    }
}


void
PutBoardEnd()
{
    if (oldx != -1) {
	SaveUnderMousePointer(oldx, oldy, SCREENWIDTH, SCREENHEIGHT,
			      oldcolour);
	RenderMousePointer(oldx, oldy, MOUSEFG, MOUSEDARKFG,
			   SCREENWIDTH, SCREENHEIGHT);
    }
}


void
PutSquare(int x, int y, int entry)
{
    int l;

    for (l = 0; l < SQUARESIZE; l++) {
	vga16_drawscansegment(squarelines[square[entry][l]],
			      BOARDLEFT + x * SQUARESIZE,
			      BOARDTOP + y * SQUARESIZE + l, SQUARESIZE);
    }
}


void
ShowWin(int who, int winscore, int losescore)
{
    char str[10];
    
    if (who == 1) {
	vga16_text(WINLEFT, WINBOTTOM, " I WIN ", entryfg[who], TEXTBG);
    } else {
	vga16_text(WINLEFT, WINBOTTOM, "YOU WIN", entryfg[who], TEXTBG);
    }
    sprintf(str, "%2d/%2d", winscore, losescore);
    vga16_text(SCORELEFT, SCOREBOTTOM, str, entryfg[who], TEXTBG);
}


void
ShowDraw(int score)
{
    char str[10];

    vga16_text(WINLEFT, WINBOTTOM, " A TIE ", TEXTFG, TEXTBG);
    sprintf(str, "%2d/%2d", score, score);
    vga16_text(SCORELEFT, SCOREBOTTOM, str, TEXTFG, TEXTBG);
}


void
ThinkingOn()
{
    vga16_text(THINKLEFT, THINKBOTTOM, "THINKING", THINKFG, THINKBG);
    vga_runinbackground(1);
}


void
ThinkingOff()
{
    while (!vga_oktowrite()) {
	usleep(100000);		/* 1/10 sec */
    }
    vga_runinbackground(0);
    vga16_text(THINKLEFT, THINKBOTTOM, "        ", THINKFG, THINKBG);
}


void
ShowTime(double t)
{
    char buf[10];

    sprintf(buf, "%.2fs", t);
    vga16_text(TIMELEFT - 4 * (strlen(buf) - 5), TIMEBOTTOM, buf,
	       THINKFG, THINKBG);
}


void
MoveMousePointer(int x, int y)
{
    if (x != oldx || y != oldy) {
	if (oldx != -1) {
	    RestoreUnderMousePointer(oldx, oldy, SCREENWIDTH, SCREENHEIGHT,
				     oldcolour);
	}
	SaveUnderMousePointer(x, y, SCREENWIDTH, SCREENHEIGHT, oldcolour);
	RenderMousePointer(x, y, MOUSEFG, MOUSEDARKFG,
			   SCREENWIDTH, SCREENHEIGHT);
	oldx = x;
	oldy = y;
    }
}


int
ParseMousePosition(int x, int y)
{
    if (x >= NEWGAMELEFT && x <= NEWGAMERIGHT
	&& y >= NEWGAMETOP && y <= NEWGAMEBOTTOM) {
	return NEWGAME;
    } else if (x >= QUITLEFT && x <= QUITRIGHT
	       && y >= QUITTOP && y <= QUITBOTTOM) {
	return QUIT;
    } else if ((x - BOARDLEFT) / SQUARESIZE >= 0
	       && (y - BOARDTOP) / SQUARESIZE >= 0) {
	return (x - BOARDLEFT) / SQUARESIZE * 8 + (y - BOARDTOP) / SQUARESIZE;
    }
    
    return UNKNOWN;
}


int
GetMove()
{
    int move = UNKNOWN, key;

#if !defined(NOMOUSE)    
    int x, y, button;
    
    if (oldx == -1) {
	x = mouse_getx();
	y = mouse_gety();
	MoveMousePointer(x, y);
    }
#else
    MoveMousePointer(0, 0);
#endif    
    
    while (move == UNKNOWN) {
	usleep(USLEEP_TIME);	/* don't chew up as much CPU */
	
#if !defined(NOMOUSE)
	if (mouse_update()) {
	    x = mouse_getx();
	    y = mouse_gety();
	    button = mouse_getbutton();

	    MoveMousePointer(x, y);

	    if (button & MOUSE_LEFTBUTTON) {
		move = ParseMousePosition(x, y);
	    }
	}
#endif

	if ((key = key_getkey()) != -1 ) {
	    switch (key) {
	      case 'n':
	      case 'N':
		move = NEWGAME;
		break;
	      case 'q':
	      case 'Q':
		move = QUIT;
		break;
	      case KEY_CURSORUP:
		if (oldy >= MOUSEKEYMOVE) {
		    MoveMousePointer(oldx, oldy - MOUSEKEYMOVE);
		}
		break;
	      case KEY_CURSORDOWN:
		if (oldy < SCREENHEIGHT - MOUSEKEYMOVE) {
		    MoveMousePointer(oldx, oldy + MOUSEKEYMOVE);
		}
		break;
	      case KEY_CURSORLEFT:
		if (oldx >= MOUSEKEYMOVE) {
		    MoveMousePointer(oldx - MOUSEKEYMOVE, oldy);
		}
		break;
	      case KEY_CURSORRIGHT:
		if (oldx < SCREENWIDTH - MOUSEKEYMOVE) {
		    MoveMousePointer(oldx + MOUSEKEYMOVE, oldy);
		}
		break;
	      case '\n':
		move = ParseMousePosition(oldx, oldy);
		break;
	      default:
		break;
	    }
	}
    }

    return move;
}


void
Poll()
{
    int key, move;
#if !defined(NOMOUSE)
    int x, y, button;
#endif    
    
    if (!vga_oktowrite()) {
	return;
    }
    
#if !defined(NOMOUSE)
    if (mouse_update()) {
	x = mouse_getx();
	y = mouse_gety();
	button = mouse_getbutton();

	MoveMousePointer(x, y);

	if (button & MOUSE_LEFTBUTTON) {
	    move = ParseMousePosition(x, y);
	    if (move == QUIT) {
		EndDisplay();
		exit(0);
	    }
	}
    }
#endif

    if ((key = key_getkey()) != -1 ) {
	switch (key) {
	  case 'q':
	  case 'Q':
	    EndDisplay();
	    exit(0);
	  case KEY_CURSORUP:
	    if (oldy >= MOUSEKEYMOVE) {
		MoveMousePointer(oldx, oldy - MOUSEKEYMOVE);
	    }
	    break;
	  case KEY_CURSORDOWN:
	    if (oldy < SCREENHEIGHT - MOUSEKEYMOVE) {
		MoveMousePointer(oldx, oldy + MOUSEKEYMOVE);
	    }
	    break;
	  case KEY_CURSORLEFT:
	    if (oldx >= MOUSEKEYMOVE) {
		MoveMousePointer(oldx - MOUSEKEYMOVE, oldy);
	    }
	    break;
	  case KEY_CURSORRIGHT:
	    if (oldx < SCREENWIDTH - MOUSEKEYMOVE) {
		MoveMousePointer(oldx + MOUSEKEYMOVE, oldy);
	    }
	    break;
	  case '\n':
	    move = ParseMousePosition(oldx, oldy);
	    if (move == QUIT) {
		EndDisplay();
		exit(0);
	    }
	    break;
	  default:
	    break;
	}
    }
}
