/*-
 * 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"ks.h"

#pragma noregalo

#define MAX_WIDTH 16
#define MAX_ITEMS 64

typedef struct{
	unsigned label[MAX_WIDTH];
	int (*receiver)(KSMsg *msg);
	void *call_data;
}item_t;

static item_t Label;
static item_t Item[MAX_ITEMS];

static int NumItems;
static int Width;
static int Receiver( KSMsg *msg );
static KSWindow Win;
static int Cursor;

static int PrevCursorX,PrevCursorY;
static int PrevCursorEnable;
static int PrevInputMode;

static int copy( unsigned *dst, unsigned char far *src )
{
	unsigned code;
	int width;

	width = 0;
	while( *src ){
		code = *src++;
		if( code & 0x80 )
			code = code << 8 | *src++;
		*dst++ = code;
		width++;
		if( width == MAX_WIDTH )
			break;
	}
	if( width < MAX_WIDTH )
		*dst = 0;
	return width;
}



void KSMenuCreate( unsigned char far *label, int (*can_receiver)(KSMsg *msg),void *call_data)
{
	int w;

	NumItems = 0;
	Width = 3;
	Label.receiver = can_receiver;
	Label.call_data = call_data;
	w = copy(Label.label,label);
	if( w > Width )
		Width = w;
}


int KSMenuAddItem( unsigned char far *label, int (*receiver)(KSMsg *msg), void *call_data )
{
	int w;

	if( NumItems == MAX_ITEMS )
		return -1;

	w = copy(Item[NumItems].label,label);
	if( w > Width )
		Width = w;
	Item[NumItems].receiver = receiver;
	Item[NumItems].call_data = call_data;
	return NumItems++;
}


static void draw_item( item_t *ip, int y , int plt)
{
	int x;

	for( x = 0 ; x < MAX_WIDTH ; x++ ){
		if( ip->label[x] == 0 )
			break;
		KSDrawCharacter(&Win,x+2,y,ip->label[x],plt);
	}
}


static void draw_cursor( void )
{
	KSDrawCharacter(&Win,1,Cursor%8+2,'',KS_PLT_NORMAL);
}

static void erase_cursor( void )
{
	KSDrawCharacter(&Win,1,Cursor%8+2,' ',KS_PLT_NORMAL);
}

static void draw( void )
{
	int c,i;
	item_t *ip;
	int x;

	KSDrawWindowBorder(&Win);
	KSClearArea(&Win,1,1,Win.width-2,Win.height-2,KS_PLT_NORMAL);

	draw_item(&Label,0,KS_PLT_NORMAL);

	c = Cursor & ~7;
	ip = Item + c;
	for(  i = 0 ; i < 8 ; i++ ){
		if( ip->receiver )
			draw_item(ip,i+2,KS_PLT_NORMAL);
		else
			draw_item(ip,i+2,KS_PLT_GRAY);
		ip++;
		c++;
		if( c == NumItems )
			break;
	}
	if( NumItems > 8 ){
		x = Win.width - 4;
		KSDrawCharacter(&Win,x++,Win.height-1,
					Cursor/8+'1',KS_PLT_NORMAL);
		KSDrawCharacter(&Win,x++,Win.height-1,'/',KS_PLT_NORMAL);
		KSDrawCharacter(&Win,x++,Win.height-1,
					(NumItems-1)/8+'1',KS_PLT_NORMAL);
	}

}


static void select( KSMsg *msg, item_t *ip, int cursor )
{
	msg->type = KS_MSG_MENU;
	msg->menu.item = cursor;
	msg->menu.receiver = ip->receiver;
	msg->menu.call_data = ip->call_data;
	KSSendMsg(msg);
}


static void reset( void )
{
	KSUnregistWindow(&Win);
	KSMoveCursor(PrevCursorX,PrevCursorY);
	KSEnableCursor(PrevCursorEnable);
	KSRemoveKeyHandler();
	KSSetInputMode(PrevInputMode);
}


static int handler( KSMsg *msg )
{
	int page;

	if( msg->type == KS_MSG_EXPOSURE ){
		draw();
		draw_cursor();
		return 0;
	}
	if( msg->type == KS_MSG_STRING ){
		page = Cursor / 8;
		erase_cursor();
		switch( msg->string.str[0] ){
		case KEYCODE_UP:
		case KEYCODE_PRIOR:
			if( Cursor > 0 )
				Cursor--;
			break;
		case KEYCODE_DOWN:
		case KEYCODE_NEXT:
			if( Cursor < NumItems - 1 )
				Cursor++;
			break;
		case KEYCODE_RIGHT:
		case KEYCODE_END:
			Cursor += 8;
			if( Cursor >= NumItems )
				Cursor = NumItems - 1;
			break;
		case KEYCODE_LEFT:
		case KEYCODE_HOME:
			Cursor -= 8;
			if( Cursor < 0 )
				Cursor = 0;
			break;

		case KEYCODE_SELECT:
			if( Item[Cursor].receiver ){
				reset();
				select(msg,Item+Cursor,Cursor);
				return 1;
			}
			break;
		case KEYCODE_CONVERSION:
			reset();
			if( Label.receiver ){
				select(msg,&Label,-1);
				return 1;
			}
			break;
		}
		if( Cursor/8 != page )
			draw();
		draw_cursor();
	}
	return 0;
}



int KSMenuStart( void )
{
	Cursor = 0;
	Win.width = Width+3;
	Win.height = (NumItems > 8 ? 8 : NumItems) + 4;
	Win.x = (28-Win.width)/2;
	Win.y = (13-Win.height)/2;
	Win.mapped = 1;
	Win.receiver = handler;
	Win.call_data = NULL;
	KSRegistWindow(&Win);
	if( KSSetKeyHandler(handler,NULL) < 0 )
		return -1;
	PrevInputMode = KSGetInputMode();
	KSSetInputMode(KS_INPUT_MODE_RAW);
	PrevCursorEnable = KSGetCursorStatus(&PrevCursorX,&PrevCursorY);
	KSEnableCursor(0);
	return 0;
}
