/*
+--------------------------------------------------------------------+
| PROJECT: MMI-Framework (8417)         $Workfile:: mfw_kbd.c       $|
| $Author:: Es  $ CONDAT GmbH           $Revision:: 18              $|
| CREATED: 21.09.98                     $Modtime:: 23.03.00 8:24    $|
| STATE  : code                                                      |
+--------------------------------------------------------------------+

   MODULE  : MFW_KBD

   PURPOSE : keyboard handling functions

   EXPORT  :

   TO DO   : adjust PEI/driver interface

   HISTORY :

    xrashmic 22 Aug, 2004 MMI-SPR-32798
    Adding the support for screen capture using a dynamically assigned key.

   Apr 03, 2005 REF: CRR 29988 - xpradipg
   Description: Optimisation 2: Reduce the keyboard buffer and the dtmf buffer
   size
   Solution : The keyboard buffer is reduced to 20 from 50

*/

#define ENTITY_MFW

#include "typedefs.h"
#include "vsi.h"
#include "pei.h"
#include "custom.h"
#include "gsm.h"

#include <stdio.h>
#include <string.h>

#include "mfw_mfw.h"
#include "mfw_sys.h"
#include "mfw_tim.h"
#include "drv_key.h"
#include "mfw_win.h"
#include "mfw_kbd.h"

/* BEGIN ADD: Req ID: : Sumit : 14-Mar-05 */
#ifndef NEPTUNE_BOARD
/* END ADD: Req ID: : Sumit : 14-Mar-05 */
#include "kpd/kpd_api.h"
/* BEGIN ADD: Req ID: : Sumit : 14-Mar-05 */
#else
#include "ti1_key.h"
#endif /* NEPTUNE_BOARD*/
/* END ADD: Req ID: : Sumit : 14-Mar-05 */

#include "mfw_ffs.h"
#include "mfw_utils.h"

#include "mfw_mme.h"


static int kbdCommand (U32 cmd, void *h);
static int toLong (U32 t, void *h);
static int toAuto (U32 t, void *h);

static MfwCb doAlways;                  /* all cases handler        */
static MfwHdr timLongH;                 /* the long press timer     */
static MfwTim timLong;
static MfwHdr timAutoH;                 /* the auto repeat timer    */
static MfwTim timAuto;
static U32 valAuto;                     /* auto start intervall     */
static U32 valRepeat;                   /* auto repeat intervall    */
static U32 curMap;                      /* current key map          */
static U8 curKey;                       /* current key code         */

static int still_processing_flag;

// PATCH LE 06.06.00
// current MFW element needed for multiple instances
EXTERN MfwHdr * current_mfw_elem;
// END PATCH LE 06.06.00

/*
** KeyPress Buffer Macros and Limits
*/
//   Apr 03, 2005 REF: CRR 29988 - xpradipg
//	Keyboard buffer reduced from 50 to 20
#ifdef FF_MMI_REDUCED_KBD_BUFFER
#define MAX_KPRESS_BFR 20
#else
#define MAX_KPRESS_BFR 50
#endif
typedef struct keyPressDetailsTag {
	char make;
	char key;
} keyPressDetails;

static keyPressDetails keyPressBfr[MAX_KPRESS_BFR];
static SHORT mfw_kbd_kpress_buf_id;

extern void sendKeyInd( T_KPD_VIRTUAL_KEY_ID virtual_key_id,
						T_KPD_KEY_STATE key_state,
						T_KPD_PRESS_STATE press_state);
extern char drvGetKeyIndex( char key);

/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdInit            |
+--------------------------------------------------------------------+

  PURPOSE : initialize keyboard handler

*/

MfwRes kbdInit (void)
{
    timLong.time = 0;                   /* setup long press timer   */
    timLong.left = 0;
    timLong.handler = toLong;
    timLong.next = 0;
    timLongH.data = &timLong;
    timLongH.type = MfwTypTim;

    timAuto.time = 0;                   /* setup auto repeat timer  */
    timAuto.left = 0;
    timAuto.handler = toAuto;
    timAuto.next = 0;
    timAutoH.data = &timAuto;
    timAutoH.type = MfwTypTim;
    valAuto = valRepeat = 0;

    keyInit(kbdSignal);                 /* init keyboard driver     */

    mfwCommand[MfwTypKbd] = (MfwCb) kbdCommand;
    doAlways = 0;

    mfw_kbd_kpress_buf_id = mfw_cbuf_create(MAX_KPRESS_BFR,
    										  sizeof(keyPressDetails),
    										  FALSE,
    										  0xFF,
    										  TRUE,
    										  keyPressBfr);

	if (mfw_kbd_kpress_buf_id < 0)
		TRACE_EVENT_P1("ERROR : mfw_cbuf_create failed with error value %d", mfw_kbd_kpress_buf_id);

    return MfwResOk;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdExit            |
+--------------------------------------------------------------------+

  PURPOSE : finalize keyboard handler

*/

MfwRes kbdExit (void)
{
    mfwCommand[MfwTypKbd] = 0;
    keyExit();

    return MfwResOk;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdAlways          |
+--------------------------------------------------------------------+

  PURPOSE : create keyboard control for all events

*/

MfwCb kbdAlways (MfwCb f)
{
    MfwCb always = doAlways;

    doAlways = f;
	if  (!f)
	{
        timStop(&timLongH);
        timStop(&timAutoH);
	}

    return always;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdTime            |
+--------------------------------------------------------------------+

  PURPOSE : define keyboard timeouts

*/

void kbdTime (long tLong, long tAuto, long tRepeat)
{
    timLong.time = tLong;               /* setup long press timer   */
    timAuto.time = tAuto;               /* setup auto repeat timer  */
    valAuto = tAuto;                    /* save auto start timeout  */
    valRepeat = tRepeat;                /* save repeat intervall    */
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdCreate          |
+--------------------------------------------------------------------+

  PURPOSE : create keyboard control

*/

MfwHnd kbdCreate (MfwHnd w, MfwEvt e, MfwCb f)
{
    MfwHdr *hdr = (MfwHdr *) mfwAlloc(sizeof(MfwHdr));
    MfwKbd *kbd = (MfwKbd *) mfwAlloc(sizeof(MfwKbd));
	MfwHdr *insert_status =0;
	
    if (!hdr || !kbd)
    {
    	TRACE_ERROR("ERROR: kbdCreate() Mem Alloc Failed.");
			
	   	if(hdr)
   			mfwFree((U8*)hdr,sizeof(MfwHdr));
   		if(kbd)
   			mfwFree((U8*)kbd,sizeof(MfwKbd));	
	   	return 0;
  	}

    kbd->map = e;
    kbd->key = 0;
    kbd->handler = f;

    hdr->data = kbd;
    hdr->type = MfwTypKbd;

    insert_status = mfwInsert(w,hdr);
    
    if(!insert_status)
	{
  		TRACE_ERROR("ERROR: kbdCreate() Failed to Install Handler. ");
   		mfwFree((U8*)hdr,sizeof(MfwHdr));
   		mfwFree((U8*)kbd ,sizeof(MfwKbd));
		return 0;
  	}
    return insert_status;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdDelete          |
+--------------------------------------------------------------------+

  PURPOSE : delete keyboard control

*/

MfwRes kbdDelete (MfwHnd h)
{
    MfwRes res;

    if (!h)
        return MfwResIllHnd;

    res = (mfwRemove(h)) ? MfwResOk : MfwResIllHnd;

    mfwFree(((MfwHdr *) h)->data,sizeof(MfwKbd));
    mfwFree(h,sizeof(MfwHdr));

    return res;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : sigExec            |
+--------------------------------------------------------------------+

  PURPOSE : execute keyboard signal
            if the registered set of keys match the currently
            pressed key, the event handler is called, if the
            registered flags match the current flags according
            to the following table:
            M: Make/Break (key release events wanted)
            L: Long press event (timeout set via kbdTime())
            A: Auto repeat event (timing defined via kbdTime())

                                            M 0 0 0 0 1 1 1 1
            current event flags ---------   L 0 0 0 0 0 0 1 1
                                            A 0 0 0 0 0 1 0 1
                                      M L A .................
                                      0 0 0 . 0 0 0 0 1 0 0 0
                                      0 0 1 . 0 0 0 0 1 1 0 1
            registered                0 1 0 . 0 0 0 0 0 0 1 1
            event       -----------   0 1 1 . 0 0 0 0 0 1 1 1
            flags                     1 0 0 . 1 1 1 1 1 0 0 0
                                      1 0 1 . 1 1 1 1 1 1 0 1
                                      1 1 0 . 1 1 1 1 0 0 1 1
                                      1 1 1 . 1 1 1 1 0 1 1 1

*/

static int sigExec (MfwHdr *curElem, U32 map, U8 key)
{
    MfwKbd *kc;

    while (curElem)
    {
        if (curElem->type == MfwTypKbd)
        {
            kc = curElem->data;
            if (kc->map & map & ~KEY_FLAGS)
            {                           /* keys match               */
                kc->code = key;         /* set current key code     */
                if (map & KEY_MAKE)
                {
                    kc->key |= map;     /* set key in control block */
                }
                else
                {
                    kc->key &= ~map;    /* del key in control block */
                    kc->key &= ~KEY_MAKE; /* del make/break flag    */
                }
                if ((KEY_MAKE & ~map & kc->map) ||
                    (KEY_MAKE & map &&
                        ((KEY_LONG & map & kc->map)        ||
                         (!(KEY_LONG & kc->map)
                            && (KEY_AUTO & kc->map & map)) ||
                         (!(KEY_LONG & map)
                            && !(KEY_AUTO & map)
                            && !(KEY_LONG & kc->map))      ||
                         (!(KEY_LONG & map)
                            && (KEY_AUTO & map)
                            && (KEY_AUTO & kc->map)))))
                {
                    if (kc->handler)    /* handler valid            */
                    {
                      // PATCH LE 06.06.00
                      // store current mfw elem
                      current_mfw_elem = curElem;
                      // END PATCH LE 06.06.00
                      if ((*(kc->handler))(map,kc))
                          return 1;   /* event consumed           */
                    }
                }
            }
        }
        curElem = curElem->next;
    }

    return 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : sigDistribute      |
+--------------------------------------------------------------------+

  PURPOSE : distribute keyboard event

*/

static void sigDistribute (U32 map, U8 key)
{
    int res = 0;

    TRACE_EVENT("sigDistribute");

    if (doAlways)
        if (doAlways(map,(void*) -1))
            return;                     /* event consumed           */

    if (mfwSignallingMethod == 0)
    {
        if (mfwFocus)
            res = sigExec(mfwFocus,map,key);
        if (!res && mfwRoot)
            res = sigExec(mfwRoot,map,key);
    }
    else
    {
        MfwHdr *h = mfwFocus;
        if (!h)
            h = mfwRoot;
        while (h)
        {
            if ((res = sigExec(h,map,key)))   // RAVI - Introduced brackets.
                break;
            if (h == mfwRoot)
                break;
            h = mfwParent(mfwParent(h));
            if (h)
                h = ((MfwWin *)(h->data))->elems;
        }
        if (!res && mfwRoot && h != mfwRoot)
            res = sigExec(mfwRoot,map,key);
    }

    if (doAlways)
        doAlways(map,(void*) res);

    return;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdSignal          |
+--------------------------------------------------------------------+

  PURPOSE : keyboard event (called by driver/PEI)

*/

#define MAX_CONSEC_KEYS 6   // Maximum number of keys which can be processed before
						        // allowing the protocol stack processing time
//xrashmic 22 Aug, 2004 MMI-SPR-32798					        
#ifdef MMI_EM_ENABLED
extern U8 screenCaptureKey;
void screen_capture(void);
#endif
void kbdSignal (char make, char key)
{
	U32 map;
	UBYTE temp ;
	USHORT numElem;

	int loopNo;
	int keyNo;


	if ((kbd_processKeyInput()== QUEUE_EVERY_KEY) ||
	     (kbd_processKeyInput()== QUEUE_N_KEYS))		
		temp = dspl_Enable(0);

	mme_backlightEvent(BL_KEY_PRESS);

	loopNo = 0;
	keyNo = 0;

	while (kbd_getMakeAndKey(&make,&key) != -1)
	{
#ifdef MMI_EM_ENABLED
              //xrashmic 22 Aug, 2004 MMI-SPR-32798
              //If screen capture is enabled the key pressed is the screen capture key, 
              //we write the LCD buffer into the FFS and and consume the key 
              //event here itself without sending it to BMI
              // Also the screen capture key will be disabled here
              if(key== screenCaptureKey && make==1)
              {
                    TRACE_EVENT("*****Capturing the screen");
                    screenCaptureKey=KCD_NONE;
                    screen_capture();
                    dspl_Enable(temp);
                    return;
              }
#endif

		still_processing_flag = 1;
		TRACE_EVENT_P2("NDH : KbdSignal - key %d, state %02x", key, make);

		if ((key >= 0) && (key <= KCD_MAX))
		{
			loopNo++;
			keyNo++;
			
			map = 1L << key;

			if (make)
			{
				map |= KEY_MAKE;
				curMap = map;
				curKey = key;
				timStart(&timLongH);
				timAuto.time = valAuto;

				/*NM, p007a*/
				if (valAuto)
					timStart(&timAutoH);
				/*NM, p007a end*/

			}
			else
			{
				map &= ~KEY_MAKE;
				curMap = map; //ES!!
				curKey = key; //ES!!
				timStop(&timLongH);
				
				if (valAuto)
					timStop(&timAutoH);
			}

			//Select when we update the display
			switch (kbd_processKeyInput())
			{
				case QUEUE_EVERY_KEY:
					sigDistribute(map,key);
					break;
					
				case PROCESS_EVERY_KEY:
					temp = dspl_Enable(0);
					sigDistribute(map,key);
					dspl_Enable(temp);
					break;
					
				case QUEUE_N_KEYS:
					if ((loopNo %(NUM_QUEUE_KEYS*2))==0) 
					{
						kbd_setDisplayUpdateNeeded(1);
						sigDistribute(map,key); 
						dspl_Enable(temp); 
						temp = dspl_Enable(0);
						kbd_setDisplayUpdateNeeded(0);
					}
					else
						sigDistribute(map,key);
					break;
			}
		}

		if (keyNo == MAX_CONSEC_KEYS)
		{
			still_processing_flag = FALSE;
			break;
		}

		still_processing_flag = FALSE;
	}

	numElem = mfw_cbuf_num_elements(mfw_kbd_kpress_buf_id);
	TRACE_EVENT_P2("NDH >>> Kbd :- There are %d elements in the buffer (id : %d)" ,
					 numElem, mfw_kbd_kpress_buf_id);
	
	if ((keyNo == MAX_CONSEC_KEYS) && (numElem > 0))
	{
		sendKeyInd(0, 0, 0); // dummy values to trigger another keypress_ind
		/*
		** This delay is required to slow down the BMI when no trace is being output in order
		** to permit the Protocol Stack & other tasks to function correctly
		*/
		vsi_t_sleep (VSI_CALLER  30); 
	}

	if ((kbd_processKeyInput()== QUEUE_EVERY_KEY) ||
	     (kbd_processKeyInput()== QUEUE_N_KEYS))		
		dspl_Enable(temp);

	return;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : toLong             |
+--------------------------------------------------------------------+

  PURPOSE : long press timeout handler

*/

static int toLong (U32 t, void *h)
{
    curMap |= KEY_LONG;
    sigDistribute(curMap,curKey);

    return 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : toAuto             |
+--------------------------------------------------------------------+

  PURPOSE : auto repeat timeout handler

*/

static int toAuto (U32 t, void *h)
{
    curMap |= KEY_AUTO;
    sigDistribute(curMap,curKey);
    timAuto.time = valRepeat;
    /* NM p007c*/
    if (valRepeat)
   		timStart(&timAutoH);
	/* NM p007c end*/
    return 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbdCommand         |
+--------------------------------------------------------------------+

  PURPOSE : handle mfw windows command

*/

static int kbdCommand (U32 cmd, void *h)
{
    switch (cmd)
    {
        case MfwCmdDelete:              /* delete me                */
            if (!h)
                return 0;
            kbdDelete(h);
            return 1;
        default:
            break;
    }

    return 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbd_putMakeAndKey    |
+--------------------------------------------------------------------+

  PURPOSE : places 'make' (key up/down) and key index into a queue

*/

int kbd_putMakeAndKey( char make, char key)
{
	keyPressDetails localKP;
	SHORT retVal;

	localKP.make = make;
	localKP.key = key;

	retVal = mfw_cbuf_put(mfw_kbd_kpress_buf_id, &localKP);

	if (retVal < 0)
		TRACE_EVENT_P1("ERROR : mfw_cbuf_put failed with error value %d", retVal);

	return (retVal);
}

/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbd_getMakeAndKey    |
+--------------------------------------------------------------------+

  PURPOSE : reads 'make' (key up/down) and key index into a queue
		Return	+ve number - keys left in buffer
				 0 - no keys left - last key press returned
				-1 - no keys and none in buffer
*/

int kbd_getMakeAndKey( char* make, char* key)
{
	keyPressDetails localKP;
	SHORT retVal;

	retVal = mfw_cbuf_get(mfw_kbd_kpress_buf_id, &localKP);

	if (retVal < 0)
	{
		*key = 0x7F;
		*make = 0;
		return (-1);
	}

	*make = !(localKP.make);
	*key =   drvGetKeyIndex(localKP.key);
	return (mfw_cbuf_num_elements(mfw_kbd_kpress_buf_id));
}

/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbd_getNumElements    |
+--------------------------------------------------------------------+

  PURPOSE : returns number of elements in queue

*/
int kbd_getNumElements(void)
{
	return ((int)mfw_cbuf_num_elements(mfw_kbd_kpress_buf_id));
}

int kbd_stillProcessingKeys(void)
{
	return (still_processing_flag);
}

int mfwKey_skipDisplay( void )
{
	if ((mfw_cbuf_num_elements(mfw_kbd_kpress_buf_id) > 2) && (still_processing_flag == 1))
		return (TRUE);
	else
		return (FALSE);
}

/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbd_displayUpdateNeeded   |
+--------------------------------------------------------------------+

  PURPOSE : returns TRUE if we need to update the display

*/
int displayUpdateNeeded; //used for output every 'n' key presses
int kbd_displayUpdateNeeded(void)
{	
	if (kbd_processKeyInput()==PROCESS_EVERY_KEY)
		return (TRUE);//Processing each key press - always update screen
	else if (displayUpdateNeeded==0)
		return (TRUE);//need to update the display (1 in 6 output) 
	else if (mfw_cbuf_num_elements(mfw_kbd_kpress_buf_id) > 1) 
		return (FALSE);//keys in queue - do not update
	else
		return (TRUE);//only 1 key up/down in queue - update display
}
/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbd_setDisplayUpdateNeeded   |
+--------------------------------------------------------------------+

  PURPOSE : sets/clears the flag indicating we need to update the display

*/
void kbd_setDisplayUpdateNeeded(int set)
{
	displayUpdateNeeded = set;
}
	
/*
+--------------------------------------------------------------------+
| PROJECT : MMI-Framework (8417)        MODULE  : MFW_KBD            |
| STATE   : code                        ROUTINE : kbd_processEveryKeyInput    |
+--------------------------------------------------------------------+

  PURPOSE : indicates if the software should process each keypad input individually 
  			(TRUE) or if it should process keypad inputs one at a time (FALSE) 

*/
int kbd_processKeyInput(void)
{
	return (QUEUE_EVERY_KEY);//We buffer multiple key inputs

}







