#include <signal.h>
#include "fm.h"

#ifdef USE_MOUSE
#ifdef USE_GPM
#include <gpm.h>
#endif
#if defined(USE_GPM) || defined(USE_SYSMOUSE)
extern int do_getch();
#define getch()	do_getch()
#endif				/* USE_GPM */
#endif				/* USE_MOUSE */

#ifdef __EMX__
#include <sys/kbdscan.h>
#include <strings.h>
#endif
char *NullLine = "";
Lineprop NullProp[] = {0};

/* 
 * Buffer creation
 */
void
initBuffer(Buffer *n, BufferView *v)
{
    bzero((void *)n, sizeof(Buffer));
    n->view = v;
    n->rmargin = (LineTruncated && *LineTruncated) ? 1 : 0;
    n->currentURL.scheme = SCM_UNKNOWN;
    n->buffername = "";
    n->http_response_code = -1;
}

Buffer *
newBuffer(BufferView *v)
{
    Buffer *n;

    n = New(Buffer);
    initBuffer(n, v);
    return n;
}

/* 
 * clearBuffer: clear buffer content
 */
void
clearBuffer(Buffer *buf)
{
    buf->firstLine = buf->topLine = buf->currentLine = buf->lastLine = NULL;
    buf->allLine = 0;
}

/* 
 * discardBuffer: free buffer structure
 */

void
discardBuffer(Buffer *buf)
{
    int i;
    Buffer *b, *s;

    if (buf->bufferprop & BP_DISCARDED)
	return;

    clearBuffer(buf);
    buf->bufferprop |= BP_DISCARDED;

    if (buf->bufferprop & BP_ON_VIEW) {
	BufferView *v;

	removeBufferFromView(buf);

	for (v = buf->view ; v && !v->frameset && !v->top ; v = nextBufferView(v))
	    ;
    }

    if (buf->bufferprop & BP_LINKED)
	mkunlinkedBuffer(buf);

    buf->bufferprop &= ~BP_VISIBLE;
    s = buf->linkBuffer[LB_SOURCE];

    for (i = 0; i < MAX_LB; i++)
	if ((b = buf->linkBuffer[i]))
	    b->linkBuffer[REV_LB[i]] = NULL;

    if (buf->savecache)
	unlink(buf->savecache);

    if (s)
	return;

    if (buf->async_buf)
	discardAsyncRWBuf(buf->async_buf, TRUE);

    if ((buf->bufferprop & BP_ASYNC_MASK) != BP_ASYNC_DONE) {
	if (buf->sourcefile && (buf->real_scheme != SCM_LOCAL))
	    unlink(buf->sourcefile);

	buf->frameset = NULL;
    }
}

void
removeViewFromView(BufferViewList *l, BufferView *v)
{
    if (v->flag & BV_LINKED) {
	if (v->down)
	    v->down->up = v->up;
	else
	    l->bot = v->up;

	if (v->up)
	    v->up->down = v->down;
	else
	    l->top = v->down;

	v->flag &= ~BV_LINKED;
    }
}

BufferView *
nextBufferView(BufferView *v)
{
    if (v) {
	BufferViewList *l;

	if (v->sup)
	    l = &v->sup->subv[v->y * v->sup->ncols + v->x];
	else
	    l = &TopViewList;

	removeViewFromView(l, v);
	return l->top ? l->top : nextBufferView(v->sup);
    }

    return NULL;
}

void
insertViewToView(BufferViewList *l, BufferView *nv)
{
    removeViewFromView(l, nv);
    nv->up = NULL;

    if ((nv->down = l->top))
	nv->down->up = nv;
    else
	l->bot = nv;

    l->top = nv;
    nv->flag |= BV_LINKED;
}

void
removeBufferFromView(Buffer *buf)
{
    if (buf->bufferprop & BP_ON_VIEW) {
	if (buf->up)
	    buf->up->down = buf->down;
	else
	    buf->view->top = buf->down;

	if (buf->down)
	    buf->down->up = buf->up;
	else
	    buf->view->bot = buf->up;

	buf->bufferprop &= ~BP_ON_VIEW;
    }
}

void
insertBufferToView(Buffer *ref, Buffer *buf)
{
    removeBufferFromView(buf);

    if (ref && ref->bufferprop & BP_ON_VIEW) {
	buf->view = ref->view;
	buf->down = ref;

	if ((buf->up = ref->up))
	    buf->up->down = buf;
	else
	    buf->view->top = buf;

	ref->up = buf;
    }
    else {
	buf->up = NULL;

	if ((buf->down = buf->view->top))
	    buf->down->up = buf;
	else
	    buf->view->bot = buf;

	buf->view->top = buf;
    }

    buf->bufferprop |= BP_ON_VIEW;
}

void
appendBufferToView(Buffer *ref, Buffer *buf)
{
    removeBufferFromView(buf);

    if (ref && ref->bufferprop & BP_ON_VIEW) {
	buf->view = ref->view;
	buf->up = ref;

	if ((buf->down = ref->down))
	    buf->down->up = buf;
	else
	    buf->view->bot = buf;

	ref->down = buf;
    }
    else {
	buf->down = NULL;

	if ((buf->up = buf->view->bot))
	    buf->up->down = buf;
	else
	    buf->view->top = buf;

	buf->view->bot = buf;
    }

    buf->bufferprop |= BP_ON_VIEW;
}

void
mkunlinkedBuffer(Buffer *buf)
{
    if (!(buf && buf->bufferprop & BP_LINKED))
	return;

    if (buf->next)
	buf->next->prev = buf->prev;

    if (buf->prev)
	buf->prev->next = buf->next;
    else
	Firstbuf = buf->next;

    buf->bufferprop &= ~BP_LINKED;
}

void
insertBuffer(Buffer *old, Buffer *new)
{
    if (!new)
	return;

    mkunlinkedBuffer(new);

    if (old && old->bufferprop & BP_LINKED) {
	if ((new->prev = old->prev))
	    new->prev->next = new;
	else
	    Firstbuf = new;

	old->prev = new;
	new->next = old;
    }
    else {
	if ((new->next = Firstbuf))
	    new->next->prev = new;

	new->prev = NULL;
	Firstbuf = new;
    }

    new->bufferprop |= BP_LINKED;
}

void
appendBuffer(Buffer *old, Buffer *new)
{
    if (!new || !old || !(old->bufferprop & BP_LINKED))
	return;

    mkunlinkedBuffer(new);

    if ((new->next = old->next))
	new->next->prev = new;

    old->next = new;
    new->prev = old;
    new->bufferprop |= BP_LINKED;
}

void
updateCurrentbuf(Buffer *buf)
{
    if (buf) {
	if (
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	    !buf->menu &&
#endif
	    buf->view) {
	    BufferView *v;

	    insertBufferToView(NULL, buf);

	    for (v = buf->view ; v->sup ; v = v->sup)
		insertViewToView(&v->sup->subv[v->y * v->sup->ncols + v->x], v);

	    insertViewToView(&TopViewList, v);
	}

	Currentbuf = buf;
    }
}

Buffer *
checkCurrentbuf(void)
{
    Buffer *orig;

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    if (Currentbuf && Currentbuf->menu)
	return Currentbuf;
#endif

    for (orig = Currentbuf ; Currentbuf && !(Currentbuf->bufferprop & BP_VISIBLE) ;)
	Currentbuf = Currentbuf->next;

    if (!(Currentbuf && Currentbuf->bufferprop & BP_VISIBLE)) {
	Currentbuf = Firstbuf;

	while (Currentbuf && Currentbuf != orig && !(Currentbuf->bufferprop & BP_VISIBLE))
	    Currentbuf = Currentbuf->next;
    }

    if (Currentbuf && Currentbuf->bufferprop & BP_VISIBLE)
	updateCurrentbuf(Currentbuf);

    return Currentbuf;
}

int
isPrevBuffer(Buffer *a, Buffer *b)
{
    if (a) {
	Buffer *buf;

	if (a == b)
	    return 0;

	for (buf = a ; (buf = buf->next) ;)
	    if (buf == b)
		return 1;
    }

    return -1;
}

void
deleteBuffer(Buffer *buf)
{
    if (buf) {
	BufferView *v;

	v = buf->view;
	mkunlinkedBuffer(buf);
	removeBufferFromView(buf);
	discardBuffer(buf);
    }
}

void
replaceBuffer(Buffer *delbuf, Buffer *newbuf)
{
    if (delbuf != newbuf) {
	insertBuffer(delbuf, newbuf);
	insertBufferToView(delbuf, newbuf);
	discardBuffer(delbuf);
    }
}

static void
incomplete_loading_info(Buffer *buf, char *msgbuf)
{
    if (buf->async_buf) {
	int rfd = buf->async_buf->rfd;

	msgbuf += strlen(msgbuf);

	if (is_recorded_read_fd(rfd))
	    sprintf(msgbuf, " (loading is incomplete, please wait)");
	else if (rfd >= 0 && is_hook_recorded_read_fd(rfd)) {
	    sprintf(msgbuf, " (loading is restarted, please retry)");
	    record_read_fd(rfd, NULL, NULL, NULL);
	}
    }
}

void
gotoLine(Buffer *buf, int n)
{
    char msg[128];
    Line *l;
    BufferView *v;

    if (!(l = buf->firstLine) || !(v = buf->view))
	return;
    if (l->linenumber > n) {
	sprintf(msg, "First line is #%ld", l->linenumber);
	disp_message(msg, TRUE);
	buf->topLine = buf->currentLine = l;
	return;
    }
    if (buf->lastLine->linenumber < n) {
	l = buf->lastLine;
	sprintf(msg, "Last line is #%ld", l->linenumber);
	incomplete_loading_info(buf, msg);
	disp_message(msg, TRUE);
	if (l->linenumber - buf->topLine->linenumber >= v->height - 1)
	    buf->topLine = lineSkip(buf, l, buf->topLine->linenumber - buf->currentLine->linenumber, 0);
	buf->currentLine = l;
	return;
    }
    for (; l != NULL; l = l->next) {
	if (l->linenumber >= n) {
	    if (n < buf->topLine->linenumber || buf->topLine->linenumber + v->height <= n)
		buf->topLine = lineSkip(buf, l, buf->topLine->linenumber - buf->currentLine->linenumber, 0);
	    buf->currentLine = l;
	    break;
	}
    }
}

void
#ifdef USE_MENU_BUFFER
gotoRealLineMaybe(Buffer * buf, int n, int force)
#else
gotoRealLine(Buffer * buf, int n)
#endif
{
    char msg[128];
    Line *l;
    BufferView *v;

    if (!(l = buf->firstLine) || !(v = buf->view))
	return;
    if (l->real_linenumber > n) {
#ifdef USE_MENU_BUFFER
	if (force) {
	    if (force > 1) {
#endif
		sprintf(msg, "First line is #%ld", l->real_linenumber);
		disp_message(msg, TRUE);
#ifdef USE_MENU_BUFFER
	    }
#endif
	    buf->topLine = buf->currentLine = l;
#ifdef USE_MENU_BUFFER
	}
#endif
	return;
    }
    for (l = buf->lastLine ; l && !l->real_linenumber ; l = l->next)
	;
    if (!l)
	return;
    if (l->real_linenumber < n) {
#ifdef USE_MENU_BUFFER
	if (force) {
	    if (force > 1) {
#endif
		sprintf(msg, "Last line is #%ld", l->real_linenumber);
		incomplete_loading_info(buf, msg);
		disp_message(msg, TRUE);
#ifdef USE_MENU_BUFFER
	    }
#endif
	    if (l->linenumber - buf->topLine->linenumber >= v->height - 1)
		buf->topLine = lineSkip(buf, l, buf->topLine->linenumber - buf->currentLine->linenumber, 0);
	    buf->currentLine = l;
#ifdef USE_MENU_BUFFER
	}
#endif
	return;
    }
    for (l = buf->firstLine; l != NULL; l = l->next) {
	if (l->real_linenumber >= n) {
	    Line *tl;

	    for (tl = buf->topLine ; tl && !tl->real_linenumber ; tl = tl->next)
		;
	    if (tl && tl->real_linenumber &&
		(n < tl->real_linenumber || tl->real_linenumber + v->height <= n))
		buf->topLine = lineSkip(buf, l, buf->topLine->linenumber - buf->currentLine->linenumber, 0);
	    buf->currentLine = l;
	    break;
	}
    }
}

#ifdef USE_MENU_BUFFER
void
gotoRealLine(Buffer * buf, int n)
{
    gotoRealLineMaybe(buf, n, 2);
}
#endif


/* shallow copy */
void
copyBuffer(Buffer * a, Buffer * b)
{
    readBufferCache(b);
    bcopy((void *)b, (void *)a, sizeof(Buffer));
}

#define fwrite1(d, f) (fwrite(&d, sizeof(d), 1, f)==0)
#define fread1(d, f) (fread(&d, sizeof(d), 1, f)==0)

int
writeBufferCache(Buffer *buf)
{
    Str tmp;
    FILE *cache = NULL;
    Line *l;
#ifdef USE_ANSI_COLOR
    int colorflag;
#endif

    if (buf->savecache)
	return -1;

    if (buf->firstLine == NULL)
	goto _error1;

    tmp = tmpfname(TMPF_CACHE, NULL);
    buf->savecache = tmp->ptr;
    cache = fopen(buf->savecache, "w");
    if (!cache)
	goto _error1;

    if (fwrite1(buf->currentLine->linenumber, cache) ||
	fwrite1(buf->topLine->linenumber, cache))
	goto _error;

    for (l = buf->firstLine; l; l = l->next) {
	if (fwrite1(l->real_linenumber, cache) ||
	    fwrite1(l->usrflags, cache) ||
	    fwrite1(l->width, cache) ||
	    fwrite1(l->len, cache) ||
	    fwrite(l->lineBuf, 1, l->len, cache) < l->len ||
	    fwrite(l->propBuf, sizeof(Lineprop), l->len, cache) < l->len)
	    goto _error;
#ifdef USE_ANSI_COLOR
        colorflag = l->colorBuf ? 1 : 0;
	if (fwrite1(colorflag, cache))
	    goto _error;
	if (colorflag) {
	    if (fwrite(l->colorBuf, sizeof(Linecolor), l->len, cache) < l->len)
		goto _error;
	}
#endif
    }

    fclose(cache);
    return 0;
_error:
    fclose(cache);
    unlink(buf->savecache);
_error1:
    buf->savecache = NULL;
    return -1;
}

int
readBufferCache(Buffer *buf)
{
    FILE *cache;
    Line *l = NULL, *prevl = NULL;
    long lnum = 0, clnum, tlnum;
#ifdef USE_ANSI_COLOR
    int colorflag;
#endif

    if (buf->savecache == NULL)
	return -1;

    cache = fopen(buf->savecache, "r");
    if (cache == NULL ||
	fread1(clnum, cache) ||
	fread1(tlnum, cache)) {
	buf->savecache = NULL;
	return -1;
    }

    while (!feof(cache)) {
	lnum++;
	prevl = l;
	l = New(Line);
	l->prev = prevl;
	if (prevl)
	    prevl->next = l;
	else
	    buf->firstLine = l;
	l->linenumber = lnum;
	if (lnum == clnum)
	    buf->currentLine = l;
	if (lnum == tlnum)
	    buf->topLine = l;
	if (fread1(l->real_linenumber, cache) ||
	    fread1(l->usrflags, cache) ||
	    fread1(l->width, cache) ||
	    fread1(l->len, cache))
	    break;
	l->lineBuf = NewAtom_N(char, l->len + 1);
	fread(l->lineBuf, 1, l->len, cache);
	l->lineBuf[l->len] = '\0';
	l->propBuf = NewAtom_N(Lineprop, l->len);
	fread(l->propBuf, sizeof(Lineprop), l->len, cache);
#ifdef USE_ANSI_COLOR
	if (fread1(colorflag, cache))
	    break;
	if (colorflag) {
	    l->colorBuf = NewAtom_N(Linecolor, l->len);
	    fread(l->colorBuf, sizeof(Linecolor), l->len, cache);
	} else {
	    l->colorBuf = NULL;
	}
#endif
    }
    buf->lastLine = prevl;
    if (prevl) prevl->next = NULL;
    fclose(cache);
    unlink(buf->savecache);
    buf->savecache = NULL;
    return 0;
}

/* Local Variables:    */
/* c-basic-offset: 4   */
/* tab-width: 8        */
/* End:                */
