/*
 *	Copyright 1984, 1985 by the Regents of the University of
 *	California and by Gregory Glenn Minshall.
 *
 *	Permission to use, copy, modify, and distribute these
 *	programs and their documentation for any purpose and
 *	without fee is hereby granted, provided that this
 *	copyright and permission appear on all copies and
 *	supporting documentation, the name of the Regents of
 *	the University of California not be used in advertising
 *	or publicity pertaining to distribution of the programs
 *	without specific prior permission, and notice be given in
 *	supporting documentation that copying and distribution is
 *	by permission of the Regents of the University of California
 *	and by Gregory Glenn Minshall.  Neither the Regents of the
 *	University of California nor Gregory Glenn Minshall make
 *	representations about the suitability of this software
 *	for any purpose.  It is provided "as is" without
 *	express or implied warranty.
 */

/* test stub for DataFrom3270, etc. */

#define DEFINEAIDS
#include "m4.out"		/* output of termcodes.m4 */
#include "ascebc.h"
#include "3270.h"
#include "screen.h"
#include "options.h"
#include "ectype.h"

#if	defined(DOSCCS) && !defined(lint)
static char sccsid[] = "@(#)keyboard.c	2.7	1/1/94";
#endif

#define EmptyChar	(ourPTail == ourBuffer)
#define FullChar	(ourPTail == ourBuffer+sizeof ourBuffer)

extern char	ascebc[NASCEBC][NASCII];

static char	ourBuffer[4000];

static char	*ourPHead = ourBuffer,
		*ourPTail = ourBuffer;

static int	trTbl = AE_IN;		/* which ascii->ebcdic tr table */

static int	HadAid = 0;		/* Had an AID haven't sent */

/* the following are global variables */

extern int UnLocked;		/* keyboard is UnLocked? */

/* Tab() - sets cursor to the start of the next unprotected field */
static void
Tab()
{
    register int i, j;

    i = CursorAddress;
    j = WhereAttrByte(CursorAddress);
    do {
	if (IsStartField(i) && IsUnProtected(ScreenInc(i))) {
	    break;
	}
	i = FieldInc(i);
    } while (i != j);
    if (IsStartField(i) && IsUnProtected(ScreenInc(i))) {
	CursorAddress = ScreenInc(i);
    } else {
	CursorAddress = SetBufferAddress(0,0);
    }
}


/* BackTab() - sets cursor to the start of the most recent field */

static void
BackTab()
{
    register int i;

    i = ScreenDec(CursorAddress);
    for (;;) {
	if (IsStartField(ScreenDec(i)) && IsUnProtected(i)) {
	    CursorAddress = i;
	    break;
	}
	if (i == CursorAddress) {
	    CursorAddress = SetBufferAddress(0,0);
	    break;
	}
	i = ScreenDec(i);
    }
}


/* EraseEndOfField - erase all characters to the end of a field */

static
EraseEndOfField()
{
    register int i;

    if (IsProtected(CursorAddress)) {
	RingBell();
    } else {
	TurnOnMdt(CursorAddress);
	i = CursorAddress;
	do {
	    AddHost(i, 0);
	    i = ScreenInc(i);
	} while ((i != CursorAddress) && IsUnProtected(i));
    }
}

/* Delete() - deletes a character from the screen
 *
 *	What we want to do is delete the section
 *	[where, from-1] from the screen,
 *	filling in with what comes at from.
 *
 *	The deleting continues to the end of the field (or
 *	until the cursor wraps).
 *
 *	From can be a start of a field.  We
 *	check for that.  However, there can't be any
 *	fields that start between where and from.
 *	We don't check for that.
 *
 *	Also, we assume that the protection status of
 *	everything has been checked by the caller.
 *
 */

static
Delete(where, from)
register int	where,		/* Where to start deleting from */
		from;		/* Where to pull back from */
{
    register int i;

    TurnOnMdt(where);			/* Only do this once in this field */
    i = where;
    do {
	if (IsStartField(from)) {
	    AddHost(i, 0);		/* Stick the edge at the start field */
	} else {
	    AddHost(i, GetHost(from));
	    from = ScreenInc(from);		/* Move the edge */
	}
	i = ScreenInc(i);
    } while ((!IsStartField(i)) && (i != where));
}

ColBak()
{
    register int i;

    i = ScreenLineOffset(CursorAddress);
    for (i = i-1; i >= 0; i--) {
	if (OptColTabs[i]) {
	    break;
	}
    }
    if (i < 0) {
	i = 0;
    }
    CursorAddress = SetBufferAddress(ScreenLine(CursorAddress), i);
}

ColTab()
{
    register int i;

    i = ScreenLineOffset(CursorAddress);
    for (i = i+1; i < LINESIZE; i++) {
	if (OptColTabs[i]) {
	    break;
	}
    }
    if (i >= LINESIZE) {
	i = LINESIZE-1;
    }
    CursorAddress = SetBufferAddress(ScreenLine(CursorAddress), i);
}

static
Home()
{
    register int i;
    register int j;

    i = SetBufferAddress(OptHome, 0);
    j = WhereLowByte(i);
    do {
	if (IsUnProtected(i)) {
	    CursorAddress = i;
	    return;
	}
	    /* the following could be a problem if we got here with an
	     * unformatted screen.  However, this is "impossible", since
	     * with an unformatted screen, the IsUnProtected(i) above
	     * should be true.
	     */
	i = ScreenInc(FieldInc(i));
    } while (i != j);
    CursorAddress = LowestScreen();
}

static
LastOfField(i)
register int	i;	/* position to start from */
{
    register int j;
    register int k;

    k = j = i;
    while (IsProtected(i) || Eisspace(GetHost(i))) {
	i = ScreenInc(i);
	if (i == j) {
	    break;
	}
    }
	    /* We are now IN a word IN an unprotected field (or wrapped) */
    while (!IsProtected(i)) {
	if (!Eisspace(GetHost(i))) {
	    k = i;
	}
	i = ScreenInc(i);
	if (i == j) {
	    break;
	}
    }
    return(k);
}


static
FlushChar()
{
    ourPTail = ourPHead = ourBuffer;
}


static
AddChar(character)
char	character;
{
    *ourPHead++ = character;
}


static void
SendUnformatted()
{
    register int i, j;
    register int Nulls;
    register int c;

			/* look for start of field */
    Nulls = 0;
    i = j = LowestScreen();
    do {
	c = GetHost(i);
	if (c == 0) {
	    Nulls++;
	} else {
	    while (Nulls) {
		Nulls--;
		AddChar(0x40);		/* put in blanks */
	    }
	    AddChar(c);
	}
	i = ScreenInc(i);
    } while (i != j);
}

static
SendField(i)
register int i;			/* where we saw MDT bit */
{
    register int j;
    register int k;
    register int Nulls;
    register int c;

			/* look for start of field */
    i = j = WhereLowByte(i);

    AddChar(ORDER_SBA);		/* set start field */
    AddChar(BufferTo3270_0(j));	/* set address of this field */
    AddChar(BufferTo3270_1(j));

    if (!IsStartField(j)) {
	Nulls = 0;
	k = ScreenInc(WhereHighByte(j));
	do {
	    c = GetHost(j);
	    if (c == 0) {
		Nulls++;
	    } else {
		while (Nulls) {
		    Nulls--;
		    AddChar(0x40);		/* put in blanks */
		}
		AddChar(c);
	    }
	    j = ScreenInc(j);
	} while ((j != k) && (j != i));
    }
    return(j);
}

/* Various types of reads... */
DoReadModified()
{
    register int i, j;

    if (AidByte) {
	AddChar(AidByte);
    } else {
	AddChar(0x60);
    }
    if ((AidByte != AID_PA1) && (AidByte != AID_PA2) && (AidByte != AID_PA3)
		    && (AidByte != AID_CLEAR)) {
	AddChar(BufferTo3270_0(CursorAddress));
	AddChar(BufferTo3270_1(CursorAddress));
	i = j = WhereAttrByte(LowestScreen());
	/* Is this an unformatted screen? */
	if (!IsStartField(i)) {		/* yes, handle separate */
	    SendUnformatted();
	} else {
	    do {
		if (HasMdt(i)) {
		    i = SendField(i);
		} else {
		    i = FieldInc(i);
		}
	    } while (i != j);
	}
    }
    ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail);
    if (ourPTail == ourPHead) {
	FlushChar();
	HadAid = 0;			/* killed that buffer */
    }
}

/* A read buffer operation... */

DoReadBuffer()
{
    register int i, j;

    if (AidByte) {
	AddChar(AidByte);
    } else {
	AddChar(0x60);
    }
    AddChar(BufferTo3270_0(CursorAddress));
    AddChar(BufferTo3270_1(CursorAddress));
    i = j = LowestScreen();
    do {
	if (IsStartField(i)) {
	    AddChar(ORDER_SF);
	    AddChar(BufferTo3270_1(FieldAttributes(i)));
	} else {
	    AddChar(GetHost(i));
	}
	i = ScreenInc(i);
    } while (i != j);
    ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail);
    if (ourPTail == ourPHead) {
	FlushChar();
	HadAid = 0;			/* killed that buffer */
    }
}
/* Try to send some data to host */

SendToIBM()
{
    extern int TransparentClock, OutputClock;

    if (TransparentClock == OutputClock) {
	if (HadAid) {
	    AddChar(AidByte);
	    HadAid = 0;
	} else {
	    AddChar(0xe8);
	}
	do {
	    ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail);
	} while (ourPTail != ourPHead);
	FlushChar();
    } else if (HadAid) {
	DoReadModified();
    }
    netflush();
}

/* This takes in one character from the keyboard and places it on the
 * screen.
 */

static
OneCharacter(c, insert)
int c;			/* character (Ebcdic) to be shoved in */
int insert;		/* are we in insert mode? */
{
    register int i, j;

    if (IsProtected(CursorAddress)) {
	RingBell();
	return;
    }
    if (insert) {
	/* is the last character in the field a blank or null? */
	i = ScreenDec(FieldInc(CursorAddress));
	j = GetHost(i);
	if (!Eisspace(j)) {
	    RingBell();
	    return;
	} else {
	    for (j = ScreenDec(i); i != CursorAddress;
			    j = ScreenDec(j), i = ScreenDec(i)) {
		AddHost(i, GetHost(j));
	    }
	}
    }
    AddHost(CursorAddress, c);
    TurnOnMdt(CursorAddress);
    CursorAddress = ScreenInc(CursorAddress);
    if (IsStartField(CursorAddress) &&
		((FieldAttributes(CursorAddress)&ATTR_AUTO_SKIP_MASK) ==
							ATTR_AUTO_SKIP_VALUE)) {
	Tab();
    }
}

/* go through data until an AID character is hit, then generate an interrupt */

DataFrom3270(buffer, count)
char	*buffer;		/* where the data is */
int	count;			/* how much data there is */
{
    int origCount;
    register int c;
    register int i;
    register int j;
    static int InsertMode = 0;	/* is the terminal in insert mode? */

    extern int OutputClock, TransparentClock;

    if (!UnLocked || HadAid) {
	if (HadAid) {
	    SendToIBM();
	    if (!EmptyChar) {
		return(0);			/* nothing to do */
	    }
	}
	if (!HadAid && (((*buffer&0xff) == TC_RESET) ||
			((*buffer&0xff) == TC_MASTER_RESET)) && EmptyChar) {
	    UnLocked = 1;
	}
	if (!UnLocked) {
	    return(0);
	}
    }
    /* now, either empty, or haven't seen aid yet */

    origCount = count;

    if (TransparentClock == OutputClock) {
	while (count) {
	    c = (*buffer++)&0xff;
	    count--;
	    if (IsAid(c)) {
		UnLocked = 0;
		InsertMode = 0;
		AidByte = TCtoAid(c);
		HadAid = 1;
	    } else {
		switch (c) {
		case TC_ESCAPE:
		    Stop3270(1);
		    command(0);
		    ConnectScreen();
		    break;

		case TC_RESET:
		case TC_MASTER_RESET:
		    UnLocked = 1;
		    break;

		default:
		    return(origCount-(count+1));
		}
	    }
	}
    }

    while (count) {
	c = (*buffer++)&0xff;
	count--;

	if (!IsTc(c)) {
			/* Add the character to the buffer */
	    OneCharacter(ascebc[trTbl][c], InsertMode);
	} else if (IsAid(c)) {		/* got Aid */
	    if (c == TC_CLEAR) {
		LocalClear3270();
	    }
	    UnLocked = 0;
	    InsertMode = 0;		/* just like a 3278 */
	    AidByte = TCtoAid(c);
	    HadAid = 1;
	    SendToIBM();
	    return(origCount-count);
	} else {

			/* non-AID TC character */
	    switch (c) {

	    case TC_ERASE:
		if (IsProtected(ScreenDec(CursorAddress))) {
		    RingBell();
		} else {
		    CursorAddress = ScreenDec(CursorAddress);
		    Delete(CursorAddress, ScreenInc(CursorAddress));
		}
		break;

	    case TC_WERASE:
		j = CursorAddress;
		i = ScreenDec(j);
		if (IsProtected(i)) {
		    RingBell();
		} else {
		    while ((!IsProtected(i) && Eisspace(GetHost(i)))
							&& (i != j)) {
			i = ScreenDec(i);
		    }
		    /* we are pointing at a character in a word, or
		     * at a protected position
		     */
		    while ((!IsProtected(i) && !Eisspace(GetHost(i)))
							&& (i != j)) {
			i = ScreenDec(i);
		    }
		    /* we are pointing at a space, or at a protected
		     * position
		     */
		    CursorAddress = ScreenInc(i);
		    Delete(CursorAddress, j);
		}
		break;

	    case TC_FERASE:
		if (IsProtected(CursorAddress)) {
		    RingBell();
		} else {
		    CursorAddress = ScreenInc(CursorAddress);	/* for btab */
		    BackTab();
		    EraseEndOfField();
		}
		break;

	    case TC_RESET:
		InsertMode = 0;
		break;

	    case TC_MASTER_RESET:
		InsertMode = 0;
		RefreshScreen();
		break;

	    case TC_UP:
		CursorAddress = ScreenUp(CursorAddress);
		break;

	    case TC_LEFT:
		CursorAddress = ScreenDec(CursorAddress);
		break;

	    case TC_RIGHT:
		CursorAddress = ScreenInc(CursorAddress);
		break;

	    case TC_DOWN:
		CursorAddress = ScreenDown(CursorAddress);
		break;

	    case TC_DELETE:
		if (IsProtected(CursorAddress)) {
		    RingBell();
		} else {
		    Delete(CursorAddress, ScreenInc(CursorAddress));
		}
		break;

	    case TC_INSRT:
		InsertMode = !InsertMode;
		break;

	    case TC_HOME:
		Home();
		break;

	    case TC_NL:
		/* The algorithm is to look for the first unprotected
		 * column after column 0 of the following line.  Having
		 * found that unprotected column, we check whether the
		 * cursor-address-at-entry is at or to the right of the
		 * LeftMargin AND the LeftMargin column of the found line
		 * is unprotected.  If this conjunction is true, then
		 * we set the found pointer to the address of the LeftMargin
		 * column in the found line.
		 * Then, we set the cursor address to the found address.
		 */
		i = SetBufferAddress(ScreenLine(ScreenDown(CursorAddress)), 0);
		j = ScreenInc(WhereAttrByte(CursorAddress));
		do {
		    if (IsUnProtected(i)) {
			break;
		    }
		    /* Again (see comment in Home()), this COULD be a problem
		     * with an unformatted screen.
		     */
		    /* If there was a field with only an attribute byte,
		     * we may be pointing to the attribute byte of the NEXT
		     * field, so just look at the next byte.
		     */
		    if (IsStartField(i)) {
			i = ScreenInc(i);
		    } else {
			i = ScreenInc(FieldInc(i));
		    }
		} while (i != j);
		if (!IsUnProtected(i)) {	/* couldn't find unprotected */
		    i = SetBufferAddress(0,0);
		}
		if (OptLeftMargin <= ScreenLineOffset(CursorAddress)) {
#ifndef pdp11
		    if (IsUnProtected(SetBufferAddress(ScreenLine(i),
							    OptLeftMargin))) {
#else
		    CursorAddress = SetBufferAddress(ScreenLine(i),
							    OptLeftMargin);
		    if (IsUnProtected(CursorAddress)) {
#endif
			i = SetBufferAddress(ScreenLine(i), OptLeftMargin);
		    }
		}
		CursorAddress = i;
		break;

	    case TC_EINP:
		i = j = ScreenInc(WhereAttrByte(LowestScreen()));
		do {
		    if (IsUnProtected(i)) {
			AddHost(i, 0);
			TurnOnMdt(i);
		    } else {
			    /* FieldInc() puts us at the start of the next
			     * field.
			     *
			     * We don't want to skip to the start of the
			     * next field if we are on the attribute byte,
			     * since we may be skipping over an otherwise
			     * unprotected field.
			     *
			     * Also, j points at the first byte of the first
			     * field on the screen, unprotected or not.  If
			     * we never point there, we might loop here for
			     * ever.
			     */
			if (!IsStartField(i)) {
			    i = FieldInc(i);
			}
		    }
		    i = ScreenInc(i);
		} while (i != j);
		Home();		/* get to home position */
		break;

	    case TC_EEOF:
		EraseEndOfField();
		break;

	    case TC_FM:
		if (IsProtected(CursorAddress)) {
		    RingBell();
		} else {
		    OneCharacter(EBCDIC_FM, InsertMode);  /* Add field mark */
		}
		break;

	    case TC_DP:
		if (IsProtected(CursorAddress)) {
		    RingBell();
		    break;
		}
		OneCharacter(EBCDIC_DUP, InsertMode);	/* Add dup character */
		Tab();
		break;

	    case TC_TAB:
		Tab();
		break;

	    case TC_BTAB:
		BackTab();
		break;

#ifdef	NOTUSED			/* Actually, this is superseded by unix flow
				 * control.
				 */
	    case TC_XOFF:
		Flow = 0;			/* stop output */
		break;

	    case TC_XON:
		if (!Flow) {
		    Flow = 1;			/* turn it back on */
		    DoTerminalOutput();
		}
		break;
#endif	/* NOTUSED */

	    case TC_ESCAPE:
		/* FlushChar(); do we want to flush characters from before? */
		Stop3270(1);
		command(0);
		ConnectScreen();
		break;

	    case TC_DISC:
		Stop3270(1);
		suspend();
		ConnectScreen();
		break;

	    case TC_RESHOW:
		RefreshScreen();
		break;

	    case TC_SETTAB:
		OptColTabs[ScreenLineOffset(CursorAddress)] = 1;
		break;

	    case TC_DELTAB:
		OptColTabs[ScreenLineOffset(CursorAddress)] = 0;
		break;

	    case TC_CLRTAB:
		for (i = 0; i < sizeof OptColTabs; i++) {
		    OptColTabs[i] = 0;
		}
		break;

	    case TC_COLTAB:
		ColTab();
		break;

	    case TC_COLBAK:
		ColBak();
		break;

	    case TC_INDENT:
		ColTab();
		OptLeftMargin = ScreenLineOffset(CursorAddress);
		break;

	    case TC_UNDENT:
		ColBak();
		OptLeftMargin = ScreenLineOffset(CursorAddress);
		break;

	    case TC_SETMRG:
		OptLeftMargin = ScreenLineOffset(CursorAddress);
		break;

	    case TC_SETHOM:
		OptHome = ScreenLine(CursorAddress);
		break;

		/*
		 * Point to first character of next unprotected word on
		 * screen.
		 */
	    case TC_WORDTAB:
		i = CursorAddress;
		while (!IsProtected(i) && !Eisspace(GetHost(i))) {
		    i = ScreenInc(i);
		    if (i == CursorAddress) {
			break;
		    }
		}
		/* i is either protected, a space (blank or null),
		 * or wrapped
		 */
		while (IsProtected(i) || Eisspace(GetHost(i))) {
		    i =  ScreenInc(i);
		    if (i == CursorAddress) {
			break;
		    }
		}
		CursorAddress = i;
		break;

	    case TC_WORDBACKTAB:
		i = ScreenDec(CursorAddress);
		while (IsProtected(i) || Eisspace(GetHost(i))) {
		    i = ScreenDec(i);
		    if (i == CursorAddress) {
			break;
		    }
		}
		    /* i is pointing to a character IN an unprotected word
		     * (or i wrapped)
		     */
		while (!Eisspace(GetHost(i))) {
		    i = ScreenDec(i);
		    if (i == CursorAddress) {
			break;
		    }
		}
		CursorAddress = ScreenInc(i);
		break;

			/* Point to last non-blank character of this/next
			 * unprotected word.
			 */
	    case TC_WORDEND:
		i = ScreenInc(CursorAddress);
		while (IsProtected(i) || Eisspace(GetHost(i))) {
		    i = ScreenInc(i);
		    if (i == CursorAddress) {
			break;
		    }
		}
			/* we are pointing at a character IN an
			 * unprotected word (or we wrapped)
			 */
		while (!Eisspace(GetHost(i))) {
		    i = ScreenInc(i);
		    if (i == CursorAddress) {
			break;
		    }
		}
		CursorAddress = ScreenDec(i);
		break;

			/* Get to last non-blank of this/next unprotected
			 * field.
			 */
	    case TC_FIELDEND:
		i = LastOfField(CursorAddress);
		if (i != CursorAddress) {
		    CursorAddress = i;		/* We moved; take this */
		} else {
		    j = FieldInc(CursorAddress);	/* Move to next field */
		    i = LastOfField(j);
		    if (i != j) {
			CursorAddress = i;	/* We moved; take this */
		    }
			/* else - nowhere else on screen to be; stay here */
		}
		break;

	    default:
		RingBell();		/* We don't handle this yet */
	    }
	}
    }
    return(origCount-count);
}
