#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "ztypes.h"
#include "xio.h"

#define BLANKATTR (-1)
#define SPACEWIDTH (spacewidth[FIXED_FONT])

typedef struct sline_t {
    int dirtybeg, dirtyend; /* [) protocol; -1,-1 for non-dirty */
    int isclear; /* TRUE if text is all blank */
    char *text; /* pointer to array of stat_wid chars */
    int *attr; /* pointer to array of stat_wid ints */
} sline;

/*static int stat_xpos, stat_ypos;*/ /* in pixels */
static int stat_realwid, stat_realhgt; /* in pixels */
static int stat_wid, stat_hgt; /* in chars */
static sline *linelist; /* array of stat_hgt slines */

static int linesused; /* z-machine's idea of the status line size */
static int maxlinesused; /* lines currently visible -- reset only by hitting return. */
static int curlines; /* actual window size */

static int cursorx, cursory;
static int cursorattr;

static int dotactive; /* is there an insertion mark? */
static int dotdrawn; /* is the insertion mark currently drawn? */
static int dotdrawnx, dotdrawny;
static XPoint polydot[3];

#ifdef __STDC__
static void resizewindow(int lines);
static void clearlines(int beg, int end);
static void flipdot();
#else
static void resizewindow();
static void clearlines();
static void flipdot();
#endif

#ifdef __STDC__
void xstat_init(int cols, int rows, int xpos, int ypos)
#else
void xstat_init(cols, rows, xpos, ypos)
int cols;
int rows;
int xpos;
int ypos;
#endif
{
    int ix;

    stat_wid = cols;
    stat_hgt = rows;

    /*stat_xpos = xpos;
    stat_ypos = ypos;*/
    stat_realwid = cols * SPACEWIDTH;
    stat_realhgt = rows * lineheight;

    polydot[0].x = 0;
    polydot[0].y = 0;
    polydot[1].x = 4;
    polydot[1].y = 5;
    polydot[2].x = -8;
    polydot[2].y = 0;

    dotactive = FALSE;
    dotdrawn = FALSE;
    dotdrawnx = 0;
    dotdrawny = 0;

    linelist = (sline *)malloc(sizeof(sline) * stat_hgt);

    for (ix=0; ix<stat_hgt; ix++) {
	linelist[ix].text = (char *)malloc(sizeof(char) * stat_wid);
	linelist[ix].attr = (int *)malloc(sizeof(int) * stat_wid);
	linelist[ix].isclear = FALSE;
    }

    linesused = 0;
    maxlinesused = 0;
    curlines = rows;

    cursorx = 0;
    cursory = 0;
    cursorattr = FIXED_FONT;

#ifndef TESTING
    /* export to ZIP code */
    screen_cols = cols; 
    screen_rows = rows; 
#endif

    xstat_clear_window();
}

#ifdef __STDC__
void xstat_newgeometry(int newx, int newy, int newwid, int newhgt)
#else
void xstat_newgeometry(newx, newy, newwid, newhgt)
int newx;
int newy;
int newwid;
int newhgt;
#endif
{
    stat_realwid = newwid;
    stat_realhgt = newhgt;
}

#ifdef __STDC__
void xstat_redraw()
#else
void xstat_redraw()
#endif
{
    int ix;
    sline *curline;
    
    for (ix=0; ix<stat_hgt; ix++) {
	curline = (&linelist[ix]);
	if (!curline->isclear) {
	    curline->dirtybeg = 0;
	    curline->dirtyend = stat_wid;
	}
    }

    dotdrawn = FALSE;
    xstat_layout();
}

#ifdef __STDC__
void xstat_insert(int ch)
#else
void xstat_insert(ch)
int ch;
#endif
{
    sline *curline;

    if (cursory<0 || cursory>=stat_hgt || cursorx<0 || cursorx>=stat_wid)
	return;

    if (cursory+1 > linesused) {
	xstat_set_window_size(cursory+1);
    }

    curline = (&linelist[cursory]);
    if (curline->text[cursorx] != (char)ch || curline->attr[cursorx] != cursorattr) {
	curline->text[cursorx] = (char)ch;
	curline->attr[cursorx] = cursorattr;
	curline->isclear = FALSE;
	if (curline->dirtybeg == (-1) || cursorx < curline->dirtybeg)
	    curline->dirtybeg = cursorx;
	if (curline->dirtyend == (-1) || cursorx+1 > curline->dirtyend)
	    curline->dirtyend = cursorx+1;
    }

    cursorx++;
    if (cursorx == stat_wid) {
	cursorx = 0;
	cursory++;
    }
}

#ifdef __STDC__
void xstat_newline()
#else
void xstat_newline()
#endif
{
    cursorx = 0;
    cursory++;
}

#ifdef __STDC__
void xstat_getpos(int *row, int *col)
#else
void xstat_getpos(row, col)
int *row;
int *col;
#endif
{
    *col = cursorx+1;
    *row = cursory+1;
}

#ifdef __STDC__
void xstat_setpos(int row, int col)
#else
void xstat_setpos(row, col)
int row;
int col;
#endif
{
    cursorx = col-1;
    cursory = row-1;
}

#ifdef __STDC__
void xstat_setattr(int attr)
#else
void xstat_setattr(attr)
int attr;
#endif
{
    cursorattr = attr | FIXED_FONT;
}

#ifdef __STDC__
void xstat_set_window_size(int lines)
#else
void xstat_set_window_size(lines)
int lines;
#endif
{
    linesused = lines;
    if (lines > maxlinesused)
	maxlinesused = lines;
}

#ifdef __STDC__
void xstat_set_dot_active(int visible)
#else
void xstat_set_dot_active(visible)
int visible;
#endif
{
    if (visible) {
	/* turn it on */
	if (dotactive)
	    return;
	dotactive = TRUE;
    }
    else {
	/* turn it off */
	if (!dotactive)
	    return;
	dotactive = FALSE;
    }
}

/* clear to blankness */
#ifdef __STDC__
void xstat_clear_window()
#else
void xstat_clear_window()
#endif
{
    clearlines(0, stat_hgt);

    linesused = 0;
    maxlinesused = linesused; /* because anything below the status window has been cleared */
}

#ifdef __STDC__
static void clearlines(int beg, int end)
#else
static void clearlines(beg, end)
int beg;
int end;
#endif
{
    int ix;
    int jx;
    char *cx;
    int *ax;

    for (ix=beg; ix<end; ix++) {
	if (!linelist[ix].isclear) {
	    linelist[ix].isclear = TRUE;
	    linelist[ix].dirtybeg = 0;
	    linelist[ix].dirtyend = stat_wid;
	    for (cx=linelist[ix].text, jx=stat_wid; jx; cx++, jx--)
		*cx = ' ';
	    for (ax=linelist[ix].attr, jx=stat_wid; jx; ax++, jx--)
		*ax = BLANKATTR;
	}
    }
}

/* redraw the dirty stuff */
#ifdef __STDC__
void xstat_layout()
#else
void xstat_layout()
#endif
{
    int ix;
    int beg, end, pos, px;
    int attr;
    int newsize;
    sline *curline;

    if (dotdrawn) {
	flipdot();
	dotdrawn = FALSE;
    }

    newsize = maxlinesused;
    if (prefs.autoclear && newsize != curlines) {
	clearlines(newsize, stat_hgt);
    }

    for (ix=0; ix<stat_hgt; ix++) {
	curline = (&linelist[ix]);
	if (curline->dirtybeg == (-1))
	    continue;
	beg = curline->dirtybeg;
	end = curline->dirtyend;
	pos = beg;
	while (pos < end) {
	    attr = curline->attr[pos];
	    for (px=pos; px<end && curline->attr[px]==attr; px++);
	    if (attr==BLANKATTR) {
		XClearArea(xiodpy, xioswin, pos*SPACEWIDTH, ix*lineheight, (px-pos)*SPACEWIDTH, lineheight, FALSE);
	    }
	    else if (attr & REVERSE) {
		XFillRectangle(xiodpy, xioswin, gcsfont[attr], pos*SPACEWIDTH, ix*lineheight, (px-pos)*SPACEWIDTH, lineheight);
		XDrawString(xiodpy, xioswin, gcsnegfont[attr], pos*SPACEWIDTH, ix*lineheight+lineheightoff, curline->text+pos, (px-pos));
	    }
	    else {
		XClearArea(xiodpy, xioswin, pos*SPACEWIDTH, ix*lineheight, (px-pos)*SPACEWIDTH, lineheight, FALSE);
		XDrawString(xiodpy, xioswin, gcsfont[attr], pos*SPACEWIDTH, ix*lineheight+lineheightoff, curline->text+pos, (px-pos));
	    }
	    pos = px;
	}
	curline->dirtybeg = (-1);
	curline->dirtyend = (-1);
    }

    if (dotactive) {
	dotdrawnx = cursorx * SPACEWIDTH;
	dotdrawny = cursory * lineheight + lineheightoff;
	flipdot();
	dotdrawn = TRUE;
    }

    if (prefs.autoresize && newsize != curlines) {
	resizewindow(newsize);
    }
}

/* reset the window size to linesused (if max==FALSE) or temporarily to stat_hgt (if max==TRUE) */
#ifdef __STDC__
void xstat_reset_window_size(int op)
#else
void xstat_reset_window_size(op)
int op;
#endif
{
    int newsize;

    if (op==op_Clear) {
	clearlines(linesused, stat_hgt);
	xstat_layout();
	return;
    }
    else if (op==op_Zoom)
	newsize = stat_hgt;
    else if (op==op_Shrink)
	newsize = linesused;
    else {
	return;
    }

    if (prefs.autoclear && newsize != curlines) {
	clearlines(newsize, stat_hgt);
    }
    if (prefs.autoresize && newsize != curlines) {
	resizewindow(newsize);
    }
    maxlinesused = newsize;
}

#ifdef __STDC__
static void resizewindow(int lines)
#else
static void resizewindow(lines)
int lines;
#endif
{
    XSizeHints szhints;
    int xp, yp;
    int newheight;
    Window child;

    curlines = lines;
    newheight = curlines*lineheight;
    if (newheight==0)
	newheight++;

    if (!prefs.resizeupward) {
	szhints.flags = USSize;
	szhints.width = stat_wid*SPACEWIDTH;
	szhints.height = newheight;
	XResizeWindow(xiodpy, xioswin, szhints.width, szhints.height);
	XSetNormalHints(xiodpy, xioswin, &szhints);
    }
    else {
	XTranslateCoordinates(xiodpy, xioswin, RootWindow(xiodpy, xioscn), 0, 0, &xp, &yp, &child);
	yp -= (newheight - stat_realhgt);

	szhints.flags = PMinSize|PResizeInc|USPosition|USSize;
	szhints.width = stat_wid*SPACEWIDTH;
	szhints.height = newheight;
	szhints.x = xp;
	szhints.y = yp;
	szhints.width_inc = spacewidth[FIXED_FONT];
	szhints.height_inc = lineheight;
	szhints.min_width = 1 * szhints.width_inc;
	szhints.min_height = 1 * szhints.height_inc;
	XMoveResizeWindow(xiodpy, xioswin, xp, yp, szhints.width, szhints.height);
	XSetNormalHints(xiodpy, xioswin, &szhints);
    }
}

#ifdef __STDC__
static void flipdot()
#else
static void flipdot()
#endif
{
    polydot[0].x = dotdrawnx;
    polydot[0].y = dotdrawny;
    XFillPolygon(xiodpy, xioswin, gcsflip, polydot, 3, Convex, CoordModePrevious);
}
