/****************************************************
 * vt100 emulator - window/keyboard support
 *
 *	v2.8 880117 ACS - See the README file
 *	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"

static char *infkey[] = {	/* F-keys resulting from RawKeyConvert() */
    "0~",  "1~",  "2~",  "3~",  "4~",  "5~",  "6~",  "7~",  "8~",  "9~",
    "10~", "11~", "12~", "13~", "14~", "15~", "16~", "17~", "18~", "19~",
    NULL};

/* Cursor keys resulting from RawKeyConvert() and their output values */
static struct {
    char *in;		/* in sequence */
    char *out_curapp;	/* out sequence in p_curapp */
    char *out;		/* out sequence !in p_curapp */
} ckeys[] = {
	"A",	"OA",	"[A",
	"T",	"OA",	"[A",
	"B",	"OB",	"[B",
	"S",	"OB",	"[B",
	"C",	"OC",	"[C",
	" A~",	"OC",	"[C",
	"D",	"OD",	"[D",
	" @~",	"OD",	"[D",
	NULL,	NULL,	NULL	};

/* Numeric keypad return values excluding HELP, '-' and ENTER */
static char keypad[] = "pqrstuvwxy";
/* Numeric keypad return values for HELP, - and ENTER */
static char speckeypad[] = "-l-.n.\015M\015\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;
}

/*************************************************
 *  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] = '\0';
    strncpy(InpBuf,name,lname); InpBuf[lname] = '\0';
    mystrinfo.BufferPos = lname;

    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);
    if (reqwinup && ((reqwindow->Flags) & WINDOWACTIVE))
	ActivateWindow(mywindow);
    }

/*************************************************
*  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 */
	doindex('D');
	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 */
	cmd_beep(0L);
	break;

	default:
	if (c < ' ' || c > '~') break;
	if (p_wrap && wrap_flag && x >= MAXX) {
	    x = MINX;
	    doindex('D');
	    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(retstr, code, qual, maxlen, ia, local)
unsigned char *retstr;
unsigned int code,qual;
int local, maxlen;
APTR ia;
{
    unsigned int ctrl, alt, npad;
    int i,
	cmatch,
	length = 0;	/* length of returned string */
    unsigned char *p = retstr;
    static struct InputEvent ievent = {NULL, IECLASS_RAWKEY,0,0,0};

    *p = '\0';

    ctrl  = qual & IEQUALIFIER_CONTROL;
    alt	  = qual & (IEQUALIFIER_LALT | IEQUALIFIER_RALT);
    npad  = qual & IEQUALIFIER_NUMERICPAD;

    ievent.ie_Qualifier = qual;

    ievent.ie_Code = code;
    /* get previous codes from location pointed to by IAddress
     * this "magic" pointer is valid intil the IntiiMessage is
     * replied
     */
    ievent.ie_position.ie_addr = ia;
    length = RawKeyConvert(&ievent, retstr, (LONG)maxlen, NULL);
    if(length == 0)
	return length;

    *(p+length) = '\0';	/* Null terminate the value */

    if(npad && length == 1 && p_keyapp) { /* keypad (excluding HELP key)? */
	register char t = *p;
	if((t >= '0') && (t <= '9')) {
	    if(p_keyapp) {
		strcpy(p, "\033O");
		*(p+2) = keypad[t-'0'];
		*(p+3) = '\0';
		length = 3;
	    } /* else *p is correct */
	}
	else for(i = 0; speckeypad[i]; i += 3)
	    if(speckeypad[i] == t) {
		if(p_keyapp) {
		    strcpy(p, "\033O");
		    *(p+2) = speckeypad[i+1];
		    length = 3;
		}
		else *p = speckeypad[i+2];
		break;
	    }
    } else if((length == 3) && (strcmp(p, "\233?~") == 0)) {
	/* HELP key -- only gen something if in app keypad mode */
	if(p_keyapp) {
	    strcpy(p, "\033Om");
	    length = 3;
	} else {
	    *p = '\0';
	    length = 0;
	}
    } else if(length > 1 && retstr[0] == 0x9b) { /* cursor or F-keys? */
	cmatch = 0;
	for(i = 0; ckeys[i].in && !cmatch; i++) {
	    if(p_curapp
	      && strcmp((p+1), ckeys[i].in) == 0) {
		strcpy((p+1), ckeys[i].out_curapp);
		*p = 0x1b;
		length = strlen(ckeys[i].out_curapp)+1;
		cmatch = 1;
	    } else if(strcmp((p+1), ckeys[i].in) == 0) {
		strcpy((p+1), ckeys[i].out);
		*p = 0x1b;
		length = strlen(ckeys[i].out)+1;
		cmatch = 1;
	    }
	}
	if(!cmatch) { /* Not cursor, try F-keys */
	    for(i = 0; infkey[i]; i++) {
		if(strcmp((p+1), infkey[i]) == 0) {
		    if(i > 9)
			strcpy(p, p_F[i-10]);
		    else strcpy(p, p_f[i]);
		    if(!script_on && *p == p_keyscript) {
			script_start(p+1);
			*p = '\0';
			length = 0;
		    }
		    length = strlen(p);
		    break;
		}
	    }
	}
    } else if(ctrl && (length == 1)) {
	/* Control key shortcuts? */
	switch(*p) {
	case '6':
	    *p = 30;
	    break;
	case '2':
	case ' ': /* @ done by RawKeyConvert? */
	    if(!local)
		*p = (alt ? 128 : 0);
		break;
	case '-':
	case '?':
	    *p = 31;
		break;
	}
    } else if(alt && !local && length == 1)
	*p |= 0x80; /* Add hi bit if ALT is the only modifier */
    else if(p_bs_del && *p == 8    && length == 1)
	*p = 0x7f;
    else if(p_bs_del && *p == 0x7f && length == 1)
	*p = 8;

/*    if (ctrl) {   Are all of these taken care of?
	if (c > '`' && c <= 127) c -= 96;
	else if (c > '@' && c <= '_') c -= 64;
	else if (c == '6') c = 30;
	else if (c == '-' || c == '?') c = 31;
    } */
    for(i = 0; i < length; i++)
	sendchar(*(p++));
    return(length);
}

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;
    if (reqwinup && ((reqwindow->Flags) & WINDOWACTIVE))
	ActivateWindow(mywindow);
}
