/****************************************************
 * vt100 emulator - window/keyboard support
 *
 *	v2.7 870825 ACS - Provide an info/status window rather than using
 *			  req().  Better error handling.
 *	v2.6 870227 DBW - bug fixes for all the stuff in v2.5
 *	v2.5 870214 DBW - more additions (see readme file)
 *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
 *	v2.3 861101 DBW - minor bug fixes
 *	v2.2 861012 DBW - more of the same
 *	v2.1 860915 DBW - new features (see README)
 *	     860823 DBW - Integrated and rewrote lots of code
 *	v2.0 860809 DBW - Major rewrite
 *	v1.1 860720 DBW	- Switches, 80 cols, colors, bug fixes
 *	v1.0 860712 DBW	- First version released
 *
 ****************************************************/

#include "vt100.h"

/* keyboard definitions for toasc() */
static char keys[75] = {
    '`','1','2','3','4','5','6','7','8','9','0','-' ,
    '=','\\', 0, '0','q','w','e','r','t','y','u','i','o' ,
    'p','[',']', 0, '1','2','3','a','s','d','f','g','h' ,
    'j','k','l',';','\'', 0, 0, '4','5','6', 0, 'z','x','c','v',
    'b','n','m',44,'.','/', 0, '.','7','8','9',' ',8,
    '\t',13,13,27,127,0,0,0,'-' } ;
    
/* For InfoMsg...may be changed by a NEWSIZE msg in vt100.c */
int reqminx,	/* Min value for x in reqwindow (pixels) */
    reqmaxx,	/* Max value for x in reqwindow (pixels) */
    reqmaxlen,	/* Max # chars in reqwindow */
    reqminy,	/* Min value for y in reqwindow (scan lines) */	
    reqmaxy,	/* Max value for y in reqwindow (scan lines) */
    reqfudge;	/* Clear space between border and start of 1st char */
int reqy;	/* Current pixel location in reqwindow */

void ReqNewSize(), OpenReqWindow();

/***************************************************
 *  function to swap the use of backspace and delete
 ***************************************************/

void swap_bs_del()
    {
    if (p_bs_del)   p_bs_del = 0;
    else	    p_bs_del = 1;

    keys[0x41] =    p_bs_del ? 127 : 8;
    keys[0x46] =    p_bs_del ? 8 : 127;
    }

/*************************************************
 *  function to get file name (via a requestor)
 *************************************************/
void req(prmpt,name,getinp)
char *prmpt,*name;
int  getinp;
    {
    ULONG class;
    USHORT position, RemoveGadget();
    unsigned int code, qual;
    int  lprmpt, lname;
    struct IntuiMessage *Msg;

    if(reqwinup == 0)
	OpenReqWindow();
    
    if(!getinp) {
    	InfoMsg2Line(prmpt, name);
    	return;
    }

    lprmpt = strlen(prmpt);
    lname = strlen(name);

    /* Don't use strings longer than what we've provided space for. */
    if(lprmpt > (MAXGADSTR-1)) {
	emits("Prompt too long - truncated.\n");
	lprmpt = MAXGADSTR-1;
    }
    if(lname > (MAXGADSTR-1)) {
	emits("Name too long - truncated.\n");
	lname = MAXGADSTR-1;
    }

    if (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
	class = Msg->Class;
	ReplyMsg(Msg);
	if(class == REQCLEAR)
	    numreqs = 0;
	if(class == NEWSIZE)
	    ReqNewSize(reqwindow->Height, reqwindow->Width);
    }

    /* Make sure the prompt gets updated */
    if (numreqs == 1 && strcmp(Prompt,prmpt) != 0) {
	EndRequest(&myrequest,reqwindow);
	do {
		Wait(1L << reqwindow->UserPort->mp_SigBit);
		while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
			class = Msg->Class;
			ReplyMsg(Msg);
			if(class == NEWSIZE)
			    ReqNewSize(reqwindow->Height, reqwindow->Width);
		}
	} while (class != REQCLEAR);
	numreqs = 0;
	}

    /* copy in a prompt and a default */
    strncpy(Prompt,prmpt,lprmpt); Prompt[lprmpt+1] = '\0';
    strncpy(InpBuf,name,lname); InpBuf[lname+1] = '\0';

    if (numreqs == 1) {		/* If there is a requester... reuse it */
    	RefreshGadgets(&mystrgad, reqwindow, &myrequest);
    	Delay(2L);
    }
    else {			/* otherwise create it */
	while(numreqs != 1) {
            if (Request(&myrequest, reqwindow) == 0) {
		emits("ERROR - CAN'T CREATE REQUESTOR FOR:\n");
		emits(Prompt); emit('\n'); emits(InpBuf); emit('\n');
		return;
	    }
	    else numreqs = 1;

	    do {
		Wait(1L << reqwindow->UserPort->mp_SigBit);
		while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
		    class = Msg->Class;
		    ReplyMsg(Msg);
		    if(class == REQCLEAR)
			numreqs = 0;
		    if(class == NEWSIZE)
			ReqNewSize(reqwindow->Height, reqwindow->Width);
		}
	    } while (class != REQSET);
	} /* end while numreqs != 0 */
    } /* end else */

    /* if we don't want input, we're done */
    if (getinp == 0 || numreqs == 0) return;

    if((reqwindow->Flags & WINDOWACTIVE) != WINDOWACTIVE) {
    	WindowToFront(reqwindow);
	ActivateWindow(reqwindow);
	do {
	    Wait(1L << reqwindow->UserPort->mp_SigBit);
	    while(Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
		class = Msg->Class;
		ReplyMsg(Msg);
		if(class == NEWSIZE)
		    ReqNewSize(reqwindow->Height, reqwindow->Width);
	    }
	} while (class != ACTIVEWINDOW);
    }
    
    /* here is where we pre-select the gadget   */
    if (!ActivateGadget(&mystrgad,reqwindow,&myrequest)) {

	/* wait for his/her hands to get off the keyboard (Amiga-key) */
	Delay(20L);
	while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
	    ReplyMsg(Msg);
	    if(class == NEWSIZE)
		ReqNewSize(reqwindow->Height, reqwindow->Width);
	}

	/* try once more before giving up... */
	ActivateGadget(&mystrgad,reqwindow,&myrequest);
	}

    /* wait for input to show up */
    while (1) {
	if ((NewMessage = (struct IntuiMessage *)
		GetMsg(reqwindow->UserPort)) == FALSE) {
	    Wait(1L<<reqwindow->UserPort->mp_SigBit);
	    continue;
	    }
	class = NewMessage->Class;
	ReplyMsg(NewMessage);

	/* the requestor got terminated... yea!! */
	if (class == REQCLEAR) break;

	if(class == NEWSIZE)
	    ReqNewSize(reqwindow->Height, reqwindow->Width);
	    
	/* maybe this is a menu item to handle */
/*	if (class == MENUPICK) handle_menupick(class,code); */
	}

    /* all done, so return the result */
    numreqs = 0;
    strcpy(name,InpBuf);
    }

/*************************************************
*  function to print a string
*************************************************/
void emits(string)
char string[];
    {
    int i;
    char c;

    i=0;
    while (string[i] != 0)
	{
	c=string[i];
	if (c == 10) emit(13);
	emit(c);
	i += 1;
	}
    }

/*************************************************
*  function to output ascii chars to window
*************************************************/
void emit(c)
char c;
    {
    static char wrap_flag = 0;	/* are we at column 80? */

    c &= 0x7F;
    switch( c )
	{
	case '\t':
	x += 64 - ((x-MINX) % 64);
	break;

	case 10:  /* lf */
	y += 8;
	break;

	case 13:  /* cr */
	x = MINX;
	break;

	case 8:   /* backspace */
	x -= 8;
	if (x < MINX) x = MINX;
	break;

	case 12:     /* page */
	x = MINX;
	y = MINY;
	SetAPen(mywindow->RPort,0L);
	RectFill(mywindow->RPort,(long)MINX,
	    (long)(MINY-7),(long)(MAXX+7),(long)(MAXY+1));
	SetAPen(mywindow->RPort,1L);
	break;

	case 7:     /* bell */
	if (p_volume == 0) DisplayBeep(NULL);
	else {
	    BeginIO(&Audio_Request);
	    WaitIO(&Audio_Request);
	    }
	break;

	default:
	if (c < ' ' || c > '~') break;
	if (p_wrap && wrap_flag && x >= MAXX) {
	    x = MINX;
	    y += 8;
	    if (y > MAXY) {
		y = MAXY;
		ScrollRaster(mywindow->RPort,0L,8L,(long)MINX,
		    (long)(MINY-6),(long)(MAXX+7),(long)(MAXY+1));
		}
	    }
	Move(mywindow->RPort,(long)x,(long)y);

	if (curmode&FSF_BOLD) {
	    if (p_depth > 1) {
		SetAPen(mywindow->RPort,(long)(2+(1^p_screen)));
		SetSoftStyle(mywindow->RPort,(long)curmode,253L);
		}
	    else SetSoftStyle(mywindow->RPort,(long)curmode,255L);
	    }
	else SetSoftStyle(mywindow->RPort,(long)curmode,255L);

	if (curmode&FSF_REVERSE) {
	    SetDrMd(mywindow->RPort,(long)(JAM2+INVERSVID));
	    Text(mywindow->RPort,&c,1L);
	    SetDrMd(mywindow->RPort,(long)JAM2);
	    }
	else Text(mywindow->RPort,&c,1L);

	if (curmode&FSF_BOLD) SetAPen(mywindow->RPort,1L);
	x += 8;
	} /* end of switch */

    if (y > MAXY) {
	y = MAXY;
	x = MINX;
	ScrollRaster(mywindow->RPort,0L,8L,(long)MINX,
	    (long)(MINY-6),(long)(MAXX+7),(long)(MAXY+1));
	}
    if (x > MAXX) {
	wrap_flag = 1;
	x = MAXX;
	}
    else wrap_flag = 0;
    }

/*************************************************
*  function to output ascii chars to window (batched)
*************************************************/
void emitbatch(la,lookahead)
int la;
char *lookahead;
    {
    int i;

    Move(mywindow->RPort,(long)x,(long)y);
    i = x / 8;
    if (i+la >= maxcol) {
	if (p_wrap == 0) la = maxcol - i;
	else {
	    lookahead[la] = 0;
	    emits(lookahead);
	    return;
	    }
	}
    if (curmode&FSF_BOLD) {
	if (p_depth > 1) {
	    SetAPen(mywindow->RPort,(long)(2+(1^p_screen)));
	    SetSoftStyle(mywindow->RPort,(long)curmode,253L);
	    }
	else SetSoftStyle(mywindow->RPort,(long)curmode,255L);
	}
    else SetSoftStyle(mywindow->RPort,(long)curmode,255L);

    if (curmode&FSF_REVERSE) {
	SetDrMd(mywindow->RPort,(long)(JAM2+INVERSVID));
	Text(mywindow->RPort,lookahead,(long)la);
	SetDrMd(mywindow->RPort,(long)JAM2);
	}
    else Text(mywindow->RPort,lookahead,(long)la);
    if (curmode&FSF_BOLD) SetAPen(mywindow->RPort,1L);
    x += (8 * la);
    }

/******************************
* Manipulate cursor
******************************/
void cursorflip()
    {
    SetDrMd(mywindow->RPort,(long)COMPLEMENT);
    SetAPen(mywindow->RPort,3L);
    RectFill(mywindow->RPort,
	(long)(x-1),(long)(y-6),(long)(x+8),(long)(y+1));
    SetAPen(mywindow->RPort,1L);
    SetDrMd(mywindow->RPort,(long)JAM2);
    }

/************************************************
*  function to take raw key data and convert it
*  into ascii chars
**************************************************/
int toasc(code,qual,local)
unsigned int code,qual;
int local;
    {
    unsigned int ctrl,shift,capsl,amiga,alt;
    char c = 0, keypad = 0;
    char *ptr;

    ctrl    = qual & IEQUALIFIER_CONTROL;
    capsl   = qual & IEQUALIFIER_CAPSLOCK;
    amiga   = qual & (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND);
    shift   = qual & (IEQUALIFIER_LSHIFT   | IEQUALIFIER_RSHIFT);
    alt	    = qual & (IEQUALIFIER_LALT     | IEQUALIFIER_RALT);

    switch ( code )
	{
	case 98:
	case 226:
	case 99:
	case 227:
	case 96:
	case 97:
	case 224:
	case 225:
	case 100:
	case 101:
	case 228:
	case 229:
	case 102:
	case 103:
	case 230:
	case 231:   c = 0; break; /* ctrl, shift, capsl, amiga, or alt */

	case 0x50:
	case 0x51:
	case 0x52:
	case 0x53:
	case 0x54:
	case 0x55:
	case 0x56:
	case 0x57:
	case 0x58:
	case 0x59:  c = 0;
		    if (shift)	ptr = p_F[code - 0x50];
		    else	ptr = p_f[code - 0x50];
		    if (!script_on && *ptr == p_keyscript)
			    script_start(++ptr);
		    else    sendstring(ptr);
		    break;
	case 0x0f: c = (p_keyapp) ? 'p' : '0'; keypad = TRUE; break;
	case 0x1d: c = (p_keyapp) ? 'q' : '1'; keypad = TRUE; break;
	case 0x1e: c = (p_keyapp) ? 'r' : '2'; keypad = TRUE; break;
	case 0x1f: c = (p_keyapp) ? 's' : '3'; keypad = TRUE; break;
	case 0x2d: c = (p_keyapp) ? 't' : '4'; keypad = TRUE; break;
	case 0x2e: c = (p_keyapp) ? 'u' : '5'; keypad = TRUE; break;
	case 0x2f: c = (p_keyapp) ? 'v' : '6'; keypad = TRUE; break;
	case 0x3d: c = (p_keyapp) ? 'w' : '7'; keypad = TRUE; break;
	case 0x3e: c = (p_keyapp) ? 'x' : '8'; keypad = TRUE; break;
	case 0x3f: c = (p_keyapp) ? 'y' : '9'; keypad = TRUE; break;
	case 0x43: c = (p_keyapp) ? 'M' : 13 ; keypad = TRUE; break;
	case 0x4a: c = (p_keyapp) ? 'l' : '-'; keypad = TRUE; break;
	case 0x5f: sendstring("\033Om") ;break;
	case 0x3c: c = (p_keyapp) ? 'n' : '.'; keypad = TRUE; break;
	case 0x4c:
	case 0x4d:
	case 0x4e:
	case 0x4f: sendchar(27);            /* cursor keys */
		   if (p_curapp) sendchar('O');
		   else sendchar('[');
		   sendchar(code - 11);
		   break;

	default:
	if (code < 75) c = keys[code];
	else c = 0;
	}

    if (keypad) {
	if (p_keyapp) sendstring("\033O");
	sendchar(c);
	return(0);
	}

    /* add modifiers to the keys */

    if (c != 0) {
	if (shift) {
	    if ((c <= 'z') && (c >= 'a')) c -= 32;
	    else
	    switch( c ) {
		case '[':  c = '{'; break;
		case ']':  c = '}'; break;
		case '\\': c = '|'; break;
		case '\'': c = '"'; break;
		case ';':  c = ':'; break;
		case '/':  c = '?'; break;
		case '.':  c = '>'; break;
		case ',':  c = '<'; break;
		case '`':  c = '~'; break;
		case '=':  c = '+'; break;
		case '-':  c = '_'; break;
		case '1':  c = '!'; break;
		case '2':  c = '@'; break;
		case '3':  c = '#'; break;
		case '4':  c = '$'; break;
		case '5':  c = '%'; break;
		case '6':  c = '^'; break;
		case '7':  c = '&'; break;
		case '8':  c = '*'; break;
		case '9':  c = '('; break;
		case '0':  c = ')'; break;
		default:            break;
		}
	    }
	else if (capsl && (c <= 'z') && (c >= 'a')) c -= 32;
	}
    if (ctrl) {
	if (c > '`' && c <= 127) c -= 96;
	else if (c > '@' && c <= '_') c -= 64;
	else if (c == '6') c = 30;
	else if (c == '-' || c == '?') c = 31;
	}
    if (ctrl && (c == '@' || c == '2' || c == ' ')) {
	if (!local) sendchar(alt?128:0);
	c = 0;
	}
    else if (c != 0 && (!local)) sendchar(alt?c+128:c);
    return((int)c);
    }

void
KillReq()
{
    struct IntuiMessage *Msg;
    ULONG class;
    
    if(numreqs != 0) {
	EndRequest(&myrequest,reqwindow);
	do {
		Wait(1L << reqwindow->UserPort->mp_SigBit);
		while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
			class = Msg->Class;
			ReplyMsg(Msg);
		}
	} while (class != REQCLEAR);
	numreqs = 0;
    }

    if(reqwinup) {
    	/* First, clear out all pending messages */
	while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
	    class = Msg->Class;
	    ReplyMsg(Msg);
	}
	NewReqWindow.LeftEdge = reqwindow->LeftEdge;	/* Remember ...	  */
	NewReqWindow.TopEdge = reqwindow->TopEdge;	/* ...where...	  */
	NewReqWindow.Width = reqwindow->Width;		/* ...the user... */
	NewReqWindow.Height = reqwindow->Height;	/* ...put it.	  */
	CloseWindow(reqwindow); /* Now we can close the window */
	reqwinup = 0;
    }
}

void
InfoMsg2Line(header, msg)
char *header, *msg;
{
    ScrollInfoMsg(1);
    InfoMsgNoScroll(header);
    ScrollInfoMsg(1);
    InfoMsgNoScroll(msg);
    ScrollInfoMsg(1);
}

void
InfoMsg1Line(msg)
char *msg;
{
    ScrollInfoMsg(1);
    InfoMsgNoScroll(msg);
    ScrollInfoMsg(1);
}

/*   Output the specified data to the "info" window  */
void
ScrollInfoMsg(lines)
int lines;
{
/*  ULONG class;
    struct IntuiMessage *Msg; */
    int pixels = lines << 3;
    
    if(!reqwinup)
	OpenReqWindow();

/*  if(Msg=(struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
	class = Msg->Class;
	ReplyMsg(Msg);
	if(class == NEWSIZE)
	    ReqNewSize(reqwindow->Height, reqwindow->Width);
    } */

    if ( (reqy += pixels) > reqmaxy) {
	reqy = reqmaxy;
	if(pixels > 0)
	    ScrollRaster(reqwindow->RPort, 0L, (LONG)pixels,
		(LONG)reqminx,
		(LONG)reqminy,
		(LONG)(reqmaxx+7),
		(LONG)(reqmaxy+7));
/* Was:		(LONG)(wp->Width - wp->BorderRight),
		(LONG)(wp->Height - wp->BorderBottom)); */
    }
}

void
InfoMsgNoScroll(msg)
char *msg;
{
    LONG msglen = strlen(msg);

    if(msglen > reqmaxlen)
	msglen = reqmaxlen;

    ScrollInfoMsg(0);	/* Ensure that the msg willbe visible */

    /*  Position the pen at the baseline of the character (7 scan lines
    ** into it). */
    Move(reqwindow->RPort, (LONG)reqminx, (LONG)(reqy+6));
    Text(reqwindow->RPort, msg, msglen);
}

void
ReqNewSize(height, width)
SHORT height, width;
{
    register struct Window *wp = reqwindow;
    int oldmaxy;

    /*   Compute min and max for x and y coordinates.  Note that for y the
    ** value is for the *top* of the character, not the baseline.  Text()
    ** uses a baseline value and so it must be adjusted prior to the call
    ** (characters are assumed to occupy an 8x8 matrix with the baseline at
    ** 7).  When computing the max values, calculate them so that we will
    ** sufficient room for an entire character. */
    oldmaxy = reqmaxy;
    reqminy = wp->BorderTop + reqfudge;
    reqmaxy = (((height - reqminy - wp->BorderBottom) >> 3) << 3)
		+ (reqminy-8);
    reqminx = wp->BorderLeft + reqfudge;
    reqmaxx = (((width - reqminx - wp->BorderRight) >> 3) << 3)
		+ (reqminx-8);
    reqmaxlen = (reqmaxx+7) / 8;
    if(oldmaxy > reqmaxy) { /* Clean up the bottom of the window */
	int temp = height - wp->BorderBottom - reqmaxy;
	
	ScrollRaster(wp->RPort, 0L, (LONG)temp,
	(LONG)reqminx,
	(LONG)reqmaxy,
	(LONG)(width - wp->BorderRight),
	(LONG)(height - wp->BorderBottom));
    }
}

void
OpenReqWindow()
{
    struct IntuiMessage *Msg;
    ULONG class;
    void ReqNewSize();
    
    reqwindow = OpenWindow(&NewReqWindow);
    do {
	Wait(1L << reqwindow->UserPort->mp_SigBit);
	while(Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) {
	    class = Msg->Class;
	    ReplyMsg(Msg);
	}
    } while (class != ACTIVEWINDOW);
    reqfudge = 0;	/* Leave 0 pixels/scan lines between border and char */
    ReqNewSize(reqwindow->Height, reqwindow->Width);
    reqy = reqminy;	/* Top of character set by ReqNewSize() */
    reqwinup = 1;
}
