/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   jot.c
Author(s)   :   J. Wang
Company     :   Communcation Intelligence Corporation
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   Jot related functions
Revision    :   1.1
Note        :   None
===========================================================================
*/              



#include "stdafx.h"
#include "system.h"
#include "tmrapi.h"
#include "sysetup.h"

#include <jot.h>
#include <zorroapi.h>

//#define DEBUG

#define JOT_BOX_LEFT	20
#define JOT_BOX_RIGHT	140
#define JOT_BOX_TOP		169
#define JOT_BOX_BOTTOM	218
#define JOT_SYM_LINE	45
#define JOT_UPP_LINE	95

#define MAX_POINTS 128

int			npointsI = 0;
ZO_POINT	pointsAr[MAX_POINTS];
ZO_CHAR		wait_charG = 0;
BOOLEAN		waitingB = FALSE;
BOOLEAN		symbolB = FALSE;
UWORD		sym_timerUW = 0;
UWORD		mul_timerUW = 0;
BOOLEAN     g_collecting = FALSE;

extern unsigned int g_train;
extern int g_timeout;
extern int g_timeoutMultiStroke;

static void JotRecognize();
static void JotFlush();
static void ProcessResult
(
 ZO_RESULT   *resultP,                /* Result to process            */
	BOOLEAN		non_rec_out_nullB		// TRUE if output NULL for non-recognition
	);
static void ProcessArrayResult
(
 int         num_result,
 ZO_CHAR     *resultAr
 );
static void OutputOneChar
(
 unsigned char        chUC
 );
static void OutputOneCharInString
(
 unsigned char        chUC,
 BYTE *sAppStr,
 USHORT *sAppStrCnt
 );

static void PasteString(BYTE *sAppStr, USHORT *sAppStrCnt);

static BOOLEAN ProcessSpecialMacros
(
 int         num_result,
 ZO_CHAR     *resultAr
 );

void JotInit()
{
	long handle;
	
	zo_open(&handle);
	zo_train(0, g_train);
	zo_set_macro_recog_callback(0, MatchJotMacro);
	
	UpdateJotTrainerDB(JOT_READ_DB);
	OpenJotMacroDB();
}

void JotUpdate()
{
	UpdateJotTrainerDB(JOT_READ_DB);
	zo_train(0, g_train);
}

/********************************************************
* Function:	JotHandleEvent(EvtType *Event)
* Purpose:		to handle the event that has occured
*				on Jo input area
* Scope:		application/internal
* Input:		Event 		received event
* Output:		None
* Return:		TRUE if handled
FALSE if not handled
* Comment:     None
*********************************************************/
BOOLEAN JotHandleEvent(EvtType *Event)
{
	int	x = Event->para1;
	int y = Event->para2;
	RTM currentRTM;
	LLONG timePassed;
	
    if (Event->eventType != PEN_EVENT)
        return FALSE;
	
    if ((y < JOT_BOX_TOP || x < JOT_BOX_LEFT || x > JOT_BOX_RIGHT || y > JOT_BOX_BOTTOM) && !g_collecting)
        return FALSE;
	
    if ((Event->eventID == PEN_UP || Event->eventID == PEN_MOVE) &&
        !g_collecting)
        return FALSE;
	
	
	switch (Event->eventID)
	{
	case PEN_DOWN:
		g_collecting = TRUE;
		pointsAr[0].x = x;
		pointsAr[0].y = y;
		npointsI = 1;
		if (sym_timerUW != 0 && symbolB)
		{
			TmrIntDisable(sym_timerUW);
			sym_timerUW = TmrIntEnable(g_timeout, JotFlush);
		}
		break;
	case PEN_MOVE:
		if (npointsI < MAX_POINTS)
		{
			pointsAr[npointsI].x = x;
			pointsAr[npointsI].y = y;
			npointsI++;
		}
		break;
	case PEN_UP:
		g_collecting = FALSE;
		JotRecognize();
		SndPlaySndEffect(SNDRES5_BEEP);
		break;
	}
	return TRUE;
}

static void JotRecognize()
{
	int			i;
    int         input_method;
    ZO_STROKE   stroke;                 /* Stroke to be recognized      */
    ZO_RESULT   result;                 /* Returned result              */
	int			xmin, xmax, ymin, ymax;
	ZO_MODELINE modeline;	// Jot's modeline
	
	waitingB = FALSE;
	
    stroke.resolution   = 100;     
    stroke.num_points	= npointsI;
	stroke.points		= (ZO_POINT *)&pointsAr[0];
	
	xmin = 32767;
	ymin = 32767;
	xmax = 0;
	ymax = 0;
	for (i = 0; i < npointsI; i++)
	{
		if (pointsAr[i].x < xmin)
			xmin = pointsAr[i].x;
		if (pointsAr[i].x > xmax)
			xmax = pointsAr[i].x;
		if (pointsAr[i].y < ymin)
			ymin = pointsAr[i].y;
		if (pointsAr[i].y > ymax)
			ymax = pointsAr[i].y;
	}
	
	stroke.bound.top	= ymin;
	stroke.bound.bottom = ymax;
	stroke.bound.left	= xmin;
	stroke.bound.right	= xmax;
	
	/* See if the stroke is written in symbol area */
	if ((xmin + xmax) / 2 < JOT_SYM_LINE)
	{
		if (symbolB)
		{
		}
		else
		{
			symbolB = TRUE;
			zo_mode(0, ZO_INPUT_MODELINE, PZ_MODE_EXTENDED);
			sym_timerUW = TmrIntEnable(g_timeout, JotFlush);
		}
	}
	else if (symbolB)
		JotFlush();
	
	input_method = ZO_INPUT_MODELINE | ZO_INPUT_VERTICAL_RIGHTNUM;
	
	modeline.position.x = JOT_UPP_LINE;
	modeline.position.y = JOT_UPP_LINE;
	modeline.direction = ZO_MODELINE_VERTICAL;
	
	result.num_result = 0;
	result.num_macro = 0;
	zo_stroke(0, input_method, &modeline, &stroke, &result);
	
	ProcessResult(&result, TRUE);
	
	if (waitingB && !symbolB && mul_timerUW == 0)
		mul_timerUW = TmrIntEnable(g_timeoutMultiStroke, JotFlush);
	
}	/* End of JotRecognize()	*/

static void JotFlush()
{
	ZO_RESULT	result;
	
	symbolB = FALSE;
	if (sym_timerUW)
	{
		TmrIntDisable(sym_timerUW);
		sym_timerUW = 0;
	}
	if (mul_timerUW)
	{
		TmrIntDisable(mul_timerUW);
		mul_timerUW = 0;
	}
	zo_flush(0, &result);
	ProcessResult(&result, FALSE);
	zo_mode(0, ZO_INPUT_MODELINE, PZ_MODE_DEFAULT);
}

/* ProcessResult()
*----------------------------------------------------------------------------
* Process the returned result
*----------------------------------------------------------------------------
*/    
static void ProcessResult
(
 ZO_RESULT   *resultP,                /* Result to process            */
	BOOLEAN		non_rec_out_nullB		// TRUE if output NULL for non-recognition
	)
{                                     
    if (non_rec_out_nullB && resultP->num_macro <= 0 && resultP->num_result <= 0)
	{
        resultP->num_result = 1;
        resultP->resultAr[0] = NC;
	}
    if (resultP->num_macro > 0)
	{
		//                printf("num_macro = %d, macro = %s\n", resultP->num_macro, resultP->macroP);
		if (!ProcessSpecialMacros(resultP->num_macro, resultP->macroP))
			ProcessArrayResult(resultP->num_macro, resultP->macroP);
		qfree(resultP->macroP);
	}
    ProcessArrayResult(resultP->num_result, resultP->resultAr);
	
}   /* End of ProcessResult()       */

static void GetTime(RTM *rtm, char *time)
{
/*
BOOLEAN pm = FALSE;

  if (rtm->hour >= 12)
		{
		pm = TRUE;
		if (rtm->hour > 12)
		rtm->hour -= 12;
		}
		time[0] = rtm->hour / 10 + '0';
		time[1] = rtm->hour % 10 + '0';
		time[2] = ':';
		time[3] = rtm->min / 10 + '0';
		time[4] = rtm->min % 10 + '0';
		time[5] = ':';
		time[6] = rtm->sec / 10 + '0';
		time[7] = rtm->sec % 10 + '0';
		time[8] = ' ';
		if (pm)
		time[9] = 'P';
		else
		time[9] = 'A';
		time[10] = 'M';
		time[11] = 0;
	*/
	
	SHORT	byte_write = 0;
	CountrySettings	country;
	if (!SySetupGetCountrySettings(&country))
		return;
	//		return FALSE;
	
	switch(country.time_fmt)
	{
	case SYSETUP_12HR_DISPLAY:
		if (rtm->hour < 10)
			if (rtm->hour == 0)
				byte_write += sprintf((char*)time + byte_write, "%d:", 12);
			else
				byte_write += sprintf((char*)time + byte_write, "%d:", rtm->hour);
			else
				if (rtm->hour < 13)
					byte_write += sprintf((char*)time + byte_write, "%d:", rtm->hour);
				else
					byte_write += sprintf((char*)time + byte_write, "%d:", rtm->hour - 12);
				if (rtm->min < 10)
					byte_write += sprintf((char*)time + byte_write, "0%d", rtm->min);
				else
					byte_write += sprintf((char*)time + byte_write, "%d", rtm->min);
				if (rtm->hour < 12)
					byte_write += sprintf((char*)time + byte_write, SYAM);
				else
					byte_write += sprintf((char*)time + byte_write, SYPM);
				break;
				
	case SYSETUP_24HR_DISPLAY:
		if (rtm->hour < 10)
			byte_write += sprintf((char*)time + byte_write, "0%d:", rtm->hour);
		else
			byte_write += sprintf((char*)time + byte_write, "%d:", rtm->hour);
		if (rtm->min < 10)
			byte_write += sprintf((char*)time + byte_write, "0%d", rtm->min);
		else
			byte_write += sprintf((char*)time + byte_write, "%d", rtm->min);
		break;
		
		//		default:	return FALSE;
	}
	
}

static GetDate(RTM *rtm, char *date)
{
/*
static char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

  date[0] = 0;
  if (rtm->mon >= 0 && rtm->mon < 12)
		strcpy(date, months[rtm->mon]);
		date[3] = ' ';
		date[4] = rtm->mday / 10 + '0';
		date[5] = rtm->mday % 10 + '0';
		date[6] = ',';
		date[7] = ' ';
		date[8] = rtm->year / 1000 + '0';
		date[9] = (rtm->year / 100) % 10 + '0';
		date[10] = (rtm->year / 10) % 10 + '0';
		date[11] = rtm->year % 10 + '0';
		date[12] = 0;
	*/
	SHORT	byte_write = 0;
	CountrySettings	country;
	if (!SySetupGetCountrySettings(&country))
		return;
	//		return FALSE;
	
	rtm->mon += 1;
	
	/* display month, day & year */
	switch(country.date_fmt)
	{
	case SYSETUP_YMD:
		if ((rtm->year)%100 < 10)
			byte_write = sprintf((char*)date, "0%d/", rtm->year%100);
		else
			byte_write = sprintf((char*)date, "%d/", rtm->year%100);
		//			if (rtm->mon < 10)
		//				byte_write += sprintf((char*)date + byte_write, "0%d/", rtm->mon);
		//			else
		byte_write += sprintf((char*)date + byte_write, "%d/", rtm->mon);
		if (rtm->mday < 10)
			byte_write += sprintf((char*)date + byte_write, "0%d", rtm->mday);
		else
			byte_write += sprintf((char*)date + byte_write, "%d", rtm->mday);
		break;
		
	case SYSETUP_DMY:
		if (rtm->mday < 10)
			byte_write  = sprintf((char*)date,     "0%d/", rtm->mday);
		else
			byte_write  = sprintf((char*)date,     "%d/", rtm->mday);
		//			if (rtm->mon < 10)
		//				byte_write += sprintf((char*)date + byte_write, "0%d/", rtm->mon);
		//			else
		byte_write += sprintf((char*)date + byte_write, "%d/", rtm->mon);
		if (rtm->year%100 < 10)
			byte_write += sprintf((char*)date + byte_write, "0%d", rtm->year%100);
		else
			byte_write += sprintf((char*)date + byte_write, "%d", rtm->year%100);
		break;
		
	case SYSETUP_MDY:
		//			if (rtm->mon < 10)
		//				byte_write  = sprintf((char*)date,     "0%d/", rtm->mon);
		//			else
		byte_write  = sprintf((char*)date,     "%d/", rtm->mon);
		if (rtm->mday < 10)
			byte_write += sprintf((char*)date + byte_write, "0%d/", rtm->mday);
		else
			byte_write += sprintf((char*)date + byte_write, "%d/", rtm->mday);
		if ((rtm->year)%100 < 10)
			byte_write += sprintf((char*)date + byte_write, "0%d", rtm->year%100);
		else
			byte_write += sprintf((char*)date + byte_write, "%d", rtm->year%100);
		break;
		
		//		default:	return FALSE;
	}
	
	
}

static GetWeek(RTM *rtm, char *week)
{
    static char *weeks[7] = {SYSUN, SYMON, SYTUE, SYWED,
        SYTHU, SYFRI, SYSAT};
	
	week[0] = 0;
	if (rtm->wday >= 0 && rtm->wday < 7)
		strcpy(week, weeks[rtm->wday]);
}

static BOOLEAN ProcessSpecialMacros
(
 int         num_result,
 ZO_CHAR     *resultAr
 )
{
	RTM rtm;
	char time[64];
	int len;
	int i;
	
	BYTE sAppendString[260];
    USHORT Cnt;
    USHORT *sAppendStringCnt;
	Cnt = 0;
	sAppendStringCnt = &Cnt;
	
	resultAr[num_result] = 0;
	time[0] = 0;
	if (resultAr[0] == '[')
	{
		RtcGetTime(&rtm);
		if (strcmp(resultAr, SYSTR5) == 0)
		{
			GetTime(&rtm, time);
		}
		else if (strcmp(resultAr, SYSTR6) == 0)
		{
			GetDate(&rtm, time);
		}
		else if (strcmp(resultAr, SYSTR7) == 0)
		{
			GetDate(&rtm, time);
			len = strlen(time);
			time[len] = ' ';
			GetTime(&rtm, &time[len+1]);
		}
		else if (strcmp(resultAr, SYSTR8) == 0)
		{
			GetWeek(&rtm, time);
		}
		else
			return FALSE;
		len = strlen(time);
		
		for (i = 0; i < len; i++)
			OutputOneCharInString(time[i], sAppendString, sAppendStringCnt);
		
		//    printf("\nPaste String %s %d", sAppendString, *sAppendStringCnt);
		PasteString(sAppendString, sAppendStringCnt);
		
		return TRUE;
	}
	return FALSE;
}

#if 0
static BOOLEAN ProcessSpecialMacros
(
 int         num_result,
 ZO_CHAR     *resultAr
 )
{
	RTM rtm;
	char time[64];
	int len;
	int i;
	
	resultAr[num_result] = 0;
	time[0] = 0;
	if (resultAr[0] == '[')
	{
		RtcGetTime(&rtm);
		if (strcmp(resultAr, SYSTR5) == 0)
		{
			GetTime(&rtm, time);
		}
		else if (strcmp(resultAr, SYSTR6) == 0)
		{
			GetDate(&rtm, time);
		}
		else if (strcmp(resultAr, SYSTR7) == 0)
		{
			GetDate(&rtm, time);
			len = strlen(time);
			time[len] = ' ';
			GetTime(&rtm, &time[len+1]);
		}
		else if (strcmp(resultAr, SYSTR8) == 0)
		{
			GetWeek(&rtm, time);
		}
		else
			return FALSE;
		len = strlen(time);
		for (i = 0; i < len; i++)
			OutputOneChar(time[i]);
		return TRUE;
	}
	return FALSE;
}

static void ProcessArrayResult
(
 int         num_result,
 ZO_CHAR     *resultAr
 )
{
    int         i;
	
    for (i = 0; i < num_result; i++)
	{
        switch (resultAr[i])
		{
		case PZ_INK_KEEP_THIS:
			waitingB = TRUE;
			break;
		case PZ_INK_CLEAR_ALL:
			waitingB = FALSE;
			break;
		case PZ_MODE_UCALPHA:
		case PZ_MODE_LCALPHA:
		case PZ_MODE_NUMERIC:
			break;
		case PZ_MODE_PUNCTUATION:
		case PZ_MODE_EXTENDED:
			waitingB = TRUE;
			break;
		case PZ_CHANGE_LAST_CHAR:
			OutputOneChar(BACKSPACE);  
			break;
		case PZ_WAIT:
		case PZ_WAIT_X:
		case PZ_WAIT_FOUR:
			break;
		case PZ_NEXT_RESULT_WAITING:
			i++;
			wait_charG = resultAr[i];
			if (resultAr[i] != NC)
				OutputOneChar(resultAr[i]); 
			break;
		case PZ_NEXT_RESULT_END_WAITING:
			i++;
			if (wait_charG != resultAr[i])
			{
				if (wait_charG != NC)
					OutputOneChar(BACKSPACE);
				OutputOneChar(resultAr[i]);
			} 
			wait_charG = NC;
			break;
		case PZ_GESTURE_CUT:
			OutputOneChar(VTECH_CUT);
			break;
		case PZ_GESTURE_PASTE:
			OutputOneChar(VTECH_PASTE);
			break;
		case PZ_GESTURE_COPY:
			OutputOneChar(VTECH_COPY);
			break;
		case PZ_GESTURE_UNDO:
			OutputOneChar(VTECH_UNDO);
			break;
		case PZ_GESTURE_DELETE: 
			OutputOneChar(VTECH_DELETE);
			break;
		case PZ_MACRO:
			break;
		case PZ_OPERATION:
			i++;
			break;
		case PZ_GESTURE_END:
			break;
			
		case PZ_REAL_DOT:
			OutputOneChar('.');
			break;
			
			/* These characters are waiting for potential accent marks */
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
		case 'n':
		case 'y':
		case 'A':
		case 'E':
		case 'I':
		case 'O':
		case 'U':
		case 'N':
		case 'Y':
			waitingB = TRUE;
		default:
			OutputOneChar(resultAr[i]);
		}
	}
}   /* End of ProcessArrayResult()  */
#endif
static void OutputOneChar
(
 unsigned char        chUC
 )
{
#ifdef DEBUG
	printf("OutputOneChar %d %c\n", chUC, chUC);
#endif
	KeyboardSendEvent(chUC, FALSE, FALSE, FALSE);
}   /* End of OutputOneChar()       */


static void ProcessArrayResult
(
 int         num_result,
 ZO_CHAR     *resultAr
 )
{
    int         i;
	
	BYTE sAppendString[260];
    USHORT Cnt;
    USHORT *sAppendStringCnt;
	
	Cnt = 0;
	sAppendStringCnt = &Cnt;
	
    for (i = 0; i < num_result; i++)
	{
        switch (resultAr[i])
		{
		case PZ_INK_KEEP_THIS:
			waitingB = TRUE;
			break;
		case PZ_INK_CLEAR_ALL:
			waitingB = FALSE;
			break;
		case PZ_MODE_UCALPHA:
		case PZ_MODE_LCALPHA:
		case PZ_MODE_NUMERIC:
			break;
		case PZ_MODE_PUNCTUATION:
		case PZ_MODE_EXTENDED:
			waitingB = TRUE;
			break;
		case PZ_CHANGE_LAST_CHAR:
			OutputOneChar(BACKSPACE);
			break;
		case PZ_WAIT:
		case PZ_WAIT_X:
		case PZ_WAIT_FOUR:
			break;
		case PZ_NEXT_RESULT_WAITING:
			i++;
			wait_charG = resultAr[i];
			if (resultAr[i] != NC)
				OutputOneCharInString(resultAr[i], sAppendString, sAppendStringCnt);
			break;
		case PZ_NEXT_RESULT_END_WAITING:
			i++;
			if (wait_charG != resultAr[i])
			{
				if (wait_charG != NC)
					OutputOneChar(BACKSPACE);
				OutputOneCharInString(resultAr[i], sAppendString, sAppendStringCnt);
			}
			wait_charG = NC;
			break;
		case PZ_GESTURE_CUT:
			OutputOneChar(VTECH_CUT);
			break;
		case PZ_GESTURE_PASTE:
			OutputOneChar(VTECH_PASTE);
			break;
		case PZ_GESTURE_COPY:
			OutputOneChar(VTECH_COPY);
			break;
		case PZ_GESTURE_UNDO:
			OutputOneChar(VTECH_UNDO);
			break;
		case PZ_GESTURE_DELETE:
			OutputOneChar(VTECH_DELETE);
			break;
		case PZ_MACRO:
			break;
		case PZ_OPERATION:
			i++;
			break;
		case PZ_GESTURE_END:
			break;
			
		case PZ_REAL_DOT:
			OutputOneChar('.');
			break;
			
			/* These characters are waiting for potential accent marks */
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
		case 'n':
		case 'y':
		case 'A':
		case 'E':
		case 'I':
		case 'O':
		case 'U':
		case 'N':
		case 'Y':
			waitingB = TRUE;
		default:
			OutputOneCharInString(resultAr[i], sAppendString, sAppendStringCnt);
		}
	}
	
	PasteString(sAppendString, sAppendStringCnt);
}   /* End of ProcessArrayResult()  */

static void OutputOneCharInString
(
 unsigned char        chUC,
	BYTE *sAppStr,
    USHORT *sAppStrCnt
    )
{
#ifdef DEBUG
    printf("\nOutputOneCharInString = %d, Char = %d %c", *sAppStrCnt, chUC, chUC);
#endif
	
   	sAppStr[*sAppStrCnt] = chUC;
   	*sAppStrCnt = *sAppStrCnt + 1;
   	sAppStr[*sAppStrCnt] = 0;
}   /* End of OutputOneChar()       */

static void PasteString(BYTE *sAppStr, USHORT *sAppStrCnt)
{
    ObjectID    active_form_id = 0;
    ObjectID    active_object_id = 0;
	BOOLEAN     highlighted = FALSE, insert_pt = FALSE;
	BYTE        object_type;
	void        *addr;
	Field       *field_ptr;
	Textbox     *textbox_ptr;
	BYTE*       temp_text;
	
	if (*sAppStrCnt == 0)
		return;
	if (*sAppStrCnt == 1)
	{
		KeyboardSendEvent(sAppStr[0], FALSE, FALSE, FALSE);
		return;
	}
    if (FormGetActiveFormID(&active_form_id) != TRUE)
        return;
	
    if (FormGetActiveObject(active_form_id, &active_object_id) != TRUE)
        return;
	
	if (FormGetObjectPointer(active_object_id, &object_type, (void**)&addr) == FALSE)
		return;
	
    temp_text = (BYTE*)pmalloc((strlen(sAppStr) + 1) * sizeof(BYTE));
    strcpy(temp_text, sAppStr);
    
    if (object_type == FIELD)
		EvtAppendEvt(EVT_FIELD_JOT_PASTE_STRING, active_object_id, 0, 0, (void*)temp_text);
	else if (object_type == TEXTBOX)
		EvtAppendEvt(EVT_TEXTBOX_JOT_PASTE_STRING, active_object_id, 0, 0, (void*)temp_text);
}
