/*
================================ 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        :   pbdb.c
Author(s)   :   Henry Fok
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   application c file
Revision    :   1.1
Note        :   None
===========================================================================
*/              



#include "app.h"

UBYTE chartoupper(UBYTE x)
{
	if((x>='a') && (x<='z'))
		x-= 32;
	return x;
}

/*****************************************************************
********************* Database Initialisation ********************
*****************************************************************/

/********************************************************
* Function:	PhonebookBinarySearch
* Purpose: 	This function is called to implement a binary search
* Scope:		application
* Input:		None
* Output:		None
* Return:		TRUE        handled
FALSE       not handled
* Comment: 	This function should be called in the StopApplication
*********************************************************/
BOOLEAN PhonebookBinarySearch(BYTE starting_char)
{
    UWORD   bottom, top, middle;
    UWORD   total_num_items;    
    BYTE    *item_text;
    UBYTE   temp;
	
    total_num_items = list_tel_list.total_num_items;
    (UBYTE)starting_char   =   chartoupper((UBYTE)starting_char);
	
    if (total_num_items == 0)
		return FALSE;
    else if (total_num_items == 1)
    {            
		ListDisplayGetText(&list_tel_list, 0, &item_text);
		if(item_text==NULL)
			return FALSE;
        temp    =   chartoupper((UBYTE)(*item_text));
		
        if ((UBYTE)starting_char <= temp)
		{
			list_tel_list.top_item_num  =   0;
			qfree(item_text);
			return TRUE;
		}
        else if ((UBYTE)starting_char > temp)
        {
			qfree(item_text);
			return FALSE;
		}
    }		
    else
    {
        bottom  =   0;
        top     =   total_num_items - 1;
        
        ListDisplayGetText(&list_tel_list, bottom, &item_text);
        temp    =   chartoupper((UBYTE)(*item_text));
		
        if ((UBYTE)starting_char <= temp)
		{
			list_tel_list.top_item_num  =   bottom;
			qfree(item_text);
			return TRUE;
		}
        else
        {
			qfree(item_text);
            ListDisplayGetText(&list_tel_list, top, &item_text);
            temp    =   chartoupper((UBYTE)(*item_text));
			
            if ((UBYTE)starting_char > temp)
			{
				qfree(item_text);
				return FALSE;
			}
			else if ((UBYTE)starting_char == temp)
			{
                if (total_num_items == 2)
                {
                    list_tel_list.top_item_num  =   top;
					qfree(item_text);
					return TRUE;
                }
                else
                {
                    qfree(item_text);        
                }                                    
            }		        
            else
            {
                if (total_num_items == 2)
                {
       	            list_tel_list.top_item_num  =   top;
					qfree(item_text);
					return TRUE;
                }		    	    
                else
                {
                    qfree(item_text);        
                }                                    
            }				
		}
        
        do
        {
            if (bottom == (top - 1))
            {               
                ListDisplayGetText(&list_tel_list, bottom, &item_text);
                temp    =   chartoupper((UBYTE)(*item_text));
				
                if ((UBYTE)starting_char <= temp)
				{
					list_tel_list.top_item_num  =   bottom;
					qfree(item_text);
					return TRUE;
				}
                else
                {
					list_tel_list.top_item_num  =   top;
					qfree(item_text);
					return TRUE;
				}            
            }
            else
            {
                middle  =   (top    +   bottom)/2;
				ListDisplayGetText(&list_tel_list, middle, &item_text);
				temp    =   chartoupper((UBYTE)(*item_text));       
                
                if ((UBYTE)starting_char <= temp)
				{
					top =   middle;
					qfree(item_text);
				}
                else if ((UBYTE)starting_char > temp)
                {
					bottom =   middle;
					qfree(item_text);			        
				}
            }          
        }while (top > bottom); 
    }        
}    

/********************************************************
* Function:	PhonebookCompareTwoStrings
* Purpose: 	This function is used to sort a group of string
* Scope:		application
* Input:		string1			string 1
string2			string 2
* Output:		None
* Return:		FIRST			string 1 is smaller string 2
SECOND			string 2 is smaller string 1
SAME			string 1 = string 1
* Comment: 	
*********************************************************/
BYTE PhonebookCompareTwoStrings(BYTE *string1, BYTE *string2)
{
	USHORT i = 0;
	BYTE result = SAME;
	
	while((UBYTE)string1[i] && (UBYTE)string2[i])
	{
		if(chartoupper((UBYTE)string1[i]) < chartoupper((UBYTE)string2[i]))
		{
			result = FIRST;
			break;
		}
		else if(chartoupper((UBYTE)string2[i]) < chartoupper((UBYTE)string1[i]))
		{
			result = SECOND;
			break;
		}
		i++;
	}
	if(result != SAME)
		return result;
	
	if(string1[i])
		return SECOND;
	
	if(string2[i])
		return FIRST;
	
	return SAME;
}

/********************************************************
* Function:	PhonebookCateNameToNum
* Purpose: 	This function is used find out the cate num
* Scope:		application
* Input:		dbid			The database ID
cate_name		The category name
* Output:		cate_num
* Return:		TRUE		found
FALSE		not exist
* Comment: 	The passed-in database must be opend 
*********************************************************/
BOOLEAN PhonebookCateNameToNum(DatabaseID dbid, BYTE *cate_name, UBYTE *cate_num)
{
	UBYTE cate[256];
	USHORT i;
	UBYTE num_cate;
	BYTE *cate_cate;
	
	num_cate = DataCategorySort(dbid, cate);
	cate_cate = (BYTE*)qmalloc(30*sizeof(BYTE));
	for (i = 0; i < num_cate; i++)
	{
		DataCategoryName(pb_dbid,cate[i],cate_cate);
		if (PhonebookCompareTwoStrings(cate_name,cate_cate) == SAME)
		{
			*cate_num = cate[i];
			qfree(cate_cate);
			return TRUE;
		}
	}
	DataCategoryName(pb_dbid,0,cate_cate);
	if (PhonebookCompareTwoStrings(cate_name,cate_cate) == SAME)
	{
		*cate_num = 0;
		qfree(cate_cate);
		return TRUE;
	}
	qfree(cate_cate);
    *cate_num = 255;
	return FALSE;
}

/********************************************************
* Function:	PhonebookRecordToList
* Purpose: 	This function is used to show the correct records to 
the two list objects in the TEL LIST page of the application		
* Scope:		application
* Input:		dbid			The database ID
cate_name		The required category
starting_char	The starting character		
starting_string	
* Output:		None
* Return:		TRUE		Complete
FALSE		Error
* Comment: 	The list object must be initialised first
The database must be opened first
The function will update the scrollbar too
*********************************************************/
BOOLEAN PhonebookRecordToList(DatabaseID dbid, BYTE *cate_name, 
							  BYTE starting_char, BYTE *starting_string)
{
	UBYTE cate_number;
	UWORD total_num_rec, i;
	UWORD rec_id;
	BYTE *buffer;
	UWORD byte_return;
	UWORD *rec_num;
	UWORD num_rec;
	BOOLEAN update = FALSE;
	BOOLEAN preset = FALSE;
	UWORD nth_record = 0;
	
	if (PhonebookCateNameToNum(pb_dbid, cate_name, &cate_number) == FALSE && 
		PhonebookCompareTwoStrings(cate_name,PBALL) != SAME) return FALSE;
	
	ListDisplayDeleteAllRecordID(&list_tel_list);
	
	DataTotalRecord(pb_dbid, &total_num_rec);
	if (PhonebookCompareTwoStrings(cate_name,PBALL) == SAME)
	{
		for (i = 0; i <total_num_rec; i++)
		{
			if (starting_string == NULL)
				DataNumtoRecID(pb_dbid, i, &rec_id);
			else				
			{
				DataOpenRecord(pb_dbid, i, &rec_id, NULL);
                DataGetField(pb_dbid, rec_id, 2, &buffer, &byte_return);
                if (PhonebookCompareTwoStrings(buffer, starting_string) == SAME)
				{
					preset = TRUE;
					nth_record = i;
					starting_string = NULL;
				}
				qfree(buffer);
				DataCloseRecord(pb_dbid, rec_id);
			}	
			ListDisplayInsertRecordID(&list_tel_list, rec_id);			
		}
		update = TRUE;
	}
	else
	{
		rec_num = (UWORD*)qmalloc(total_num_rec* sizeof(UWORD));
		PhonebookGetRecNumWithCateName(pb_dbid, cate_name, rec_num, &num_rec);
		for (i = 0; i<num_rec; i++)
		{
			if (starting_string == NULL)
			{
				DataNumtoRecID(pb_dbid, rec_num[i], &rec_id);
			}
			else
			{				
				DataOpenRecord(pb_dbid, rec_num[i], &rec_id, NULL);
                DataGetField(pb_dbid, rec_id, 2, &buffer, &byte_return);
                if (PhonebookCompareTwoStrings(buffer, starting_string) == SAME)
				{
					preset = TRUE;
					nth_record = i;
					starting_string = NULL;
				}
                qfree(buffer);
				DataCloseRecord(pb_dbid, rec_id);
			}				
			ListDisplayInsertRecordID(&list_tel_list, rec_id);
		}
		qfree(rec_num);
		update = TRUE;
	}
	
	
	if (update == TRUE)
	{
		PhonebookSetTelListTopItemNum(starting_char, nth_record, preset);
		return TRUE;
	}
	return FALSE;
}


/********************************************************
* Function:	PhonebookGetRecNumWithCateName
* Purpose: 	This function is called to get all record numbers of the records that 
are with the same category name
* Scope:		application
* Input:		dbid			The database ID
cate_name		The required category
* Output:		rec_num			array of record number
num_rec			number of record returned
* Return:		TRUE			success
FALSE			error occure
* Comment: 	Database must be opened
*********************************************************/
BOOLEAN PhonebookGetRecNumWithCateName(DatabaseID dbid, BYTE *cate_name, UWORD rec_num[], UWORD *num_rec)
{
	UWORD total_num_rec;
	UWORD i;
	UBYTE cate_num;
	UBYTE rec_cate_num;
	RecordID rec_id;
	UWORD total_cate_rec = 0;
	
	if (PhonebookCateNameToNum(pb_dbid, cate_name, &cate_num) == FALSE)
	{
		*num_rec = 0;
		return FALSE;
	}
	DataTotalRecord(pb_dbid, &total_num_rec);
	for ( i = 0; i<total_num_rec; i++)
	{
		DataNumtoRecID(pb_dbid, i, &rec_id);
		DataRecordInfo(pb_dbid, rec_id, NULL, &rec_cate_num, NULL, NULL, NULL);
		if (rec_cate_num == cate_num)
		{
			rec_num[total_cate_rec] = i;
			total_cate_rec++;
		}
		
	}
	*num_rec = total_cate_rec;
	return TRUE;
}

BOOLEAN PhonebookGetRecNumWithCateNameEx(DatabaseID dbid, BYTE *cate_name, UWORD rec_num[], UWORD *num_rec)
{
	UWORD total_num_rec;
	UWORD i;
	UBYTE cate_num;
	UBYTE rec_cate_num;
	RecordID rec_id;
	UWORD total_cate_rec = 0;
	
	if (PhonebookCateNameToNum(pb_dbid, cate_name, &cate_num) == FALSE)
	{
		*num_rec = 0;
		return FALSE;
	}
	DataTotalRecord(pb_dbid, &total_num_rec);
	for ( i = 0; i<total_num_rec; i++)
	{
		DataNumtoRecID(pb_dbid, i, &rec_id);
		DataRecordInfo(pb_dbid, rec_id, NULL, &rec_cate_num, NULL, NULL, NULL);
		if (rec_cate_num == cate_num)
		{
			rec_num[total_cate_rec] = rec_id;
			total_cate_rec++;
		}
		
	}
	*num_rec = total_cate_rec;
	return TRUE;
}

/********************************************************
* Function:	PhonebookCateNameToPopupTrigger
* Purpose: 	This function is used to update teh items of the 
POPUP_TRIGGER_CATE
* Scope:		application
* Input:		dbid		THE dbid of a database
* Output:		None
* Return:		None
* Comment: 	None
*********************************************************/
void PhonebookCateNameToPopupTrigger(DatabaseID dbid)
{
	UBYTE num_cate;
	UBYTE cate[255];
	BYTE *cate_name;
	SHORT i=0;
	
	
	num_cate = DataCategorySort(pb_dbid, cate);
	ControlPopupDeleteAllItems(POPUP_TRIGGER_CATE);
	ControlPopupInsertItem(POPUP_TRIGGER_CATE,0,PBALL);
	ControlPopupInsertItem(POPUP_TRIGGER_CATE,1,PBUNFILED);
	cate_name = (BYTE*)qmalloc(30*sizeof(BYTE));
	for (i = 0; i<num_cate; i++)
	{
		DataCategoryName(pb_dbid,cate[i],cate_name);
		ControlPopupInsertItem(POPUP_TRIGGER_CATE,(i+2),cate_name);
	}
	ControlPopupInsertItem(POPUP_TRIGGER_CATE,(num_cate+2),PBEDITCAT);
	qfree(cate_name);
}

BOOLEAN ListHighlightTopItem(UBYTE bChar)
{
    BYTE        *list_text;
    UBYTE       list_char;
	BOOLEAN		retVal = FALSE;
	
	ListDisplayGetText(&list_tel_list, list_tel_list.top_item_num, &list_text);
	list_char = chartoupper((UBYTE)(*list_text));
	if(list_char==bChar)
	{
		ListSetHighlightedItem(LIST_NAME,0);
		ListSetHighlightedItem(LIST_PHONENUM,0);
		retVal = TRUE;
	}
	qfree(list_text);
	return retVal;
}

/********************************************************
* Function:	PhonebookSetTelListTopItemNum
* Purpose: 	This function is called to set the top item number
of the list object - LIST_NAME and LIST_PHONENUM
* Scope:		application
* Input:		BYTE				the starting_char
* Output:		None
* Return:		TRUE			display of list is on
FALSE			display of list is off
* Comment: 	The top item number of the list objects are set
*********************************************************/
BOOLEAN PhonebookSetTelListTopItemNum(BYTE start_char, UWORD nth_record, BOOLEAN preset)
{
    USHORT      total_num_items;
	USHORT		curr_top_item;
    UWORD       count = 0;
    BYTE        *item_text;
    BOOLEAN     enable,drawn,active,visible,set_scroll;
	BOOLEAN		highlite_top = FALSE;
	//    BYTE        object_type;
	//    Scrollbar   *scroll_ptr;
    UBYTE       temp;
    BOOLEAN     temp1 = FALSE;
	
	total_num_items = list_tel_list.total_num_items;
	curr_top_item = list_tel_list.top_item_num;
	
    if (start_char >= 'a' && start_char <= 'z')
        start_char -= 32;
	
	if (preset == FALSE)
	{
        if (PhonebookBinarySearch(start_char))	
		{
			if(!ListHighlightTopItem(start_char))
				list_tel_list.top_item_num = curr_top_item;
			else
				highlite_top = TRUE;
		}
        else
			list_tel_list.top_item_num = curr_top_item;
		
		if(list_tel_list.total_num_items > 0)
		{
			ListDisplayDraw(&list_tel_list);
			ListGetAttribute(LIST_NAME, &enable, &drawn, &active, &visible, &set_scroll);
			ListSetAttribute(LIST_NAME, enable, drawn, active, TRUE, set_scroll);
			ListGetAttribute(LIST_PHONENUM, &enable, &drawn, &active, &visible, &set_scroll);
			ListSetAttribute(LIST_PHONENUM, enable, drawn, active, TRUE, set_scroll);
			PhonebookTelListSetScrollbar();
			if(highlite_top)
			{
				ListSetHighlightedItem(LIST_NAME,0);
				ListSetHighlightedItem(LIST_PHONENUM,0);
			}
		}
		else
		{
			ListGetAttribute(LIST_NAME, &enable, &drawn, &active, &visible, &set_scroll);
			ListSetAttribute(LIST_NAME, enable, drawn, active, FALSE, set_scroll);
			ListGetAttribute(LIST_PHONENUM, &enable, &drawn, &active, &visible, &set_scroll);
			ListSetAttribute(LIST_PHONENUM, enable, drawn, active, FALSE, set_scroll);
			ListEraseList(LIST_NAME);
			ListEraseList(LIST_PHONENUM);
			DisplayScrollbarIfNeeded();
			return FALSE;	
		}
	}
	else
	{
		list_tel_list.top_item_num = (USHORT)nth_record;
		if ((list_tel_list.total_num_items < list_tel_list.max_num_items_display) &&
			(((UBYTE)start_char >= 'a' && (UBYTE)start_char <= 'c') || ((UBYTE)start_char >= 32 && (UBYTE)start_char <= 'C')))
			list_tel_list.top_item_num = 0;			
		ListDisplayDraw(&list_tel_list);
		ListGetAttribute(LIST_NAME, &enable, &drawn, &active, &visible, &set_scroll);
		ListSetAttribute(LIST_NAME, enable, drawn, active, TRUE, set_scroll);
		ListGetAttribute(LIST_PHONENUM, &enable, &drawn, &active, &visible, &set_scroll);
		ListSetAttribute(LIST_PHONENUM, enable, drawn, active, TRUE, set_scroll);
		PhonebookTelListSetScrollbar();
	}
	return highlite_top;
}

/********************************************************
* Function:	PhonebookSearchForText
* Purpose: 	This function is called to search a string from a long text.
* Scope:		application
* Input:		short_string		a short text for searching
whole_string		the string to be searched			
* Output:		None
* Return:		TRUE			if the short_string can be found in the whole_string
FALSE			if the shrot_string does not exists
* Comment: 	
*********************************************************/
BOOLEAN PhonebookSearchForText(BYTE *short_string, BYTE *whole_string)
{
	UWORD length_short;
	UWORD length_whole;
	BYTE  *buffer, *buffer1, *buffer2;
	UWORD count, letter_count;
	BOOLEAN match = TRUE;
	
	
	length_short = strlen(short_string);
	length_whole = strlen(whole_string);
	buffer2 = whole_string;
	
	for (count = 0; count <= length_whole; count++)
	{
		match = TRUE;
		buffer1 = buffer2;
		buffer = short_string;
		if (strlen(buffer1) < length_short)
			return FALSE;
		
		if (*buffer == *buffer1)
		{	
			buffer++;
			buffer1++;
			buffer2 = buffer1;
			for (letter_count = 0; letter_count<(length_short - 1); letter_count++)
			{
				if (*buffer != *buffer1)
				{	
					match = FALSE;
					break;
				}
				buffer++;
				buffer1++;
			}
		}
		else
		{
			match = FALSE;
			buffer2++;
		}
		if (match == TRUE)
			return TRUE;
	}
	return FALSE;
}

/********************************************************
* Function:	PhonebookUppercaseString
* Purpose: 	This function is called to change a string to all upper case
* Scope:		application
* Input:		in_string
* Output:		out_string
* Return:		None
* Comment: 	None
*********************************************************/
void PhonebookUpperString(BYTE *in_string, BYTE **out_string)
{
	UWORD j;
	
	*out_string = (BYTE*)qmalloc((strlen(in_string) + 1) * sizeof(BYTE));
	for (j = 0; j <strlen(in_string); j++)
	{
		if (in_string[j] >= 97 && in_string[j] <= 122)
			(*out_string)[j] = in_string[j] - 32;
		else (*out_string)[j] = in_string[j];
	}
}