/*
 * KON - Kanji ON Linux Console - Copyright (C) 1992, 1993, 1994 Takashi
 * MANABE (manabe@Roy.dsl.tutics.tut.ac.jp)
 * 
 * KON is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * KON is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 675
 * Mass Ave, Cambridge, MA 02139, USA.
 */

#include	"big5con.h"

struct _con_info con;

#define	CHAR_NUL	'\x00'
#define	CHAR_BEL	'\x07'
#define	CHAR_BS		'\x08'
#define	CHAR_HT		'\x09'
#define	CHAR_LF		'\x0A'
#define	CHAR_VT		'\x0B'
#define	CHAR_FF		'\x0C'
#define	CHAR_CR		'\x0D'
#define	CHAR_SO		'\x0E'
#define	CHAR_SI		'\x0F'
#define	CHAR_XON	'\x11'
#define	CHAR_XOFF	'\x12'
#define	CHAR_CAN	'\x18'
#define	CHAR_SUB	'\x1A'
#define	CHAR_ESC	'\x1B'
#define	CHAR_DEL	'\x7F'
#define	CHAR_CSI	'\x9B'
#define	CHAR_SS2	'\x8E'

#define	LEN_REPORT	9

struct attrStack {
    struct attrStack *prev;
    u_char          x, y, attr, bcol, fcol;
};

static struct attrStack *saveAttr;

static int      scroll;
struct langInfo lInfo;

static
void 
SaveAttr()
{
    struct attrStack *tmp;

    tmp = (struct attrStack *) malloc(sizeof(struct attrStack));
    if (saveAttr)
	tmp->prev = saveAttr;
    else
	tmp->prev = NULL;
    saveAttr = tmp;
    saveAttr->x = con.x;
    saveAttr->y = con.y;
    saveAttr->attr = con.attr;
    saveAttr->fcol = con.fcol;
    saveAttr->bcol = con.bcol;
}

static
void 
RestoreAttr()
{
    struct attrStack *tmp;

    if (saveAttr) {
	con.x = saveAttr->x;
	con.y = saveAttr->y;
	con.attr = saveAttr->attr;
	con.fcol = saveAttr->fcol;
	con.bcol = saveAttr->bcol;
	tmp = saveAttr;
	saveAttr = tmp->prev;
	free(tmp);
    }
}

static void 
EscSetAttr(int col)
{
    static u_char   table[] = {0, 4, 2, 6, 1, 5, 3, 7};
    u_char          swp;

    switch (col) {
    case 0:			/* off all attributes */
	con.bcol = 0;
	con.fcol = 7;
	con.attr = 0;
	break;
    case 1:
	con.attr |= ATTR_HIGH;
	if (con.fcol)
	    con.fcol |= 8;
	break;
    case 21:
	con.attr &= ~ATTR_HIGH;
	con.fcol &= ~8;
	break;
    case 4:
	con.attr |= ATTR_ULINE;
	con.bcol |= 8;
	break;
    case 24:
	con.attr &= ~ATTR_ULINE;
	con.bcol &= ~8;
	break;
    case 7:
	if (!(con.attr & ATTR_REVERSE)) {
	    con.attr |= ATTR_REVERSE;
	    swp = con.fcol & 7;
	    if (con.attr & ATTR_ULINE)
		swp |= 8;
	    con.fcol = con.bcol & 7;
	    if (con.attr & ATTR_HIGH && con.fcol)
		con.fcol |= 8;
	    con.bcol = swp;
	}
	break;
    case 27:
	if (con.attr & ATTR_REVERSE) {
	    con.attr &= ~ATTR_REVERSE;
	    swp = con.fcol & 7;
	    if (con.attr & ATTR_ULINE)
		swp |= 8;
	    con.fcol = con.bcol & 7;
	    if (con.attr & ATTR_HIGH && con.fcol)
		con.fcol |= 8;
	    con.bcol = swp;
	}
	break;
    default:
	if (col >= 30 && col <= 37) {
	    swp = table[col - 30];
	    if (con.attr & ATTR_REVERSE) {
		if (con.attr & ATTR_ULINE)
		    swp |= 8;
		con.bcol = swp;
	    } else {
		if (con.attr & ATTR_HIGH)
		    swp |= 8;
		con.fcol = swp;
	    }
	} else if (col >= 40 && col <= 47) {
	    swp = table[col - 40];
	    if (con.attr & ATTR_REVERSE) {
		if (con.attr & ATTR_HIGH)
		    swp |= 8;
		con.fcol = swp;
	    } else {
		if (con.attr & ATTR_ULINE)
		    swp |= 8;
		con.bcol = swp;
	    }
	}
	break;
    }
}

static void 
VtSetMode(u_char mode, bool sw)
{
    switch (mode) {
	case 4:
	con.ins = sw;
	break;
    case 25:
	cInfo.sw = sw;
	break;
    }
}

static
void 
EscReport(u_char mode, u_char arg)
{
    static char     report[LEN_REPORT];

    switch (mode) {
    case 'n':
	if (arg == 6) {
            int x = (con.x < con.xmax) ? con.x : con.xmax;
            int y = (con.y < con.ymax) ? con.y : con.ymax;
            sprintf(report, "\x1B[%d;%dR", y + 1, x + 1);
	} else if (arg == 5)
	    strcpy(report, "\x1B[0n\0");
	break;
    case 'c':
	if (arg == 0)
	    strcpy(report, "\x1B[?6c\0");
	break;
    }
    write(masterPty, report, strlen(report));
}

#define	MAX_NARG	8

static
void 
EscBracket(u_char ch)
{
    u_char          n;
    static u_short  varg[MAX_NARG], narg, question;

    if (ch >= '0' && ch <= '9') {
	varg[narg] = (varg[narg] * 10) + (ch - '0');
    } else if (ch == ';') {
	if (narg < MAX_NARG)
	    narg++;
	else
	    con.esc = NULL;
    } else {
	con.esc = NULL;
	switch (ch) {
	case 'K':
	    TextClearEol(varg[0]);
	    break;
	case 'J':
	    TextClearEos(varg[0]);
	    break;
	case 'A':
	    con.y -= varg[0] ? varg[0] : 1;
	    if (con.y < con.ymin) {
		scroll -= con.y - con.ymin;
		con.y = con.ymin;
	    }
	    break;
	case 'B':
	    con.y += varg[0] ? varg[0] : 1;
	    if (con.y > con.ymax - 1) {
		scroll += con.y - con.ymax + 1;
		con.y = con.ymax - 1;
	    }
	    break;
	case 'C':
	    con.x += varg[0] ? varg[0] : 1;
	    break;
	case 'D':
	    con.x -= varg[0] ? varg[0] : 1;
	    break;
	case 'G':
	    con.x = varg[0] ? varg[0] - 1 : 0;
	    break;
	case 'P':
	    TextDeleteChar(varg[0] ? varg[0] : 1);
	    break;
	case '@':
	    TextInsertChar(varg[0] ? varg[0] : 1);
	    break;
	case 'L':
	    TextMoveDown(con.y, con.ymax - 1,
			 varg[0] ? varg[0] : 1);
	    break;
	case 'M':
	    TextMoveUp(con.y, con.ymax - 1,
		       varg[0] ? varg[0] : 1);
	    break;
	case 'H':
	case 'f':
	    if (varg[1])
		con.x = varg[1] - 1;
	    else
		con.x = 0;
	case 'd':
	    con.y = varg[0] ? varg[0] - 1 : 0;
	    break;
	case 'm':
	    for (n = 0; n <= narg; n++)
		EscSetAttr(varg[n]);
	    break;
	case 'r':
	    con.ymin = varg[0] ? (varg[0] - 1) : 0;
	    con.ymax = varg[1] ? varg[1] : dInfo.tymax;
	    con.x = 0;
	    con.y = con.ymin;
	    if (con.ymin || con.ymax != dInfo.tymax)
		con.soft = TRUE;
	    else
		con.soft = FALSE;
	    break;
	case 'l':
	    for (n = 0; n <= narg; n++)
		VtSetMode(varg[n], FALSE);
	    break;
	case 'h':
	    for (n = 0; n <= narg; n++)
		VtSetMode(varg[n], TRUE);
	    break;
	case '?':
	    question = TRUE;
	    con.esc = EscBracket;
	    break;
	case 's':
	    SaveAttr();
	    break;
	case 'u':
	    RestoreAttr();
	    break;
	case 'n':
	case 'c':
	    if (question != TRUE)
		EscReport(ch, varg[0]);
	    break;
	case 'R':
	    break;
	}
	if (con.esc == NULL)
	    question = narg = varg[0] = varg[1] = 0;
    }
}

static
void 
EscSetDCodeG0(u_char ch)
{
    int             i;

    switch (ch) {
    case '(':			/* EscSetDCodeG0 */
    case ')':			/* EscSetDCodeG1 */
	return;
    case '@':
	ch = 'B';
    default:
	i = 0;
	while (fDRegs[i].sign0) {
	    if (fDRegs[i].sign0 == ch) {
		con.db = (u_char) i | LATCH_1;
		break;
	    }
	    i++;
	}
	con.trans = CS_DBCS;
	break;
    }
    con.esc = NULL;
}

static
void 
EscSetSCodeG0(u_char ch)
{
    int             i = 0;

    switch (ch) {
    case 'U':
	con.g[0] = CS_GRAPH;
	break;
	/*
	 * Thank zmx@bbs.ee.ntu.edu.tw Fix text-table display (sysinstall
	 * under b5c+screen)
	 */
    case '0':
	con.g[0] = CS_GRAPH;
    case 'B':
	con.g[0] = CS_LEFT;
    default:
	while (fSRegs[i].sign0) {
	    if (fSRegs[i].sign0 == ch) {
		con.sb = (u_char) i;
		con.g[0] = CS_LEFT;
		break;
	    } else if (fSRegs[i].sign1 == ch) {
		con.sb = (u_char) i;
		con.g[0] = CS_RIGHT;
		break;
	    }
	    i++;
	}
    }
    con.trans = con.g[0];
    con.esc = NULL;
}

static
void 
EscSetSCodeG1(u_char ch)
{
    switch (ch) {
	case 'U':
	con.g[1] = CS_LEFT;
	break;
    case '0':
	con.g[1] = CS_GRAPH;
	break;
    case 'A':
    case 'J':
    case 'B':
	break;
    }
    con.trans = con.g[1];
    con.esc = NULL;
}

static
void 
EscStart(u_char ch)
{
    con.esc = NULL;
    switch (ch) {
    case '[':
	con.esc = EscBracket;
	break;
    case '$':			/* Set Double Byte Code */
	con.esc = EscSetDCodeG0;
	break;
    case '(':			/* Set 94 to G0 */
    case ',':			/* Set 96 to G0 */
	con.esc = EscSetSCodeG0;
	break;
    case ')':			/* Set G1 */
	con.esc = EscSetSCodeG1;
	break;
    case 'E':
	con.x = 0;
    case 'D':
	if (con.y == con.ymax - 1) /* by b5c */
	    scroll++;
	else
	    con.y++;
	break;
    case 'M':
	if (con.y == con.ymin)
	    scroll--;
	else
	    con.y--;
	break;
    case 'c':
	con.fcol = 7;
	con.attr = 0;
	con.bcol = 0;
	con.trans = CS_LEFT;
	con.sb = lInfo.sb;
	con.db = lInfo.db | LATCH_1;
    case '*':
	con.x = con.y = 0;
	TextClearAll();
	break;
    case '7':
	SaveAttr();
	break;
    case '8':
	RestoreAttr();
	break;
    }
}

static          inline
                bool 
isdb1(u_char c)
{
    switch (lInfo.sc) {
	case CODE_SJIS:
	return (c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFC);
    case CODE_BIG5:
	return (c >= 0xa1 && c <= 0xfe);
    default:
	return (c & 0x80);
    }
}

static          inline
                bool 
isdb2(u_char c)
{
    switch (lInfo.sc) {
	case CODE_BIG5:
	return ((c >= 0x40 && c <= 0x7e) || (c >= 0xa1 && c <= 0xfe));
    default:
	return (1);
    }
}

void 
VtEmu(const char *buff, int nchars)
{
    u_char          ch;
    int             addr;

    while (nchars-- > 0) {
	ch = *buff;
	buff++;
	if (!ch)
	    continue;
	if (con.esc) {
	    con.esc(ch);
	} else
	    switch (ch) {
	    case CHAR_BEL:
		Beep();
		break;
	    case CHAR_DEL:
		break;
	    case CHAR_BS:
		if (con.x)
		    con.x--;
		break;
	    case CHAR_HT:
		con.x += con.tab - (con.x % con.tab);
		if (con.x > con.xmax)
		    con.x -= con.xmax + 1;
		else
		    break;
	    case CHAR_VT:
	    case CHAR_FF:
#if 1
		con.trans = CS_LEFT;
		con.sb = lInfo.sb;
		con.db = lInfo.db | LATCH_1;
#endif
	    case CHAR_LF:
		if (con.y == con.ymax - 1)
		    scroll++;
		else
		    con.y++;
		break;
	    case CHAR_CR:
		con.x = 0;
		break;
	    case CHAR_ESC:
		con.esc = EscStart;
		continue;
	    case CHAR_SO:
		con.trans = con.g[1] | G1_SET;
		continue;
	    case CHAR_SI:
		con.trans = con.g[0];
		continue;
		/* case ' ': con.trans = CS_LEFT; */
	    default:
		if (con.x == con.xmax + 1) {
		    con.x = 0;
		    if (con.y == con.ymax - 1)
			scroll++;
		    else
			con.y++;
		    buff--;
		    nchars++;
		    break;
		}
		if (isPrevWT1() && isdb2(ch)) {
		    if (con.ins)
			TextInsertChar(1);
		    TextWput2(ch);
		    con.x++;
		    continue;
		} else if (con.x < con.xmax && (con.trans == CS_DBCS
				  || (isdb1(ch) && con.trans == CS_LEFT))) {
		    if (con.ins)
			TextInsertChar(1);
		    TextWput1(ch);
		    con.x++;
		    continue;
		} else {
		    if (con.ins)
			TextInsertChar(1);
		    TextSput(con.trans == CS_RIGHT ? ch | 0x80 : ch);
		    con.x++;
		    continue;
		}
	    }
	if (scroll > 0) {
	    ScrollUp(scroll);
	} else if (scroll < 0) {
	    ScrollDown(-scroll);
	}
	scroll = 0;
    }
}

static int 
ConfigCoding(const char *confstr)
{
    char            reg[3][MAX_COLS];
    int             n, i;

    *reg[0] = *reg[1] = *reg[2] = '\0';
    sscanf(confstr, "%s %s %s", reg[0], reg[1], reg[2]);
    for (i = 0; i < 3 && *reg[i]; i++) {
	n = (int)CodingByRegistry(reg[i]);
	if (n < 0) {
	    if (!strcasecmp(reg[i], "EUC"))
		lInfo.sc = CODE_EUC;
	    else if (!strcasecmp(reg[i], "SJIS"))
		lInfo.sc = CODE_SJIS;
	    else if (!strcasecmp(reg[i], "BIG5"))
		lInfo.sc = CODE_BIG5;
	    else
		lInfo.sc = 0;
	} else if (n & CHR_DBC)
	    lInfo.db = n & ~CHR_DFLD;
	else
	    lInfo.sb = n & ~CHR_SFLD;
    }
    return SUCCESS;
}

void 
VtInit(void)
{
    con.text_mode = TRUE;
    DefineCap("Coding", ConfigCoding, "ISO8859-1 BIG5-0 BIG5");
}

void 
VtStart(void)
{
    con.x = con.y = 0;
    con.xmax = dInfo.txmax;
    con.ymax = dInfo.tymax;
    con.tab = 8;
    con.fcol = 7;
    con.attr = 0;
    con.esc = NULL;
    con.g[0] = con.g[1] = CS_LEFT;
    con.trans = con.soft = con.ins = FALSE;
    con.sb = lInfo.sb;
    con.db = lInfo.db | LATCH_1;
    con.active = cInfo.sw = TRUE;
}
