/*
 * Amiga curses-style terminal routines.
 * Taken from Amiga ISpell, and hacked to fit GNUCHESS.
 *
 * (C) 1992 Martin W. Scott.
 */
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/gadtools.h>
#include <proto/intuition.h>

#include <stdio.h>
#include <fcntl.h>
#include <stdarg.h>
#include <ctype.h>

#include "display.h"
#include "amiterm.h"
#include "draw.h"
#include "pointer.h"
#include "requester.h"
#include "gnuchess.h"	/* for struct flag definition */

/* Prototypes for static functions */
static void CloseLibs(void);
static BOOL OpenLibs(void);
static BOOL Open_Console(struct IOStdReq *rq, struct Window *win);
static void CleanUp(void);
static void putcon(struct IOStdReq *rq, char *str);
static void __stdargs printc(char *fmt, ...);
static void cursor_off(void);
static void cursor_on(void);
static void con_set_line(int offset, int len);
static void con_set_pagelen(void);
static void setcolors(int color);
static void DisableWindow(void);
static void EnableWindow(void);
static void About(void);
static int mousebutton(BOOL down, WORD x, WORD y);
static int menupick(WORD menu, WORD item, WORD subitem);
static int getevent(struct Window *ApplWin);

/* special event 'char's */
#define IGNORE_EVENT	-1
#define MOUSE_MOVE	1000
#define MENU_PICK	1001

/* libraries */
struct IntuitionBase *IntuitionBase;
struct GadToolsBase *GadToolsBase;
struct GfxBase *GfxBase;
struct Library *ReqToolsBase;

/* storage */
static char movestr[6];		/* mousebutton move */
static char *menuchoice;	/* menu => command */
struct RastPort *rp;
static struct IOStdReq *scr_rq = NULL;
static int get_file;		/* next string from filereq */
static char *frhail;		/* hail-text for filereq */
int get_command;		/* use mouse/menus for input */
static char colorfile[100];	/* name of color file */


/* close libraries that were opened */
static void
CloseLibs()
{
	if (ReqToolsBase) CloseLibrary(ReqToolsBase);
	if (GadToolsBase) CloseLibrary(GadToolsBase);
	if (GfxBase) CloseLibrary(GfxBase);
	if (IntuitionBase) CloseLibrary(IntuitionBase);
}

/* open required libraries */
static BOOL
OpenLibs()
{
	if ((IntuitionBase = (void *)OpenLibrary("intuition.library",37L)) &&
	    (GfxBase = OpenLibrary("graphics.library", 37L)) &&
	    (GadToolsBase =  (void *)OpenLibrary("gadtools.library",37L)))
	{
		ReqToolsBase = (void *)OpenLibrary("reqtools.library", 37L);
		return TRUE;
	}

	CloseLibs();
	return FALSE;
}

static BOOL
Open_Console(struct IOStdReq *rq, struct Window *win)
{
	rq->io_Data = (APTR) win;
	rq->io_Length = sizeof(struct IOStdReq);
	if (OpenDevice("console.device",0,rq,0))
	  return(FALSE);	/* could not get device */	  
	return(TRUE);	/* device opened successfully */
}

static void
CleanUp()
{
	if (scr_rq)
	{
		if (scr_rq->io_Device) CloseDevice(scr_rq);
		FreeMem(scr_rq, sizeof(struct IOStdReq));
		scr_rq = NULL;
	}
	CloseChessWindow();
	CloseDownScreen();
	CloseLibs();
}

void
endwin()
{
	CleanUp();
}

int
initscr()
{
	if (!OpenLibs())
		return FALSE;

	if (!(scr_rq = AllocMem(sizeof(struct IOStdReq), MEMF_PUBLIC | MEMF_CLEAR)))
	{
		CloseLibs();
		return FALSE;
	}

	strcpy(colorfile, libdir);
	AddPart(colorfile, "gnuchess.colors", sizeof(colorfile));
	LoadColors(colorfile);	/* may fail...who cares? */

	if ((SetupScreen() == 0) &&
	    (OpenChessWindow() == 0) &&
	    (Open_Console(scr_rq, ChessWnd)))
	{
		rp = ChessWnd->RPort;
		return TRUE;
	}
	CleanUp();
	return FALSE;

} /* initscr */


/********************************************************************************/
/*	Buffered output routines						*/
/********************************************************************************/

#define CONBUFSIZ	128

static char putbuf[CONBUFSIZ+1];
static UWORD bufpos;

#define putch(ch)	{if (bufpos == CONBUFSIZ) refresh(); putbuf[bufpos++] = (ch);}
#define inst_putch(ch)	putch(ch); refresh()

void
refresh()
{
	putbuf[bufpos]='\0';
	putcon(scr_rq, putbuf);
	bufpos=0;
	chkabort();
}

void
putstr(char *str)
{
	if (strlen(str) >= (CONBUFSIZ-bufpos))
		refresh();
	while (*str)
		putch(*str++);
}

/*******************************************************************************/

static void
putcon(struct IOStdReq *rq, char *str)
{
    /* in case the console has not been initialized */
    if ( rq == NULL ) {
        fprintf(stdout,"%s",str);
	return;
    }
    rq->io_Command = CMD_WRITE;
    rq->io_Data = (APTR) str;
    rq->io_Length = -1;	
    DoIO(rq);    
}

static void __stdargs
printc(char *fmt, ...)
{
	va_list ap;
	char	pbuff[256];
	va_start(ap,fmt);
	vsprintf(pbuff,fmt,ap);
	putstr(pbuff);
}

static void
cursor_off()
{
     printc("%c%c%c%c",0x9b,0x30,0x20,0x70);
}

static void
cursor_on()
{
    printc("%c%c%c",0x9b,0x20,0x70);
}

static void
con_set_line(int offset,int len)
{
    char buf[10];
    sprintf(buf,"%d",(short)offset);
    printc("%c%s%c",0x9b,buf,0x78);

    sprintf(buf,"%d",(short)len);
    printc("%c%s%c",0x9b,buf,0x75);
}

static void
con_set_pagelen()
{
    printc("%c%c",0x9b,0x74);
}

static void
setcolors(int color)
{
    char buf[100];
    sprintf(buf,"\x9b%dm", color);
    putstr(buf);
}

static void
EnableWindow()
{
	struct MenuItem *item;

	ModifyIDCMP(ChessWnd, CHESS_IDCMP);

	/* update menu toggle status */
	item = ItemAddress(ChessMenus, FULLMENUNUM(TOGGLES, REVERSE, 0));
	if (flag.reverse) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;

	item = ItemAddress(ChessMenus, FULLMENUNUM(TOGGLES, COORDINATES, 0));
	if (flag.coords) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;

	item = ItemAddress(ChessMenus, FULLMENUNUM(TOGGLES, THINKING, 0));
	if (flag.post) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;

	item = ItemAddress(ChessMenus, FULLMENUNUM(TOGGLES, RANDOM, 0));
	if (dither) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;

	item = ItemAddress(ChessMenus, FULLMENUNUM(TOGGLES, BEEP, 0));
	if (flag.beep) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;
/***
	item = ItemAddress(ChessMenus, FULLMENUNUM(TOGGLES, USEBOOK, 0));
	if (Book) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;
 NOT WORKING... ***/

	SetMenuStrip(ChessWnd, ChessMenus);
}

static void
DisableWindow()		/* let user type ahead, but no more */
{
	ClearMenuStrip(ChessWnd);
	ModifyIDCMP(ChessWnd, IDCMP_VANILLAKEY);
}

#define ABOUT_TXT_1 "\
Welcome to Amiga GnuChess Version 1.0,\n\
derived from GNU Chess Version 4.00, patchlevel 58.\n\
\n\
GNU Chess is Copyright (c) 1986-1992 Free Software Foundation.\n\
Amiga graphics display is Copyright (c) 1992 Martin W. Scott.\n\
Freely redistributable, subject to the GNU General Public Licence.\n\
"

#define ABOUT_TXT_2 "\
You may contact the author of this Amiga port at:\n\
\n\
        Martin W. Scott,\n\
        23, Drum Brae North,\n\
        Edinburgh, EH4 8AT\n\
        United Kingdom.\n\
\n\
This version was released in September of 1992.\n\
Thanks to Nico François for reqtools.library.\
"

/* EasyEasyrequest... */
static void
About()
{
	struct EasyStruct es;
	char *arglist[2];

	es.es_StructSize = sizeof(struct EasyStruct);
	es.es_Flags = 0L;
	es.es_Title = "About Amiga GnuChess";
	es.es_TextFormat = "%s%s";
	es.es_GadgetFormat = "OK";
	arglist[0] = ABOUT_TXT_1;
	arglist[1] = ABOUT_TXT_2;
	EasyRequestArgs(ChessWnd, &es, NULL, arglist);
}

/* convert mouse-event to chess-square string */
/* to do?: erase square and make pointer an image of the piece 'held' */

static int
mousebutton(BOOL down, WORD x, WORD y)
{
	static UWORD	from_row, from_col;
	static BOOL	have_from;
	UWORD		row, col;
	int		rv = -1;

	row = 7-(y-TOP_OFFSET)/SQUARE_HEIGHT;
	col = (x-LEFT_OFFSET)/SQUARE_WIDTH;

	if (!have_from && down)		/* from square selected */
	{
		if (row < 8 && col < 8)
		{
			AmiToggleSquare(row,col);
			from_row = row;
			from_col = col;
			have_from = TRUE;
			SetToPointer(ChessWnd);
		}
	     /* else ignore mouse-command */
	}
	else if (have_from)		/* to square selected */
	{
		ClrToPointer(ChessWnd);
		AmiToggleSquare(from_row,from_col);	/* restore from-square */

		if ((row < 8 && col < 8) &&			/* in bounds */
		    (from_row != row || from_col != col))	/* aborted */
		{
			if (flag.reverse)	/* convert position */
			{
				from_row = 7-from_row;
				from_col = 7-from_col;
				row = 7-row;
				col = 7-col;
			}
			sprintf(movestr, "%c%c%c%c", from_col+'a', from_row+'1',
						     col+'a', row+'1');
			rv = MOUSE_MOVE;
		}
		have_from = FALSE;
	}

	return rv;
}

/* convert menu choice to command strings */
static int
menupick(WORD menu, WORD item, WORD subitem)
{
	int rv = MENU_PICK;

	switch (menu)
	{
	case PROJECT:
		switch (item)
		{
		case NEW_GAME:	menuchoice = "new"; break;
		case LOAD_GAME: menuchoice = "get";
				frhail = "Load game...";
				get_file = TRUE;
				break;
		case SAVE_GAME: menuchoice = "save";
				frhail = "Save game...";
				get_file = TRUE;
				break;
		case LISTING:	menuchoice = "list"; break;
		case EDITBOARD: menuchoice = "edit"; break;
		case SHOWHELP:	menuchoice = "help"; break;
		case QUIT:	menuchoice = "quit"; break;

		case ABOUT:	About();
		default:	rv = -1;
		}
		break;

	case SETTINGS:
		switch (item)
		{
		case SET_CLOCK:	menuchoice = "clock"; break;
		case COLORS:	DoPalette(ChessWnd); rv = -1; break;
		case SAVECOLORS: SaveColors(colorfile);
		default:	rv = -1;
		}
		break;

	case TOGGLES:
		switch (item)
		{
		case REVERSE:	menuchoice = "reverse"; break;
		case COORDINATES: menuchoice = "coords"; break;
		case THINKING:	menuchoice = "post"; break;
		case RANDOM:	menuchoice = "random"; break;
		case BEEP:	menuchoice = "beep"; break;
/*		case USEBOOK:	menuchoice = "book"; break;*/

		default:	rv = -1;
		}
		break;

	case MOVE:
		switch (item)
		{
		case GO:	menuchoice = "go"; break;
		case UNDO:	menuchoice = "undo"; break;
		case REMOVE:	menuchoice = "remove"; break;
		case SWITCH:	menuchoice = "switch"; break;
		case FORCE:	menuchoice = "force"; break;
		case REDRAW:	menuchoice = "bd"; break;
		case HINT:	menuchoice = "hint"; break;

		default:	rv = -1;
		}
		break;

	default:	rv = -1;
	}

	return rv;
}

/* get input from user and tokenize it */ 
static int
getevent(struct Window *ApplWin)
{
    struct IntuiMessage *mess;
    int retval = IGNORE_EVENT;

    /* wait for an intuition event */
    while( !(mess = (struct IntuiMessage *)GetMsg(ApplWin->UserPort)) )
        Wait(1 << ApplWin->UserPort->mp_SigBit);
 
    switch(mess->Class) {
	case IDCMP_VANILLAKEY:
	    retval = mess->Code;
	    if (isprint(retval))
		inst_putch(retval);
	    break;
	case IDCMP_MENUPICK:
	    retval = menupick(MENUNUM(mess->Code), ITEMNUM(mess->Code), SUBNUM(mess->Code));
	    break;
	case IDCMP_MOUSEBUTTONS:
	    retval = mousebutton(mess->Code == SELECTDOWN, mess->MouseX, mess->MouseY);
	    break;
	default:
	    break;
    }
    ReplyMsg(mess);
    return(retval);
}

/*
 * end of amiga console support.
 *-----------------------------------------------------------------------*/

void __stdargs
printw(char *fmt, ...)
{
	va_list ap;
	char	pbuff[256];
	va_start(ap,fmt);
	vsprintf(pbuff,fmt,ap);
	putstr(pbuff);
}

int __stdargs
scanw(char *fmt, ...)
{
	va_list ap;
	char	pbuf[256];
	va_start(ap,fmt);
	if (getstr(pbuf) != (char *)EOF)
		return sscanf(pbuf, fmt, va_arg(ap,long),va_arg(ap,long),va_arg(ap,long),va_arg(ap,long),va_arg(ap,long));
	else return 0;
}

int
getch()
{
    int ch;

    refresh();
    while ( (ch=getevent(ChessWnd)) == -1 )
        ;
    return( ch );
}

char *
getstr(char *buf)
{
    int ch;
    char *t = buf;

    /* last command was load/save */
    if (get_file && DoFileRequest(ChessWnd, frhail, buf))
    {
	get_file = FALSE;
	return buf;
    }
    /* else fall-through, let user type name... */

    get_file = FALSE;
    refresh();
    if (get_command)
	EnableWindow();

    *buf = '\0';	/* assume nothing first */

    while ((ch=getevent(ChessWnd)) != '\r')
	switch (ch)
	{
		case 0x0c:	/* ^L i.e. redraw */
			strcpy(buf, "bd");
			goto finished;
			
		case 0x1c:	/* ^\ i.e. EOF */
			DisableWindow();
			return (char *)EOF;

		case '\b':	/* backspace, delete */
		case 0x7f:
			if (t > buf)
			{
				t--;
				printw("\b \b");
				refresh();
			}
			break;

		case MOUSE_MOVE:
			putstr(movestr);
			refresh();
			strcpy(buf, movestr);
			goto finished;		

		case MENU_PICK:
			strcpy(buf, menuchoice);
			goto finished;

		case IGNORE_EVENT:
			break;

		default:
			*t++ = ch;
	}
    *t = '\0';

finished:
    DisableWindow();
    return (buf);
}

/* clear the screen */
void
clear ()
{
	char buf[10];
	sprintf(buf,"%c",0x0c);
	putstr(buf);
	refresh();
}

/* clear to end of line */
void
clrtoeol ()
{
	char buf[10];
	sprintf(buf,"%c%c",0x9b,0x4b);
	putstr(buf);
}

/* move to row, col */
void
move (row, col)
{
    char buf[10];
    sprintf(buf,"%c%d%c%d%c",0x9b,row+1,0x3b,col+1,0x48);
    putstr(buf);
}

/* do a back space */
void
backup ()
{
    inst_putch((int)'\b');
}

void
leaveok(void *p, int turnoff)
{
	if (turnoff)
		cursor_off();
	else
		cursor_on();
}

void
attron(int what)
{
	if (what < 8)	/* color change */
		setcolors(30+what);
}

void
standout()
{
	setcolors(7);
}

void
standend()
{
	setcolors(0);
}

void
flash()
{
    inst_putch(0x7);
}

extern void dobeep(long);
void beep()
{
	dobeep(100);
}

/* unused at moment... */
void crmode(){}
void nocrmode(){}
