#include <atari.h>
#include <string.h>

#include "screen.h"
#include "edbox.h"
#include "indic8.h"
#include "streq.h"
#include "memclear.h"

#define SDLST ((u16 *)0x0230)

/* if DOS isn't being used, the config will carve up the $0700-$1fff
   area for extra scrollback. */
unsigned int *bonus_addrs = (unsigned int *)0xd4; /* aka FR0 */

/* the screen that's currently displaying */
char scr_current;

/* the screen that's currently being written to */
char scr_active;

static char xpos;

void scr_waitvcount_116(void) {
	while(ANTIC.vcount < 116)
		/* NOP */;
}

void scr_init(void) {
	int i;

	OS.sdmctl = 0; /* disappear the screen */

	scr_waitvcount_116();
	*SDLST = DLIST_VIS;
	OS.chbas = FONT_ADDR_HI;

	init_pools();

	for(i = 0; i < MAX_SCREENS; i++) {
		screens[i].status = SCR_UNUSED;
	}

	scr_create("[server]");
	scr_create("[private]");

	OS.sdmctl = 0x22; /* show the screen again */

	scr_display(0);
}

char scr_create(const char *name) {
	char pool, i;

	/* don't create a duplicate screen */
	if( (i = scr_getbyname(name)) )
		return i;

	pool = get_smallest_pool();
	for(i = 0; i < MAX_SCREENS; i++) {
		if(screens[i].status == SCR_UNUSED) {
			pools[pool].screen_count++;
			strcpy(screens[i].title, name);
			screens[i].status = SCR_INACTIVE;
			screens[i].pool = pool;
			screens[i].line_list = (line_t *)END_MARKER;
			screens[i].line_count = screens[i].scrollback_pos = 0;
			scr_display(i);
			return i;
		}
	}

	return 0xff;
}

void scr_destroy(char s) {
	/* <2 because screens 0 and 1 (server and private) cannot be destroyed */
	if(s < 2 || s >= MAX_SCREENS)
		return;

	// pool_reclaim_lines(screens[s].pool, screens[s].line_list);
	pools[screens[s].pool].screen_count--;

	screens[s].title[0] = 0;
	screens[s].status = SCR_UNUSED;
	screens[s].pool = POOL_UNUSED;
	screens[s].line_list = 0;
	screens[s].line_count = screens[s].scrollback_pos = 0;
}

void render_vis_buf(void) {
	line_t *line;
	u8 *vis;
	int cnt;

	line = screens[scr_current].line_list;

	/* skip lines before the current scroll position */
	for(cnt = 0; cnt < screens[scr_current].scrollback_pos; cnt++)
		line = line->next;

	/* point at bottom line of visbuf */
	vis = (u8 *)(SCR_VIS_BUF + LINE_LEN * (SCR_VIS_HEIGHT - 1));
	for(cnt = 0; cnt < SCR_VIS_HEIGHT; cnt++) {
		memcpy(vis, line->data, LINE_LEN);
		line = line->next;
		vis -= LINE_LEN;
	}
}

void scr_display(char s) {
	if(s >= MAX_SCREENS)
		return;

	if(screens[s].status == SCR_UNUSED)
		return;

	screens[s].status = SCR_INACTIVE;
	scr_current = s;

	scr_waitvcount_116();
	render_vis_buf();

	scr_show_status(s);
}

void scr_scrollback(void) {
	if(screens[scr_current].scrollback_pos <= screens[scr_current].line_count) {
		screens[scr_current].scrollback_pos += SCR_VIS_HEIGHT;
		scr_display(scr_current);
	}
}

void scr_end_scrollback(void) {
	screens[scr_current].scrollback_pos = 0;
	scr_display(scr_current);
}

void scr_show_status(char s) {
	int i;
	char *p, sc;

	status_box[0] = (s % 10) + 177; /* inverse number */
	status_box[1] = ':';
	strncpy(status_box + 2, screens[s].title, 32);

	p = status_box + 43; /* one space past the 2nd indicator */

	for(i = 0; i < MAX_SCREENS; i++) {
		sc = (i % 10) + '1';
		switch(screens[i].status) {
			case SCR_INACTIVE: /* color0 */
				break;
			case SCR_ACTIVE: /* color1 */
				sc |= 0x40;
				break;
			case SCR_OTHER: /* color2 */
				sc |= 0x80;
				break;
			case SCR_HILITE: /* color3 */
				sc |= 0xc0;
				break;
			default:
				continue; /* don't show anything for unused */
		}
		*p++ = sc;
	}

	if(!edbox_visible) {
		scr_waitvcount_116();
		*dlist_status_lms = (u16)status_box;
		*dlist_last_line = 0x06; /* ANTIC GR.1 */
	}
}

void scr_refresh(void) {
	scr_display(scr_current);
}

char scr_getbyname(const char *name) {
	char i;

	if(!name) return 0;

	for(i = 2; i < MAX_SCREENS; i++) {
		if(streq_i(name, screens[i].title))
			return i;
	}

	return 0;
}

void scr_putc_active(char c) {
	scr_putc(scr_active, c);
}

void scr_eol_active(void) {
	scr_putc(scr_active, '\n');
}

void scr_eol_current(void) {
	scr_putc(scr_current, '\n');
}

/* TODO: skip color codes (start with 0x03 or 0x04).
   if we're going to ever support utf-8, decode it here...
   also, 0x16 is supposed to be reverse video. not widely used/supported.
   eventually, this will be rewritten in asm anyway. */
/* FIXME: There is a hidden assumption here: that a sequence of scr_putc(),
   up to the \n that ends it, will all be sent to the same screen.
   This is because there's only one xpos variable... */
void scr_putc(char s, char c) {
	u8 *dest;
	static char bold = 0;

	if(s == SCR_CURR)
		s = scr_current;

	if(c == '\n') {
		bold = 0;
		if(!xpos)
			return;
		xpos = LINE_LEN;
		if(s == scr_current) render_vis_buf();
		return;
	}

	if(xpos == 0 || xpos == LINE_LEN) {
		add_line(s);
		xpos = 0;
	}

	dest = screens[s].line_list->data + xpos;

	if(c & 0x80) {
		/* utf-8 (or maybe latin-1), we don't support it yet */
		c = 0xbf; /* inverse ? */
	} else if(c == 0x02) {
		bold = !bold;
		return;
	} else if(c == 0x1d) {
		/* italics */
		c = '/';
	} else if(c == 0x1e) {
		/* strikethru */
		c = '-';
	} else if(c == 0x1f) {
		/* underline */
		c = '_';
	} else if(c == 0x0f) {
		/* all formatting off */
		bold = 0;
		return;
	} else if(c < 0x20) {
		/* unsupported control character */
		c += 192;
	}

	if(bold) c |= 0x80;

	*dest = c;
	xpos++;
}

void scr_print(char s, const char *text) {
	while(*text) {
		scr_putc(s, *text);
		if(*text == '\n')
			break;
		text++;
	}
	scr_activate(s);
}

void scr_print_current(const char *text) {
	scr_print(scr_current, text);
}

void scr_print_active(const char *text) {
	scr_print(scr_active, text);
}

/*
void scr_print_server(const char *text) {
	scr_print(SCR_SERVER, text);
}

void scr_print_priv(const char *text) {
	scr_print(SCR_SERVER, text);
}
*/

void scr_activate(char s) {
	/*
	if(s != scr_current) {
		if(scr_status[s] != SCR_HILITE) scr_status[s] = SCR_ACTIVE;
		scr_show_status(scr_current);
	}
	*/
	scr_active = s;
}

char *scr_get_cur_name(void) {
	char *r = screens[scr_current].title;
	if(*r)
		return r;
	else
		return 0;
}
