// Copyright 1994 Brad Pitzel
//
// Feel free to use/distribute/modify as long as credit/copyrights for myself 
// are included.

// a layer on top of the svgalib library.
// Routines to setup the initialize the screen/svgalib/etc, restore back
// to text when all done, throw up a few messages/prompts, get simple
// string input from the user, etc..

#include "Ui.h"
#include "Keyboard.h"

GraphicsContext Ui::backscreen;
GraphicsContext Ui::physicalscreen;


int Ui::vgaMode;
int Ui::virtualScn=0;

int Ui::fontHt;
int Ui::fontW;
unsigned char *Ui::VexpandedFont=NULL;

struct termios Ui::old;

// set drawing to virtualScn screen
void Ui::drawToVirtual() 
	{
	if (!virtualScn) {
		// Create virtual screen.
		gl_setcontextvgavirtual(vgaMode);
		gl_setclippingwindow(0,0,WIDTH-1,HEIGHT-1);
		gl_getcontext(&backscreen);
		virtualScn=1;
		}
	gl_setcontext(&backscreen); 
	}


void Ui::init()
	{
	
	vga_init();

	// Graphics mode to use:
	// NOTE! If you change to any mode other than 320x200, set
	// BACKDROPS=NO in the 'config' file before re-compiling.
	vgaMode = G320x200x256;
//	vgaMode = G640x480x256;
//	vgaMode = G800x600x256;
//	vgaMode = G1024x768x256;	// hope you have a big monitor :-)

	if (!vga_hasmode(vgaMode)) 
		{
		cerr << "Mode not available." << endl;
		exit(-1);
		}

	// trap signals to restore text mode, etc
	signal(SIGHUP, die);
	//signal(SIGINT, die);
	signal(SIGQUIT, die);
	signal(SIGILL, die);
	//signal(SIGTRAP, die);
	signal(SIGABRT, die);
	signal(SIGFPE, die);
	signal(SIGKILL, die);
	signal(SIGSEGV, die);
	signal(SIGTERM, die);
	//signal(SIGSTOP, die);
	//signal(SIGTSTP, die);
	//signal(SIGTTIN, die);
	//signal(SIGTTOU, die);

		
	vga_setmode(vgaMode);
	gl_setcontextvga(vgaMode);	// Physical screen context.
	gl_getcontext(&physicalscreen);
	gl_setrgbpalette();
	
	initFont();


	int tmp;
	// save current term settings	
	tmp = tcgetattr(fileno(stdin), &Ui::old);
	if (tmp)
		cerr << "Ui::init, tcgetattr = " << tmp << endl;



	}


void Ui::die(int x)
	{
	Ui::restore();

	cerr << "Ui::Received signal " << x << endl;
	
	Keyboard::restore();	// in case we are in raw keyboard mode
	exit(-1);
	}

void Ui::restore()
	{

	// restore tty
	int tmp;
	tmp = tcsetattr(fileno(stdin), 0, &Ui::old);

	if (tmp)
		cerr << "Ui::init, tcsetattr = " << tmp << endl;

	if (VexpandedFont)
		{
		delete [] VexpandedFont;
		VexpandedFont=NULL;
		}	


	if (virtualScn)
		gl_freecontext(&backscreen);

	vga_setmode(TEXT);

	}

void Ui::initFont()
	{
	fontHt = 8;
	fontW = 8;

//	VexpandedFont = (unsigned char *)malloc(256*fontW*fontHt*BYTESPERPIXEL);
	VexpandedFont = new unsigned char[256*fontW*fontHt*BYTESPERPIXEL];
	
	if (VexpandedFont==NULL)
		cerr << "Ui::initFont, malloc failed for expanded font" << endl;
	else
		{
		gl_expandfont( fontW, fontHt, 255, gamefont01, VexpandedFont );
		//gl_expandfont( fontW, fontHt, 255, gl_font8x8, VexpandedFont );
		gl_setfont( fontW, fontHt, VexpandedFont );
		}
	}
	
void Ui::setFontColor(int c)
	{
	if (VexpandedFont)
		{
		gl_colorfont( fontW, fontHt, c, VexpandedFont );
		gl_setfont( fontW, fontHt, VexpandedFont );
		}
	}

void Ui::message( int y, const char *msg )
	{
	int x1, y1, x2, y2;
	int mx, my, len;

	len = strlen(msg);

	mx = (WIDTH - len*fontW) >> 1;
	x1 = mx - 2;
	x2 = mx + len*fontW + 1;

	my = y;
	y1 = my - 2;
	y2 = my + fontHt + 1;

	// put box around text
	gl_hline(x1,y1,x2,255);
	gl_hline(x1,y2,x2,255);
	gl_line(x1,y1,x1,y2,255);
	gl_line(x2,y1,x2,y2,255);

	gl_write( mx, my, (char *)msg );
	}


// prompt user, return 1=yes, 0=no
char Ui::yesNo( const char *msg )
	{
	char result=-1;
	GraphicsContext GCold;
	
	gl_getcontext(&GCold);

	drawToPhysical();
	
	String s = msg;
	s += " (y/n) ?";

	Ui::message( (const char *)s );

	while (result<0)
		switch (getchar())
			{
			case 'y':
			case 'Y':
				result = 1;
				break;
			
			case 'n':
			case 'N':
				result = 0;
				break;
			}
	
	gl_setcontext(&GCold);
	return result;
	}


// get text input from user
// len must be 1 less than msg buffer to allow for 0 at end
int Ui::input( int x, int y, char *msg, int len )
	{
	int pos=0;
	int xp=x;
	int done=0;
	int key=0;
	char buf[1];
	
	// guarantee string no longer than 'len'
	*(msg+len)=0;
	// put cursor at end of any existing text in string
	pos=strlen(msg);
	xp += pos*fontW;
	
	// put term into non-processing mode so we can interpret
	// backspace, etc..
	int tmp;
	struct termios current,termSave;

	// get current term settings	
	tmp = tcgetattr(fileno(stdin), &termSave);
	tmp = tcgetattr(fileno(stdin), &current);
	if (tmp)
		cerr << "Ui::input, tcgetattr = " << tmp << endl;

	current.c_lflag = current.c_lflag & ~ (ICANON | ECHO ); 
	current.c_cc[VMIN] = 1;
	current.c_cc[VTIME] = 10;	/* intercharacter timeout */

	tmp = tcsetattr(fileno(stdin), TCSAFLUSH, &current);
	if (tmp)
		cerr << "Ui::input, tcsetattr = " << tmp << endl;

	gl_write( x, y, msg );
	gl_setwritemode(WRITEMODE_OVERWRITE);
	while(!done)
		{
		drawCursor(xp,y);
		key = getchar();
		hideCursor(xp,y);
	
		switch (key) 
			{

			case 10:
				done   = 1;
				break;

			case 127:	// backspace
				if (pos>0)
					{
					pos--;
					xp -= fontW;
					gl_writen(xp,y,1," ");
					*(msg+pos)=' ';
					}
	
			default: //regular characters, i.e a-z,0-9,etc..
				if ( (pos<len) && (key >= ' ') && (key<127) )
					{
					buf[0] = (char)key;
					*(msg+pos) = buf[0];
					gl_writen(xp,y,1,buf);
					pos++;
					xp += fontW;
					}
			}
	
		}

	// NULL terminate string
	*(msg+pos)=0;


	// restore term
	tmp = tcsetattr(fileno(stdin), 0, &termSave);
	if (tmp)
		cerr << "Ui::input, tcsetattr = " << tmp << endl;

	// return last key pressed
	return key;
	}


// for 'input', above.  Show a simple non-blinking cursor
inline void Ui::drawCursor(int x, int y)
	{
	gl_hline(x, y+fontHt, x+fontW-1, 255);
	}

inline void Ui::hideCursor(int x, int y)
	{
	gl_hline(x, y+fontHt, x+fontW-1, 0);
	}
