/*
 * Oh Hell!
 *
 * Copyright (C) Evan Harris, 1991, 1993, 1994
 *
 * 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 <vga.h>
#include <vgamouse.h>

#include "vga16.h"
#include "ohhell.h"


#define OH_SCREENMODE G640x480x16
#define OH_SCREENWIDTH 640
#define OH_SCREENHEIGHT 480

#define OH_TEXTFG 1
#define OH_TEXTBG 0
#define OH_BUTTONFG 2
#define OH_BUTTONBG 6
#define OH_MOUSEFG 5
#define OH_MOUSEDARKFG 2

#define NUMCARDIMAGES 52
#define CARDACTUALWIDTH 53
#define CARDIMAGEWIDTH 56
#define CARDIMAGEHEIGHT 80

#define VERSION "OH HELL!  v1.1"

#ifndef CARDSFILE
#define CARDSFILE "Cards56x80"
#endif

#define CARDTOIMAGE(card)	(card % NUMCARDIMAGES)

#define BITWIDTH CARDIMAGEWIDTH /* multiple of 8 */
#define BITHEIGHT CARDIMAGEHEIGHT
#define BITSHOWLEFT 24		/* multiple of 8 */

#define OH_QUITLEFT (OH_SCREENWIDTH - 88)
#define OH_QUITRIGHT (OH_QUITLEFT + 80)
#define OH_QUITBOTTOM 76
#define OH_QUITTOP (OH_QUITBOTTOM - 16)
#define OH_NEWGAMEBOTTOM 48
#define OH_NEWGAMETOP (OH_NEWGAMEBOTTOM - 16)

#define OH_PALETTESIZE 9

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

#define MIN(a, b) ((a) < (b) ? (a) : (b))
static void LoadCards(char *file);
static long GetMouseButton(void);
static void WhileMouseButton(void);

static unsigned char ***imageCard;
static unsigned char *blankLine;


void
InitDisplay(int argc, char **argv)
{
    vga_disabledriverreport();
    vga_init();
    vga_setmousesupport(1);
    
    if (vga_setmode(OH_SCREENMODE) != 0) {
	fprintf(stderr, "Mode %s not available!\n",
		vga_getmodename(OH_SCREENMODE));
	exit(1);
    }

    vga16_init();

    LoadCards(CARDSFILE);

    blankLine = (unsigned char *)calloc(OH_SCREENWIDTH, sizeof(unsigned char));
    if (blankLine == NULL) {
	fprintf(stderr, "Error: cannot get memory for blankLine\n");
	exit(1);
    }

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

    vga16_text(16, OH_SCREENHEIGHT - 56, "SCORES", OH_TEXTFG, OH_TEXTBG);
    vga16_text(OH_SCREENWIDTH - 110, OH_SCREENHEIGHT - 56, "BIDS",
	       OH_TEXTFG, OH_TEXTBG);
    vga16_text(OH_SCREENWIDTH - 52, OH_SCREENHEIGHT - 56, "TRICKS",
	       OH_TEXTFG, OH_TEXTBG);

    vga16_text(OH_QUITLEFT, OH_QUITBOTTOM, "   QUIT   ",
	       OH_BUTTONFG, OH_BUTTONBG);
    vga16_text(OH_QUITLEFT, OH_NEWGAMEBOTTOM, " NEW GAME ",
	       OH_BUTTONFG, OH_BUTTONBG);

    vga16_text(OH_SCREENWIDTH - 120, 120, VERSION, OH_TEXTFG, OH_TEXTBG);
}


static void
LoadCards(char *file)
{
    int i, j, k, l, c, colour;
    FILE *f;

    f = fopen(file, "r");
    if (f == NULL) {
	fprintf(stderr, "Cannot find '%s'\n", file);
	exit(1);
    }

    imageCard = (unsigned char ***)malloc(NUMCARDIMAGES
					  * sizeof(unsigned char **));
    if (imageCard == NULL) {
	fprintf(stderr, "Error: cannot get memory for imageCard\n");
	exit(1);
    }
    for (i = 0; i < NUMCARDIMAGES; i++) {
	imageCard[i] = (unsigned char **)malloc(CARDIMAGEHEIGHT
						* sizeof(unsigned char *));
	if (imageCard == NULL) {
	    fprintf(stderr, "Error: cannot get memory for imageCard[%d]\n",
		    i);
	    exit(1);
	}
	for (j = 0; j < CARDIMAGEHEIGHT; j++) {
	    imageCard[i][j] = (unsigned char *)malloc(CARDIMAGEWIDTH
						      * sizeof(unsigned char));
	    if (imageCard[i][j] == NULL) { 
		fprintf(stderr,
			"Error: cannot get memory for imageCard[%d][%d]\n",
			i, j);
		exit(1);
	    }
	    
	    if (SUIT(i) == SPADES || SUIT(i) == CLUBS) {
		colour = 2;
	    } else {
		colour = 3;
	    }
	    if (TYPE(i) < JACK) {
		for (k = 0; k < CARDIMAGEWIDTH / 8; k++) {
		    if ((c = getc(f)) == EOF) {
			fprintf(stderr, "Unexpected EOF in '%s'\n", file);
			exit(1);
		    }
		    for (l = 0; l < 8; l++) {
			imageCard[i][j][8 * k + l] =
			    (c & 1 << (7 - l)) ? colour : 1;
		    }
		}
		for (k = CARDACTUALWIDTH; k < CARDIMAGEWIDTH; k++) {
		    imageCard[i][j][k] = 0;
		}
	    } else {
		for (k = 0; k < CARDIMAGEWIDTH / 2; k++) {
		    if ((c = getc(f)) == EOF) {
			fprintf(stderr, "Unexpected EOF in '%s'\n", file);
			exit(1);
		    }
		    imageCard[i][j][2 * k] = (unsigned char)c >> 4;
		    imageCard[i][j][2 * k + 1] = (unsigned char)c & 0xf;
		}
	    }
	}
    }

    fclose(f);
}


static int cardPosLeft[4] = {	/* S, W, N, E */
    ((OH_SCREENWIDTH - BITWIDTH) / 2) & ~(0x7),
    ((OH_SCREENWIDTH - BITWIDTH) / 8) & ~(0x7),
    ((OH_SCREENWIDTH - BITWIDTH) / 2) & ~(0x7),
    (7 * (OH_SCREENWIDTH - BITWIDTH) / 8) & ~(0x7),
};
static int cardPosTop[4] = {	/* S, W, N, E */
    OH_SCREENHEIGHT - 2 * BITHEIGHT - 10,
    (OH_SCREENHEIGHT - 2 * BITHEIGHT - 10) / 2,
    0,
    (OH_SCREENHEIGHT - 2 * BITHEIGHT - 10) / 2,
};

void
ShowPlay(unsigned char card, unsigned char player)
{
    int line;
    
    for (line = 0; line < BITHEIGHT; line++) {
	vga16_drawscansegment(imageCard[CARDTOIMAGE(card)][line],
			      cardPosLeft[player], cardPosTop[player] + line,
			      BITWIDTH);
    }
}


void
RemovePlays()
{
    int player, line;
    
    for (player = 0; player < 4; player++) {
	for (line = 0; line < BITHEIGHT; line++) {
	    vga16_drawscansegment(blankLine, cardPosLeft[player],
				  cardPosTop[player] + line, BITWIDTH);
	}
    }
}


static int numInDeal;


void
ShowHand(unsigned char *hand, unsigned char num)
{
    int left, top, width, line, i;

    numInDeal = num;

    left = ((OH_SCREENWIDTH - ((num - 1) * BITSHOWLEFT + BITWIDTH)) / 2)
	& ~0x7;
    top = OH_SCREENHEIGHT - BITHEIGHT;
    for (line = 0; line < BITHEIGHT; line++) {
	vga16_drawscansegment(blankLine, left, top + line,
			      BITWIDTH + (num - 1) * BITSHOWLEFT);
    }

    left += (num - 1) * BITSHOWLEFT;
    width = BITWIDTH;
    for (i = num - 1; i >= 0; i--) {
	if (hand[i] != NOCARD) {
	    for (line = 0; line < BITHEIGHT; line++) {
		vga16_drawscansegment(imageCard[CARDTOIMAGE(hand[i])][line],
				      left, top + line, width);
	    }
	    width = BITSHOWLEFT;
	} else {
	    width += BITSHOWLEFT;
	    if (width > BITWIDTH) {
		width = BITWIDTH;
	    }
	}
	left -= BITSHOWLEFT;
    }
}


static int winsPosLeft[4] = {	/* S, W, N, E */
    OH_SCREENWIDTH - 40,
    OH_SCREENWIDTH - 56,
    OH_SCREENWIDTH - 40,
    OH_SCREENWIDTH - 24,
};
static int winsPosTop[4] = {	/* S, W, N, E */
    OH_SCREENHEIGHT - 4,
    OH_SCREENHEIGHT - 20,
    OH_SCREENHEIGHT - 36,
    OH_SCREENHEIGHT - 20,
};

void
ShowWins(unsigned char *wins)
{
    char s[4];
    int i;

    for (i = 0; i < 4; i++) {
	sprintf(s, "%2d", wins[i]);
	vga16_text(winsPosLeft[i], winsPosTop[i], s, OH_TEXTFG, OH_TEXTBG);
    }
}


static int bidPosLeft[4] = {	/* S, W, N, E */
    OH_SCREENWIDTH - 106,
    OH_SCREENWIDTH - 122,
    OH_SCREENWIDTH - 106,
    OH_SCREENWIDTH - 90,
};
static int bidPosTop[4] = {	/* S, W, N, E */
    OH_SCREENHEIGHT - 4,
    OH_SCREENHEIGHT - 20,
    OH_SCREENHEIGHT - 36,
    OH_SCREENHEIGHT - 20,
};

void
ShowBid(unsigned char bid, unsigned char location)
{
    char s[4];

    sprintf(s, "%2d", bid);
    vga16_text(bidPosLeft[location], bidPosTop[location], s,
	       OH_TEXTFG, OH_TEXTBG);
}


void
RemoveBids()
{
    int i;
    
    for (i = 0; i < 4; i++) {
	vga16_text(bidPosLeft[i], bidPosTop[i], "  ", OH_TEXTFG, OH_TEXTBG);
    }
}


static int scorePosLeft[4] = {	/* S, W, N, E */
    24,
    0,
    24,
    48,
};
static int scorePosTop[4] = {	/* S, W, N, E */
    OH_SCREENHEIGHT - 4,
    OH_SCREENHEIGHT - 20,
    OH_SCREENHEIGHT - 36,
    OH_SCREENHEIGHT - 20,
};

void
ShowScores(unsigned char *scores)
{
    int i;
    char s[4];

    for (i = 0; i < 4; i++) {
	sprintf(s, "%3d", scores[i]);
	vga16_text(scorePosLeft[i], scorePosTop[i], s, OH_TEXTFG, OH_TEXTBG);
    }
}


void
ShowTrumps(unsigned char card)
{
    int left, top, line;

    left = ((OH_SCREENWIDTH - BITWIDTH) / 2) & ~0x7;
    top = (OH_SCREENHEIGHT - 2 * BITHEIGHT - 10) / 2;

    for (line = 0; line < BITHEIGHT; line++) {
	vga16_drawscansegment(imageCard[card][line], left, top + line,
			      BITWIDTH);
    }
}


void
RemoveTrumps()
{
    int left, top, line;

    left = ((OH_SCREENWIDTH - BITWIDTH) / 2) & ~0x7;
    top = (OH_SCREENHEIGHT - 2 * BITHEIGHT - 10) / 2;

    for (line = 0; line < BITHEIGHT; line++) {
	vga16_drawscansegment(blankLine, left, top + line, BITWIDTH);
    }
}


short
GetPlayerBid(unsigned char max)
{
    char s[6];
    int i, c, left, x, y, len;
    
    vga16_text(10, 20, "Enter bid:", OH_TEXTFG, OH_TEXTBG);
    left = 100;
    for (i = 0; i <= max; i++) {
	sprintf(s, " %d ", i);
	vga16_text(left, 20, s, OH_BUTTONFG, OH_BUTTONBG);
	left += strlen(s) * 8 + 8;
    }
	
    for (c = NOCARD; c == NOCARD;) {
	c = GetMouseButton();
	if (c >= 0) {
	    x = (c >> 9);
	    y = (c & 0x1ff);
	    if (y > 4 && y <= 20) {
		c = 0;
		x -= 100;
		while (x >= 0) {
		    len = (c < 10 ? 3 : 4) * 8;
		    if (x < len) {
			break;
		    }
		    c++;
		    x -= len + 8;
		}
		if (x < 0 || c > max) {
		    c = NOCARD;
		}
	    } else {
		c = NOCARD;
	    }
	}
    }

    WhileMouseButton();

    for (i = 0; i < 16; i++) {
	vga16_drawscanline(20 - i, blankLine);
    }

    return c;
}


short
GetCmd()
{
    int c, i, x, y, len;

    for (c = NOCARD; c == NOCARD;) {
	c = GetMouseButton();
	if (c >= 0) {
	    x = (c >> 9);
	    y = (c & 0x1ff);
	    if (y >= OH_SCREENHEIGHT - BITHEIGHT && y < OH_SCREENHEIGHT) {
		c = 0;
		x -= (((OH_SCREENWIDTH
			- ((numInDeal - 1) * BITSHOWLEFT + BITWIDTH)) / 2)
		      & ~0x7);
		while (x >= 0 && c < numInDeal) {
		    if (c == numInDeal - 1) {
			len = BITWIDTH;
		    } else {
			len = BITSHOWLEFT;
			i = c + 1;
			while (i < numInDeal && hand[0][i] == NOCARD) {
			    if (i == numInDeal - 1) {
				len += BITWIDTH;
			    } else {
				len += BITSHOWLEFT;
			    }
			    i++;
			}
			len = MIN(len, CARDACTUALWIDTH);
		    }
		    if (x < len && hand[0][c] != NOCARD) {
			break;
		    }
		    c++;
		    x -= BITSHOWLEFT;
		}
		if (x < 0 || c >= numInDeal) {
		    c = NOCARD;
		}
	    } else {
		c = NOCARD;
	    }
	}
    }

    WhileMouseButton();

    return c;
}


short
WaitForKey()
{
    long c;

    c = GetMouseButton();
    WhileMouseButton();
    if (c < 0) {
	return (short)c;
    } else {
	return CONTINUE;
    }
}


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

static long
GetMouseButton()
{
    int x, y, button, key;

    x = mouse_getx();
    y = mouse_gety();
    if (x != oldx || y != oldy) {
	if (oldx != -1) {
	    RestoreUnderMousePointer(oldx, oldy, oldcolour);
	}
	SaveUnderMousePointer(x, y, oldcolour);
	RenderMousePointer(x, y, OH_MOUSEFG, OH_MOUSEDARKFG);
	oldx = x;
	oldy = y;
    }
	
    for (;;) {
	if (mouse_update()) {
	    x = mouse_getx();
	    y = mouse_gety();
	    button = mouse_getbutton();

	    if (x != oldx || y != oldy) {
		if (oldx != -1) {
		    RestoreUnderMousePointer(oldx, oldy, oldcolour);
		}
		SaveUnderMousePointer(x, y, oldcolour);
		RenderMousePointer(x, y, OH_MOUSEFG, OH_MOUSEDARKFG);
		oldx = x;
		oldy = y;
	    }
	
	    if (button & MOUSE_LEFTBUTTON) {
		break;
	    }
	}
	if ((key = vga_getkey()) != -1) {
	    switch (key) {
	      case 'n':
		return NEWGAME;
	      case 'q':
		return QUIT;
	      default:
		break;
	    }
	}
    }

    if (x >= OH_QUITLEFT && x < OH_QUITRIGHT) {
	if (y > OH_NEWGAMETOP && y <= OH_NEWGAMEBOTTOM) {
	    return NEWGAME;
	} else if (y > OH_QUITTOP && y <= OH_QUITBOTTOM) {
	    return QUIT;
	}
    } 

    return (x << 9) | y;
}


static void
WhileMouseButton()
{
    int button = 1;
    
    while (button) {
	mouse_update();
	button = mouse_getbutton();
    }
    if (oldx != -1) {
	RestoreUnderMousePointer(oldx, oldy, oldcolour);
    }
    oldx = -1;
}
