#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vt.h"
#include "misc.h"
#include "xio.h"
#include "vbi.h"
#include "edit.h"

static void edwin_event(struct edwin *w, struct vt_event *ev);

static void
cursor(struct edwin *w, int x, int y)
{
    u8 buf0[40];
    u8 buf1[40];
    u8 buf2[32];

    if (w->edline)
	return;
    if (x < 0 || x >= W || y < 0 || y >= H)
	return;
    if (y == 0 && x < 8)
	return;

    xio_get_line(w->xw, 0, buf0);
    xio_get_line(w->xw, y, buf1);

    sprintf(buf2, "%2d\3%02x  \7    ", y, buf1[x]);
    buf2[6] = "CI"[w->mode];
    memcpy(buf0, buf2, 8);

    xio_put_line(w->xw, 0, buf0);
    xio_set_cursor(w->xw, w->x = x, w->y = y);
}

static void
adv_cursor(struct edwin *w)
{
    if (w->x < W-1)
	cursor(w, w->x+1, w->y);
    else if (w->y < H-1)
	cursor(w, 0, w->y+1);
    else
	cursor(w, 8, 0);
}

static void
set_char(struct edwin *w, int c)
{
    u8 buf[40];

    xio_get_line(w->xw, w->y, buf);
    buf[w->x] = c;
    xio_put_line(w->xw, w->y, buf);
}

static int
get_char(struct edwin *w)
{
    u8 buf[40];

    xio_get_line(w->xw, w->y, buf);
    return buf[w->x];
}

static void
set_gfx(struct edwin *w, int i)
{
    int c = get_char(w);

    if ((c & 0xa0) != 0x20)
	c = 0x20;
    switch (i)
    {
	case 1:	c ^= 0x10;	break;
	case 2:	c ^= 0x40;	break;
	case 4:	c ^= 0x04;	break;
	case 5:	c ^= 0x08;	break;
	case 7:	c ^= 0x01;	break;
	case 8:	c ^= 0x02;	break;
	case 0:	c = 0x20;	break;
	case 9:	c = 0x7f;	break;
	case 3:	c ^= 0x5f;	break;
	case 6:	c ^= 0x5f;	break;
    }
    set_char(w, c);
}

static void
set_mode(struct edwin *w, int on, int off)
{
    u8 buf[40];
    int x, c;

    xio_get_line(w->xw, w->y, buf);

    for (c = x = 0; x <= w->x; ++x)
	if (buf[x] == on || buf[x] == off)
	    c = buf[x];
    if (c == on)
	set_char(w, off);
    else
	set_char(w, on);
}

static void
del_char(struct edwin *w)
{
    u8 buf[40];
    int x;

    xio_get_line(w->xw, w->y, buf);
    for (x = w->x; x < W-1; ++x)
	buf[x] = buf[x+1];
    buf[x] = ' ';
    xio_put_line(w->xw, w->y, buf);
}

static void
ins_char(struct edwin *w, int c)
{
    u8 buf[40];
    int x, t;

    xio_get_line(w->xw, w->y, buf);
    for (x = w->x; x < W; ++x)
	t = buf[x], buf[x] = c, c = t;
    xio_put_line(w->xw, w->y, buf);
}

static void
del_line(struct edwin *w)
{
    u8 buf[40];
    int y;
    
    for (y = w->y; y < H-1; ++y)
    {
	xio_get_line(w->xw, y+1, buf);
	xio_put_line(w->xw, y, buf);
    }
}

static void
ins_line(struct edwin *w)
{
    u8 buf[40];
    u8 buf2[40];
    int y;
    
    memset(buf2, ' ', sizeof(buf2));
    for (y = w->y; y < H; ++y)
    {
	xio_get_line(w->xw, y, buf);
	xio_put_line(w->xw, y, buf2);
	memcpy(buf2, buf, sizeof(buf2));
    }
}


static void
do_save(struct edwin *w, u8 *name)
{
    u8 buf[40];
    int x, y;
    FILE *fp;

    w->edline = 0;
    xio_put_line(w->xw, 24, w->tmpbuf);
    cursor(w, w->x, w->y);

    if (name == 0 || *name == 0)
	return;

    fp = fopen(name, "w");
    if (fp == 0)
    {
	ioerror(name);
	return;
    }
    for (y = 0; y < 25; ++y)
    {
	xio_get_line(w->xw, y, buf);
	if (y == 0)
	    memset(buf, ' ', 8);
	if (y == 0)
	    fprintf(fp, "#ifndef HELP_HEADER\n");
	if (y == 5)
	    fprintf(fp, "#else\nHELP_HEADER\n#endif\n");
	fprintf(fp, "\"");
	for (x = 0; x < 40; ++x)
	    if ((buf[x] & 0x7f) < 0x20 || buf[x] == 0x7f || buf[x] == '"')
	    {
		if (x == 39 || buf[x+1] < '0' || buf[x+1] > '9')
		    fprintf(fp, "\\%o", buf[x]);
		else
		    fprintf(fp, "\\%03o", buf[x]);
	    }
	    else
		fprintf(fp, "%c", buf[x]);
	fprintf(fp, "\",\n");
    }
    fclose(fp);
    error("%s dumped", name);
}

static void
save(struct edwin *w)
{
    u8 name[64];

    if (w->subno == ANY_SUB)
	sprintf(name, "vt%03x.out", w->pgno);
    else
	sprintf(name, "vt%03x-%02x.out", w->pgno, w->subno & 0xff);

    xio_get_line(w->xw, 24, w->tmpbuf);
    w->edline = edline_new(w->xw, "Save as:\2", name, do_save, w);
}


struct edwin *
edwin_new(struct xio *xio, struct vbi *vbi, int pgno, int subno)
{
    struct edwin *w;
    struct vt_page *vtp;
    u8 buf[64];
    int i;

    if (not(w = malloc(sizeof(*w))))
	goto fail1;

    if (not(w->xw = xio_open_win(xio, 0)))
	goto fail2;

    w->pgno = pgno;
    w->subno = subno;

    vtp = vbi_query_page(vbi, pgno, subno);
    if (vtp)
	for (i = 0; i < 25; ++i)
	    xio_put_line(w->xw, i, vtp->data[i]);
    else
	xio_clear_win(w->xw);
    
    w->mode = 1;
    w->edline = 0;
    if (w->subno == ANY_SUB)
	sprintf(buf, "Editor %03x", w->pgno);
    else
	sprintf(buf, "Editor %03x/%x", w->pgno, w->subno);
    xio_title(w->xw, buf);
    xio_set_concealed(w->xw, w->reveal = 1);
    xio_set_handler(w->xw, edwin_event, w);
    cursor(w, W/2, H/2);
    return w;

fail2:
    free(w);
fail1:
    return 0;
}

static void
edwin_close(struct edwin *w)
{
    if (w->edline)
	edline_abort(w->edline);
    xio_close_win(w->xw, 1);
    free(w);
}

static int
ins_key(struct edwin *w, int key, int shift)
{
    switch (key)
    {
	case '\17': /* ^O for the bullet */
	    key = 0x7f;
	    // fall through
	case 0x20 ... 0x7e:
	case 0xa0 ... 0xff:
	    set_char(w, key);
	    adv_cursor(w);
	    return 1;
	case '\t':
	    w->mode = 0;
	    return 2;
    }
    return 0;
}

static int
cmd_key(struct edwin *w, int key, int shift)
{
    switch (key)
    {
	case '\e':
	case 'q':		edwin_close(w);			return 1;
	case '\r':
	case '\n':		w->x = W; adv_cursor(w);	return 1;
	case '\t':		w->mode = 1;			return 2;
	case KEY_F(1):		save(w);			return 1;

	case KEY_INS:
	    if (shift)
		ins_line(w);
	    else
		ins_char(w, ' ');
	    return 2;

	case '\b':
	    if (shift || w->x == 0)
		return 0;
	    w->x--;
	    //fall through
	case 0x7f:
	case KEY_DEL:
	    if (shift)
		del_line(w);
	    else
		del_char(w);
	    return 2;

	case KEY_LEFT:		cursor(w, w->x - 1, w->y);	return 1;
	case KEY_RIGHT:		cursor(w, w->x + 1, w->y);	return 1;
	case KEY_UP:		cursor(w, w->x, w->y - 1);	return 1;
	case KEY_DOWN:		cursor(w, w->x, w->y + 1);	return 1;

	case 'k': /*black*/	set_char(w, 0x00);		return 2;
	case 'K':		set_char(w, 0x10);		return 2;
	case 'r': /*red*/	set_char(w, 0x01);		return 2;
	case 'R':		set_char(w, 0x11);		return 2;
	case 'g': /*green*/	set_char(w, 0x02);		return 2;
	case 'G':		set_char(w, 0x12);		return 2;
	case 'y': /*yellow*/	set_char(w, 0x03);		return 2;
	case 'Y':		set_char(w, 0x13);		return 2;
	case 'b': /*blue*/	set_char(w, 0x04);		return 2;
	case 'B':		set_char(w, 0x14);		return 2;
	case 'v': /*violet*/	set_char(w, 0x05);		return 2;
	case 'V':		set_char(w, 0x15);		return 2;
	case 'c': /*cyan*/	set_char(w, 0x06);		return 2;
	case 'C':		set_char(w, 0x16);		return 2;
	case 'w': /*white*/	set_char(w, 0x07);		return 2;
	case 'W':		set_char(w, 0x17);		return 2;

	case 's': /*new bg*/	set_char(w, 0x1d);		return 2;
	case 'S': /*blk bg*/	set_char(w, 0x1c);		return 2;
	case 'e': /*concealed*/	set_char(w, 0x18);		return 2;
	case 'E': xio_set_concealed(w->xw, w->reveal ^= 1);	return 1;

	case '0' ... '9':	set_gfx(w, key - '0');		return 2;

	case 'f': /*flash*/	set_mode(w, 0x08, 0x09);	return 2;
	case 'd': /*dbl*/	set_mode(w, 0x0d, 0x0c);	return 2;
	case 'h': /*hold gfx*/	set_mode(w, 0x1e, 0x1f);	return 2;
	case 'x': /*box*/	set_mode(w, 0x0b, 0x0a);	return 2;

	case ' ':
	    set_char(w, key);
	    return 2;
    }
    return 0;
}

static void
edwin_event(struct edwin *w, struct vt_event *ev)
{
    int i;

    switch (ev->type)
    {
	case EV_CLOSE:
	    edwin_close(w);
	    break;

	case EV_KEY:
	    i = 0;
	    if (w->mode == 1)
		i = ins_key(w, ev->i1, ev->i2);
	    if (not i)
		i |= cmd_key(w, ev->i1, ev->i2);
	    if (i & 2)
		cursor(w, w->x, w->y);
	    if (i == 0)
		xio_bell(w->xw);
	    break;

	case EV_MOUSE:
	    cursor(w, ev->i3, ev->i4);
	    break;
    }
}
