/*-
 * Copyright (c) 2001
 * Tatsuya Kudoh(CDR/TK),ROYALPANDA.    All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include<stdlib.h>
#include<sys/bios.h>

#include"ks.h"
#include"malloc.h"

#pragma noregalo

#define COLOR_NONE		0x0000
#define COLOR_KEYBOARD		0x0055
#define COLOR_KEYBOARD_SHADOW	0x3355
#define COLOR_FLOAT		0x0055
#define COLOR_WINDOW		0x0077

#define COLOR_NORMAL		0x0707
#define COLOR_REVERSE		0x7070
#define COLOR_EMPH		0x0303
#define COLOR_GRAY		0x4747

#define COLOR_CURSOR		0x5000


#define SCR_WIDTH		28
#define SCR_HEIGHT		13

static unsigned CodeMap[SCR_WIDTH*SCR_HEIGHT];
static unsigned char OwnerMap[SCR_WIDTH*SCR_HEIGHT];

static KSWindow RootWindow;
static KSWindow *Top = &RootWindow;

static int CursorX = 0;
static int CursorY = 0;
static int CursorEnable = 0;

static int CursorBlink = 0;

#define CURSOR_BLINK_PERIOD	75
#define CURSOR_BLINK_VISIBLE	50

#define CURSOR_HOT_X	3
#define CURSOR_HOT_Y	4

static unsigned CursorFont[] = {
	/* cursor.xpm */
0x0000,0x0000,0x0000,0x1010,0x2838,0x447c,0x82fe,0xfefe, /*  0, 0 */
};


void KSSetFontData( int face, int font, unsigned code )
{
	unsigned char buf[16];
	unsigned char glyph[8];
	int i;

	font_get_data(font,1,buf);
	if( code == 0 || code == 32 ){
		for( ; face < 16 ; face+=2 )
			buf[face] = 0;
	}else{
		text_get_fontdata(code,glyph);
		for( i = 0 ; i < 8 ; i++ ){
			buf[face] = glyph[i];
			face += 2;
		}
	}
	font_set_colordata(font,1,buf);
}


static void update_owner_map( void )
{
	KSWindow *win;
	unsigned char *o;
	int priority;
	int x,y;

	win = &RootWindow;
	priority = 0;
	while( win != NULL ){
		if( win->mapped ){
			for( y = 0 ; y < win->height ; y++ ){
				o = OwnerMap + (win->y+y)*SCR_WIDTH + win->x;
				for( x = 0 ; x < win->width ; x++ )
					*o++ = priority;
			}
		}
		win->priority = priority;
		priority++;
		win = win->next;
	}
}


static void erase_window( KSWindow *w )
{
	KSWindow *win;
	int sx,sy,ex,ey;
	int sx2,sy2,ex2,ey2;
	int width,height;
	KSMsg *msg;

	if( w->mapped == 0 )
		return;

	sx = w->x;
	ex = sx + w->width;
	sy = w->y;
	ey = sy + w->height;

	for( win = &RootWindow ; win && win != w ; win = win->next ){
		sx2 = win->x;
		ex2 = sx2 + win->width;
		sy2 = win->y;
		ey2 = sy2 + win->height;

		if( sx > sx2 )
			sx2 = sx;
		if( sy > sy2 )
			sy2 = sy;
		if( ex < ex2 )
			ex2 = ex;
		if( ey < ey2 )
			ey2 = ey;

		width = ex2 - sx2;
		height = ey2 - sy2;

		if( width <= 0 || height <= 0 )
			continue;

		msg = KSAllocateMsg();
		if( msg == NULL )
			return;	/* fatal */

		msg->type = KS_MSG_EXPOSURE;
		msg->exposure.receiver = win->receiver;
		msg->exposure.call_data = win->call_data;
		msg->exposure.x = sx2 - win->x;
		msg->exposure.y = sy2 - win->y;
		msg->exposure.width = width;
		msg->exposure.height = height;

		KSSendMsg(msg);
	}
}


void KSRegistWindow( KSWindow *w )
{
	w->next = NULL;
	Top->next = w;
	Top = w;

	if( w->mapped ){
		w->mapped = 0;
		KSMapWindow(w);
	}
}


void KSUnregistWindow( KSWindow *w )
{
	KSWindow *win;

	win = &RootWindow;
	while( win && win->next != w )
		win = win->next;
	if( win == NULL )
		return;

	win->next = w->next;
	if( win->next == NULL )
		Top = win;

	update_owner_map();

	if( w->mapped != 0 )
		erase_window(w);
}


void KSMapWindow( KSWindow *w )
{
	KSMsg *msg;

	if( w->mapped != 0 )
		return;

	w->mapped = 1;
	update_owner_map();

	msg = KSAllocateMsg();
	if( msg == NULL )
		return;	/* fatal */

	msg->type = KS_MSG_EXPOSURE;
	msg->exposure.receiver = w->receiver;
	msg->exposure.call_data = w->call_data;
	msg->exposure.x = 0;
	msg->exposure.y = 0;
	msg->exposure.width = w->width;
	msg->exposure.height = w->height;
	KSSendMsg(msg);
}


void KSUnmapWindow( KSWindow *w )
{
	if( w->mapped == 0 )
		return;
	w->mapped = 0;
	update_owner_map();
	erase_window(w);
}


void KSDrawCharacter( KSWindow *win, int x, int y, unsigned code, unsigned plt )
{
	int pos;
	unsigned attr;

	x += win->x;
	y += win->y;
	if( x < 0 || y < 0 || x >= SCR_WIDTH || y >= SCR_HEIGHT )
		return;

	pos = y * SCR_WIDTH + x;
	if( OwnerMap[pos] != win->priority )
		return;

	screen_get_char(0,x,y,1,1,&attr);
	if( CodeMap[pos] != code ){
		CodeMap[pos] = code;
		KSSetFontData(0,attr&0x1ff,code);
	}
	if( (attr & 0xfe00) != plt ){
		attr = (attr & 0x1ff) | plt;
		screen_set_char(0,x,y,1,1,&attr);
	}
}


void KSDrawWindowBorder( KSWindow *win )
{
	int i;

	KSDrawCharacter(win,0,0,'„¬',KS_PLT_NORMAL);
	KSDrawCharacter(win,win->width-1,0,'„­',KS_PLT_NORMAL);
	KSDrawCharacter(win,0,win->height-1,'„¯',KS_PLT_NORMAL);
	KSDrawCharacter(win,win->width-1,win->height-1,'„®',KS_PLT_NORMAL);

	for( i = 1 ; i < win->width-1 ; i++ ){
		KSDrawCharacter(win,i,0,'„ª',KS_PLT_NORMAL);
		KSDrawCharacter(win,i,win->height-1,'„ª',KS_PLT_NORMAL);
	}

	for( i = 1 ; i < win->height-1 ; i++ ){
		KSDrawCharacter(win,0,i,'„«',KS_PLT_NORMAL);
		KSDrawCharacter(win,win->width-1,i,'„«',KS_PLT_NORMAL);
	}
}

void KSClearWindow( KSWindow *win, unsigned plt )
{
	int x,y;

	for( y = 0 ; y < win->height ; y++ )
		for( x = 0 ; x < win->width ; x++ )
			KSDrawCharacter(win,x,y,0,plt);
}

void KSClearArea( KSWindow *win, int x, int y, int width, int height, unsigned plt )
{
	int xx;
	int w;

	while( height-- ){
		w = width;
		xx = x;
		while( w-- ){
			KSDrawCharacter(win,xx,y,0,plt);
			xx++;
		}
		y++;
	}
}


static int handler( KSMsg *msg )
{
	if( msg->type != KS_MSG_EXPOSURE )
		return 0;

	KSClearArea(&RootWindow,msg->exposure.x,msg->exposure.y,
			msg->exposure.width,msg->exposure.height,KS_PLT_NORMAL);
	return 0;
}


static void show_cursor( void )
{
	int x,y;

	if( CursorBlink < CURSOR_BLINK_VISIBLE ){
		x = (CursorX * 8 - CURSOR_HOT_X) & 0xff;
		y = (CursorY * 8 + 8 - CURSOR_HOT_Y) & 0xff;
		sprite_set_char_location(KS_SPR_CURSOR,
				(KS_PLT_CURSOR & ~(8<<9))|KS_FONT_CURSOR,x,y);
	}else{
		sprite_set_location(KS_SPR_CURSOR,0,144);
	}
}

void KSMoveCursor( int x, int y )
{
	CursorX = x;
	CursorY = y;
	CursorBlink = 0;
	if( CursorEnable )
		show_cursor();
}

int KSGetCursorStatus( int *x_ret, int *y_ret )
{
	if( x_ret )
		*x_ret = CursorX;
	if( y_ret )
		*y_ret = CursorY;

	return CursorEnable;
}


void KSEnableCursor( int enable )
{
	if( enable ){
		CursorEnable = 1;
		show_cursor();
	}else{
		CursorEnable = 0;
		sprite_set_location(KS_SPR_CURSOR,0,144);
	}
}

void KSBlinkCursor( void )
{
	if( CursorEnable ){
		CursorBlink++;
		if( CursorBlink == CURSOR_BLINK_PERIOD )
			CursorBlink = 0;
		show_cursor();
	}
}

void KSDrawInitialize( void )
{
	int x,y;
	int i;

	RootWindow.x = 0;
	RootWindow.y = 0;
	RootWindow.width = SCR_WIDTH;
	RootWindow.height = SCR_HEIGHT;
	RootWindow.mapped = 1;
	RootWindow.receiver = handler;
	RootWindow.call_data = 0;
	RootWindow.next = NULL;

	Top = &RootWindow;

	display_control(0);
	lcd_set_color(0x7aef,0x0136);

	palette_set_color(KS_PLT_KEYBOARD>>9, COLOR_KEYBOARD);
	palette_set_color(KS_PLT_KEYBOARD_SHADOW>>9, COLOR_KEYBOARD_SHADOW);
	palette_set_color(KS_PLT_FLOAT>>9, COLOR_FLOAT);
	palette_set_color(KS_PLT_NONE>>9, COLOR_NONE);
	palette_set_color(KS_PLT_WINDOW>>9, COLOR_WINDOW);

	palette_set_color(KS_PLT_NORMAL>>9, COLOR_NORMAL);
	palette_set_color(KS_PLT_REVERSE>>9, COLOR_REVERSE);
	palette_set_color(KS_PLT_EMPH>>9, COLOR_EMPH);
	palette_set_color(KS_PLT_GRAY>>9, COLOR_GRAY);

	palette_set_color(KS_PLT_CURSOR>>9, COLOR_CURSOR);

	i = 0;
	for( y = 0 ; y < SCR_HEIGHT ; y++ ){
		for( x = 0 ; x < SCR_WIDTH ; x++ ){
			screen_fill_char(0,x,y,1,1,KS_PLT_NORMAL|i);
			KSSetFontData(0,i,32);
			CodeMap[i] = 32;
			OwnerMap[i] = 0;
			i++;
		}
	}

	screen_fill_char(0,0,13,28,5,KS_PLT_WINDOW|KS_FONT_NONE);

	font_set_colordata(KS_FONT_CURSOR,1,CursorFont);
	KSEnableCursor(0);
	sprite_set_range(0,1);

	display_control(DCM_SCR1|DCM_SPR|7<<8);
}

