/*-
 * 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 BUFLEN	25

#define EOL_MARKER	0x11

typedef struct line_t{
	unsigned label[BUFLEN+1];
	unsigned *buf;
	int (*receiver)(KSMsg *msg);
	void *call_data;
	int len;
	int x;

	int prev_cursor_enable;
	int prev_cursor_x;
	int prev_cursor_y;

	KSWindow win;
}line_t;

static KSMemoryBlockManager LineMemMgr;

static int WinY = 2;
#define WIN_Y_MIN  0
#define WIN_Y_MAX  6

static void cursor( line_t *lp )
{
	KSMoveCursor(lp->win.x+lp->x+1, lp->win.y+3);
}


static void draw_buf( line_t *lp )
{
	unsigned *p;
	int x;

	p = lp->buf;
	for( x = 0 ; x < lp->x ; x++ )
		KSDrawCharacter(&lp->win,x+1,3,*p++,KS_PLT_NORMAL);

	p = lp->buf + x + (BUFLEN - lp->len);
	for( ; x < lp->len ; x++ )
		KSDrawCharacter(&lp->win,x+1,3,*p++,KS_PLT_NORMAL);

	KSDrawCharacter(&lp->win,x+1,3,EOL_MARKER,KS_PLT_NORMAL);
	x++;

	for( ; x < BUFLEN+1 ; x++ )
		KSDrawCharacter(&lp->win,x+1,3,' ',KS_PLT_NORMAL);
}


static int draw( line_t *lp )
{
	int x;
	unsigned *p;
	unsigned char *s;
	unsigned code;

	/* only use 2bytes characters */
	static unsigned char msg[] = "［改行／次頁］決定　［前項］取消";
	static int msg_len = 16;

	KSClearArea(&lp->win,1,1,26,5,KS_PLT_NORMAL);
	KSDrawWindowBorder(&lp->win);

	p = lp->label;
	x = 1;
	while( *p )
		KSDrawCharacter(&lp->win,x++,1,*p++,KS_PLT_NORMAL);

	s = msg;
	x = 27 - msg_len;
	while( *s ){
		code = (unsigned)s[0] << 8 | s[1];
		s+=2;
		KSDrawCharacter(&lp->win,x++,5,code,KS_PLT_NORMAL);
	}
	draw_buf(lp);
}


static int insert( line_t *lp, unsigned code )
{
	if( lp->len == BUFLEN )
		return 0;

	lp->buf[lp->x] = code;
	lp->x++;
	lp->len++;

	return 1;
}


static int backspace( line_t *lp )
{
	if( lp->x == 0 )
		return 0;

	lp->x--;
	lp->len--;

	return 1;
}


static int forward( line_t *lp )
{
	int src;

	if( lp->x == lp->len )
		return 0;

	src = lp->x + BUFLEN - lp->len;
	lp->buf[lp->x] = lp->buf[src];
	lp->x++;

	return 1;
}


static int backward( line_t *lp )
{
	int dst;

	if( lp->x == 0 )
		return 0;

	lp->x--;
	dst = lp->x + BUFLEN - lp->len;
	lp->buf[dst] = lp->buf[lp->x];

	return 1;
}


static int handler( KSMsg *msg )
{
	unsigned code;
	int redraw;
	int result;
	unsigned *str;
	line_t *p;

	if( msg->type == KS_MSG_EXPOSURE ){
		draw((line_t*)msg->exposure.call_data);
		return 0;
	}


	if( msg->type == KS_MSG_STRING )
		str = msg->string.str;
	else if( msg->type == KS_MSG_TEXT )
		str = msg->text.text;
	else
		return 0;

	p = (line_t*)msg->null.call_data;

	redraw = 0;
	result = 0;
	while( result == 0 && *str ){
		code = *str++;
		switch( code ){
		case KEYCODE_RETURN:
		case KEYCODE_NEXT:
			result = 1;
			break;

		case KEYCODE_PRIOR:
			result = -1;
			break;

		case KEYCODE_RIGHT:
			redraw |= forward(p);
			break;

		case KEYCODE_LEFT:
			redraw |= backward(p);
			break;

		case KEYCODE_END:
			while( forward(p) )
				redraw |= 1;
			break;

		case KEYCODE_HOME:
			while( backward(p) )
				redraw |= 1;
			break;

		case KEYCODE_BACKSPACE:
			redraw |= backspace(p);
			break;

		default:
			if( code != 127 && code >= 32 )
				redraw |= insert(p,code);
			break;
		}
	}
	if( redraw )
		draw_buf(p);

	cursor(p);

	if( result ){
		KSFinishReggFEP();

		if( msg->type == KS_MSG_TEXT )
			KSFree(msg->text.text);

		msg->type = KS_MSG_TEXT;
		msg->text.receiver = p->receiver;
		msg->text.call_data = p->call_data;

		if( result == 1 ){
			while( forward(p) )
				;
			p->buf[p->len] = 0;
			msg->text.len = p->len;
		}else{
			p->buf[0] = 0;
			msg->text.len = 0;
		}
		msg->text.text = p->buf;
		KSSendMsg(msg);

		KSResetKeyboard();

		KSMoveCursor(p->prev_cursor_x,p->prev_cursor_y);
		KSEnableCursor(p->prev_cursor_enable);

		KSUnregistWindow(&p->win);

		WinY -= 2;
		if( WinY < WIN_Y_MIN )
			WinY = WIN_Y_MAX;

		KSFreeMemoryBlock(&LineMemMgr,p);

		return 1;
	}
	return 0;
}


int KSLineInput( unsigned char far *label, unsigned far *str, int (*receiver)(KSMsg *msg),void *call_data )
{
	line_t *p;
	unsigned code;
	int i;

	p = KSAllocateMemoryBlock(&LineMemMgr);
	if( p == NULL )
		return -1;

	p->buf = KSAllocate((BUFLEN+1)*2);
	if( p == NULL ){
		KSFreeMemoryBlock(&LineMemMgr,p);
		return -1;
	}

	if( KSStartReggFEP(handler,p) < 0 ){
		KSFree(p->buf);
		KSFreeMemoryBlock(&LineMemMgr,p);
		return -1;
	}

	i = 0;
	if( label ){
		while( *label && i < BUFLEN ){
			code = *label++;
			if( code & 0x80 )
				code = code << 8 | *label++;
			if( code & 0xff == 0 )
				break;
			if( code == 127 || code < 32 )
				code = 32;
			p->label[i++] = code;
		}
	}
	p->label[i] = 0;

	if( str ){
		i = 0;
		while( *str && i < BUFLEN ){
			code = *str++;
			if( code == 127 || code < 32 )
				code = 32;
			if( code == 0 )
				break;
			p->buf[i++] = code;
		}
		p->len = i;
		p->x = i;
	}else{
		p->len = 0;
		p->x = 0;
	}
	p->receiver = receiver;
	p->call_data = call_data;

	p->win.x = 0;
	p->win.y = WinY;
	WinY += 2;
	if( WinY > WIN_Y_MAX )
		WinY = WIN_Y_MIN;
	p->win.width = 28;
	p->win.height = 7;
	p->win.mapped = 1;
	p->win.receiver = handler;
	p->win.call_data = p;
	KSRegistWindow(&p->win);

	p->prev_cursor_enable =
			KSGetCursorStatus(&p->prev_cursor_x,&p->prev_cursor_y);
	cursor(p);
	KSEnableCursor(1);

	return 0;
}


void KSInitializeLineInput( void )
{
	KSSetMemoryBlockManager(&LineMemMgr,sizeof(line_t));
}
