/**
 * @file   kpd_process_internal_msg.c
 *
 * Implementation of Keypad functions.
 * These functions implement the keypad processing for all the messages the
 * keypad task can receive.
 *
 * @author   Laurent Sollier (l-sollier@ti.com)
 * @version 0.1
 */

/*
 * History:
 *
 *   Date          Author       Modification
 *  ----------------------------------------
 *  10/10/2001     L Sollier    Create
 *
 *
 * (C) Copyright 2001 by Texas Instruments Incorporated, All Rights Reserved
 */

#include "kpd/kpd_api.h"
#include "kpd/kpd_env.h"
#include "kpd/kpd_i.h"
#include "kpd/kpd_virtual_key_table_mgt.h"
#include "kpd/kpd_physical_key_def.h"

#include "rvf/rvf_api.h"
#include "rvm/rvm_use_id_list.h"

#include <string.h>

/* External declaration */
extern T_KPD_ENV_CTRL_BLK* kpd_env_ctrl_blk;


/* Definition of the wait time in the loop */
#ifdef _WINDOWS
   #define WAIT_TIME_LOOP (50)
#else
   #define WAIT_TIME_LOOP (10)
#endif


/* This structure gathers informations about one physical key Id.
   It define if subscriber is notified for this physical key */
typedef struct {  UINT32   subscriber_mask;
               }  T_PHYSICAL_KEY_MASK;


/** Definition of a set of keys with repeat keys parameters. */
typedef struct {  UINT8                nb_notified_keys;
                  UINT16               long_press_time; /* in ms */
                  UINT16               repeat_time; /* in ms */
                  T_KPD_NOTIF_LEVEL    notif_level[KPD_NB_PHYSICAL_KEYS];
               } T_PHYSICAL_KEY_PARAM_TABLE;


/* This structure gather general informations about subscriber id */
typedef struct {  T_RV_RETURN return_path;
                  T_KPD_MODE mode;
                  T_PHYSICAL_KEY_PARAM_TABLE notified_keys;
               } T_SUBSCRIBER_INFOS;


/* Informations for all the physical keys Id.
   This variable is updated each time a client subscribe to the keypad driver,
   unsubscribe, or change notification key level.
   Warn that position of the physical key Id is implicit cause of the rule
   used to define the vpm table */
static T_PHYSICAL_KEY_MASK physical_key_mask[KPD_NB_PHYSICAL_KEYS] = {0};

/* Informations for all the subscribers */
static T_SUBSCRIBER_INFOS* subscriber_infos[KPD_MAX_SUBSCRIBER] = {0};



/**
 * @name Functions implementation
 *
 */
/*@{*/

/**
 * function: kpd_return_path_already_defined
 */
static BOOL kpd_return_path_already_defined(T_RV_RETURN return_path)
{
   UINT8 i;
   BOOL ret = FALSE;

   for (i = 0; i < KPD_MAX_SUBSCRIBER; i++)
      if ( subscriber_infos[i] != 0 )
         if (subscriber_infos[i]->return_path.callback_func != 0)
         {
            if (subscriber_infos[i]->return_path.callback_func == return_path.callback_func)
            {
               ret = TRUE;
               break;
            }
         }
         else
         {
            if (subscriber_infos[i]->return_path.addr_id == return_path.addr_id)
            {
               ret = TRUE;
               break;
            }
         }

   return ret;
}

/**
 * function: kpd_subscribe_i
 */
T_RV_RET kpd_subscribe_i(T_SUBSCRIBER_ID subscriber_id,
                         T_KPD_MODE mode,
                         T_KPD_VIRTUAL_KEY_TABLE* notified_keys,
                         T_RV_RETURN return_path)
{
   INT8 i, position;
   T_RVF_MB_STATUS mb_status;
   T_RV_RET ret;

   /* Check the validity of the key table */
   ret = kpd_check_key_table(notified_keys, mode);

   if (ret != RV_OK)
   {
      /* Remove subscriber id because id was reserved */
      kpd_remove_subscriber(subscriber_id);

      /* Send error message */
      kpd_send_status_message(KPD_SUBSCRIBE_OP, KPD_ERR_KEYS_TABLE, return_path);
   }
   else
   {
      if (kpd_return_path_already_defined(return_path) == TRUE)
      {
         /* Remove subscriber id because id was reserved */
         kpd_remove_subscriber(subscriber_id);

         /* Send error message */
         kpd_send_status_message(KPD_SUBSCRIBE_OP, KPD_ERR_RETURN_PATH_EXISTING, return_path);

         ret = RV_INVALID_PARAMETER;
      }
      else
      {      
         /* Update subscriber informations */
         /* ------------------------------ */

         /* Reserve memory for subscriber informations */
         mb_status = rvf_get_buf (kpd_env_ctrl_blk->prim_id,
                                  sizeof(T_SUBSCRIBER_INFOS),
                                  (void **) &subscriber_infos[subscriber_id]);

         if (mb_status != RVF_RED) /* Memory allocation success */
         {
            /* Initialize structure */
            memset(subscriber_infos[subscriber_id], 0, sizeof(T_SUBSCRIBER_INFOS));

            /* Fill the subscriber structure */
            subscriber_infos[subscriber_id]->mode = mode;
            subscriber_infos[subscriber_id]->return_path = return_path;
            subscriber_infos[subscriber_id]->notified_keys.nb_notified_keys = notified_keys->nb_notified_keys;
            subscriber_infos[subscriber_id]->notified_keys.long_press_time = 0;
            subscriber_infos[subscriber_id]->notified_keys.repeat_time = 0;
            for (i = 0; i < notified_keys->nb_notified_keys; i++)
            {
               /* Retrieve physical key Id from virtual key_id */
               kpd_retrieve_virtual_key_position(  notified_keys->notified_keys[i],
                                                   mode,
                                                   &position);

               subscriber_infos[subscriber_id]->notified_keys.notif_level[position] = KPD_RELEASE_NOTIF;
            }

            /* Update link (ID <-> key) */
            /* ------------------------ */
            for (i = 0; i < notified_keys->nb_notified_keys; i++)
            {
               /* Retrieve position in vpm table */
               kpd_retrieve_virtual_key_position(notified_keys->notified_keys[i],
                                                 mode,
                                                 &position);

               /* Update subscriber mask for the physical key */
               physical_key_mask[position].subscriber_mask |= (1<<subscriber_id);

            }

            /* Send success message to suscriber */
            kpd_send_status_message(KPD_SUBSCRIBE_OP, KPD_PROCESS_OK, return_path);
         }
         else
         {
            KPD_SEND_TRACE("KPD: Memory allocation error", RV_TRACE_LEVEL_ERROR);

            /* Remove subscriber id because id was reserved */
            kpd_remove_subscriber(subscriber_id);

            /* Send error message to suscriber */
            kpd_send_status_message(KPD_SUBSCRIBE_OP, KPD_ERR_INTERNAL, return_path);
         }
      }
   }

   return RV_OK;
}


/**
 * function: kpd_unsubscribe_i
 */
T_RV_RET kpd_unsubscribe_i(T_SUBSCRIBER_ID subscriber_id)
{
   UINT8 i;

   /* Delete subscriber informations */
   rvf_free_buf(subscriber_infos[subscriber_id]);
   subscriber_infos[subscriber_id] = 0;

   /* Delete link (ID <-> key) */
   for (i = 0; i < KPD_NB_PHYSICAL_KEYS; i++)
   {
      physical_key_mask[i].subscriber_mask &= ~(1<<subscriber_id);
   }

   /* If the subscriber is the keypad owner, this privilege is unset */
   if (kpd_is_owner_keypad(subscriber_id) == TRUE)
      kpd_set_keypad_mode(MN_MODE);

   return RV_OK;
}


/**
 * function: kpd_define_key_notification_i
 */
T_RV_RET kpd_define_key_notification_i(T_SUBSCRIBER_ID subscriber_id,
                                       T_KPD_VIRTUAL_KEY_TABLE* notif_key_table,
                                       T_KPD_NOTIF_LEVEL notif_level,
                                       UINT16 long_press_time,
                                       UINT16 repeat_time)
{
   T_RV_RET ret = RV_OK;
   UINT8 i;
   INT8 position;

   /* Check the validity of the key table */
   ret = kpd_check_key_table(notif_key_table, subscriber_infos[subscriber_id]->mode);

   if (ret != RV_OK)
   {
      /* Send error message */
      kpd_send_status_message(KPD_REPEAT_KEYS_OP, KPD_ERR_KEYS_TABLE, subscriber_infos[subscriber_id]->return_path);

   }
   else
   {
      /* Update subscriber informations */
      /* ------------------------------ */
      subscriber_infos[subscriber_id]->notified_keys.long_press_time = long_press_time*100;
      subscriber_infos[subscriber_id]->notified_keys.repeat_time = repeat_time*100;

      for (i = 0; i < notif_key_table->nb_notified_keys; i++)
      {
         /* Retrieve physical key Id from virtual key_id */
         kpd_retrieve_virtual_key_position(  notif_key_table->notified_keys[i],
                                             subscriber_infos[subscriber_id]->mode,
                                             &position);

         /* Check if subscriber have asked notification for this key at subscription */
         if ( physical_key_mask[position].subscriber_mask & (1<<subscriber_id) )
            subscriber_infos[subscriber_id]->notified_keys.notif_level[position] = notif_level;
      }

      /* Send success message to suscriber */
      kpd_send_status_message(KPD_REPEAT_KEYS_OP, KPD_PROCESS_OK, subscriber_infos[subscriber_id]->return_path);
   }

   return ret;
}


/**
 * function: kpd_change_mode_i
 */
T_RV_RET kpd_change_mode_i(T_SUBSCRIBER_ID subscriber_id,
                           T_KPD_VIRTUAL_KEY_TABLE* notified_keys,
                           T_KPD_MODE new_mode)
{
   UINT8 i;
   INT8 position;
   T_RV_RET ret;

   /* Check the validity of the key table */
   ret = kpd_check_key_table(notified_keys, new_mode);

   if (ret != RV_OK)
   {
      /* Send error message */
      kpd_send_status_message(KPD_CHANGE_MODE_OP, KPD_ERR_KEYS_TABLE, subscriber_infos[subscriber_id]->return_path);
   }
   else
   {
      /* Delete link (ID <-> key) for old mode*/
      for (i = 0; i < KPD_NB_PHYSICAL_KEYS; i++)
      {
         physical_key_mask[i].subscriber_mask &= ~(1<<subscriber_id);
         subscriber_infos[subscriber_id]->notified_keys.notif_level[i] = KPD_NO_NOTIF;
      }

      /* Update subscriber structure */
      subscriber_infos[subscriber_id]->mode = new_mode;
      subscriber_infos[subscriber_id]->notified_keys.nb_notified_keys = notified_keys->nb_notified_keys;
      subscriber_infos[subscriber_id]->notified_keys.long_press_time = 0;
      subscriber_infos[subscriber_id]->notified_keys.repeat_time = 0;
      for (i = 0; i < notified_keys->nb_notified_keys; i++)
      {
         /* Retrieve physical key Id from virtual key_id */
         kpd_retrieve_virtual_key_position(  notified_keys->notified_keys[i],
                                             new_mode,
                                             &position);

         subscriber_infos[subscriber_id]->notified_keys.notif_level[position] = KPD_RELEASE_NOTIF;
 
         /* Update link (ID <-> key) for new mode */
         physical_key_mask[position].subscriber_mask |= (1<<subscriber_id);
      }

      /* If the subscriber is the keypad owner, this privilege is unset */
      if (kpd_is_owner_keypad(subscriber_id) == TRUE)
         kpd_set_keypad_mode(MN_MODE);

      /* Send success message to suscriber */
      kpd_send_status_message(KPD_CHANGE_MODE_OP, KPD_PROCESS_OK, subscriber_infos[subscriber_id]->return_path);
   }

   return RV_OK;
}

/**
 * function: kpd_own_keypad_i
 */
T_RV_RET kpd_own_keypad_i(T_SUBSCRIBER_ID subscriber_id,
                          BOOL is_keypad_owner,
                          T_KPD_VIRTUAL_KEY_TABLE* keys_owner)
{
   INT8 position;
   UINT8 i;

   if (is_keypad_owner == FALSE)
   {
      /* Check if subscriber Id own the keypad */
      if (kpd_is_owner_keypad(subscriber_id))
      {
         kpd_set_keypad_mode(MN_MODE);

         /* Send success message to suscriber */
         kpd_send_status_message(KPD_OWN_KEYPAD_OP, KPD_PROCESS_OK, subscriber_infos[subscriber_id]->return_path);

      }
      else
         kpd_send_status_message(KPD_OWN_KEYPAD_OP, KPD_ERR_ID_OWNER_KEYPAD, subscriber_infos[subscriber_id]->return_path);
   }
   else
   {
      /* Check if keypad driver is already in single-notified mode */
      if (kpd_get_keypad_mode() == SN_MODE)
      {
         /* Send error message to suscriber */
         kpd_send_status_message(KPD_OWN_KEYPAD_OP, KPD_ERR_SN_MODE, subscriber_infos[subscriber_id]->return_path);
      }
      else
      {
         /* Check if all the keys defined in keys_owner are notified to the subsciber */
         for (i = 0; i < keys_owner->nb_notified_keys; i++)
         {
            /* Retrieve physical key Id from virtual key_id */
            kpd_retrieve_virtual_key_position(  keys_owner->notified_keys[i],
                                                subscriber_infos[subscriber_id]->mode,
                                                &position);

            if ( subscriber_infos[subscriber_id]->notified_keys.notif_level == KPD_NO_NOTIF )
            {
               /* Send error message to suscriber */
               kpd_send_status_message(KPD_OWN_KEYPAD_OP, KPD_ERR_KEYS_TABLE, subscriber_infos[subscriber_id]->return_path);
               return RV_INTERNAL_ERR;
            }
         }

         /* Set keypad driver in single-notified mode */
         kpd_set_keypad_mode(SN_MODE);

         /* Set owner keypad Id */
         kpd_set_owner_keypad_id(subscriber_id);

         /* Set list of keys used by the keypad owner. Thsi list is is a sub-list
            of the keys defined at subscription. For these keys, the keypad owner will
            be the only notified. */
         kpd_set_keys_in_sn_mode(keys_owner, subscriber_infos[subscriber_id]->mode);

         /* Send success message to suscriber */
         kpd_send_status_message(KPD_OWN_KEYPAD_OP, KPD_PROCESS_OK, subscriber_infos[subscriber_id]->return_path);
      }
   }

   return RV_OK;
}

/**
 * function: kpd_set_key_config_i
 */
T_RV_RET kpd_set_key_config_i(T_SUBSCRIBER_ID subscriber_id,
                              T_KPD_VIRTUAL_KEY_TABLE* reference_keys,
                              T_KPD_VIRTUAL_KEY_TABLE* new_keys)
{
#ifdef KPD_MODE_CONFIG
   UINT8 i;

   /* Check if some subscriber use the configurable mode */
   for (i = 0; i < KPD_MAX_SUBSCRIBER; i++)
   {
      if ( (subscriber_infos[i] != 0) && (subscriber_infos[i]->mode == KPD_MODE_CONFIG) )
      {
         /* Send error message to suscriber */
         kpd_send_status_message(KPD_SET_CONFIG_MODE_OP,
                                 KPD_ERR_CONFIG_MODE_USED,
                                 subscriber_infos[subscriber_id]->return_path);
         return RV_OK;
      }
   }

   /* Set keys in configurable mode */
   kpd_define_new_config(reference_keys, new_keys);

   /* Send success message to suscriber */
   kpd_send_status_message(KPD_SET_CONFIG_MODE_OP,
                           KPD_PROCESS_OK,
                           subscriber_infos[subscriber_id]->return_path);

#endif

   return RV_OK;
}


/**
 * function: kpd_process_key_pressed_i
 */
void kpd_process_key_pressed_i(T_KPD_PHYSICAL_KEY_ID physical_key_pressed_id)
{
   UINT8 i;
   UINT32 loop_counter = 0;
   UINT16 counter[KPD_MAX_SUBSCRIBER] = {0};
   T_KPD_PHYSICAL_KEY_ID key_id = physical_key_pressed_id;

   KPD_SEND_TRACE_PARAM("KPD: kpd_process_key_pressed_i ", key_id, RV_TRACE_LEVEL_DEBUG_LOW);

   /* Notify subscribers of the key pressed */
   for (i = 0; i < KPD_MAX_SUBSCRIBER; i++) /* To do : Loop on the real number of subscribers */
   { /* To do : Test on the physical_key_mask (to ensure subscriber is subscribed for this key) */
      if ( (subscriber_infos[i]!=0) && (subscriber_infos[i]->notified_keys.notif_level[key_id] & KPD_FIRST_PRESS_NOTIF) )
      {
         kpd_send_key_event_message(key_id, KPD_KEY_PRESSED,
                                    KPD_FIRST_PRESS, subscriber_infos[i]->mode,
                                    subscriber_infos[i]->return_path);
      }
   }

   /* If key pressed is the PWR key, the message "Released key" is immediately sent */
   if (key_id != KPD_SHORT_PRESS_PWR_KEY)
   {
      /* Loop infinitely until key is released */
      do
      {
         rvf_delay(RVF_MS_TO_TICKS(WAIT_TIME_LOOP));
         physical_key_pressed_id = kpd_scan_keypad();
         loop_counter++;

         /* Send message for repeat key */
         for (i = 0; i < KPD_MAX_SUBSCRIBER; i++)
         {
            if ( (subscriber_infos[i]!=0) && (subscriber_infos[i]->notified_keys.notif_level[key_id] & (KPD_LONG_PRESS_NOTIF|KPD_INFINITE_REPEAT_NOTIF)) )
            {
               if ((counter[i] == 0) && (WAIT_TIME_LOOP*loop_counter >= subscriber_infos[i]->notified_keys.long_press_time) )
               {
                  kpd_send_key_event_message(key_id, KPD_KEY_PRESSED,
                                             KPD_LONG_PRESS, subscriber_infos[i]->mode,
                                             subscriber_infos[i]->return_path);
                  counter[i] ++;
               }
               else if (subscriber_infos[i]->notified_keys.notif_level[key_id] & KPD_INFINITE_REPEAT_NOTIF)
               {
                  if (WAIT_TIME_LOOP*loop_counter >= (UINT32)((counter[i]*subscriber_infos[i]->notified_keys.repeat_time + subscriber_infos[i]->notified_keys.long_press_time)) )
                  {
                     kpd_send_key_event_message(key_id, KPD_KEY_PRESSED,
                                                KPD_REPEAT_PRESS, subscriber_infos[i]->mode,
                                                subscriber_infos[i]->return_path);
                     counter[i] ++;
                  }
               }
            }
         }

      } while (physical_key_pressed_id != KPD_PKEY_NULL);
   }

   /* Notify subscribers of the key released */
   for (i = 0; i < KPD_MAX_SUBSCRIBER; i++)
   {
      if ( (subscriber_infos[i]!=0) && (subscriber_infos[i]->notified_keys.notif_level[key_id] & KPD_RELEASE_NOTIF) )
      {
         kpd_send_key_event_message(key_id, KPD_KEY_RELEASED,
                                    KPD_INSIGNIFICANT_VALUE, subscriber_infos[i]->mode,
                                    subscriber_infos[i]->return_path);
      }
   }

   /* On board,this function unmask keypad interrupt
      On Riviera tool, this function is empty        */
   if (key_id != KPD_SHORT_PRESS_PWR_KEY)
   {
#if (CHIPSET == 12)
      kpd_software_reset();
      kpd_init_ctrl_reg(1, HARDWARE_DECODING, KPD_CLK_DIV32,
                        KPD_DETECTION_DISABLED, KPD_DETECTION_DISABLED,
                        KPD_DETECTION_DISABLED, KPD_DETECTION_DISABLED);
#endif

      kpd_acknowledge_key_pressed();
   }
}


/**
 * function: kpd_process_key_pressed_sn_mode_i
 */
void kpd_process_key_pressed_sn_mode_i(T_KPD_PHYSICAL_KEY_ID physical_key_pressed_id)
{
   T_SUBSCRIBER_ID owner_keypad_id = kpd_get_owner_keypad_id();
   T_KPD_PHYSICAL_KEY_ID key_id = physical_key_pressed_id;
   UINT32 loop_counter = 0;
   UINT16 counter = 0;

   if ( subscriber_infos[owner_keypad_id]->notified_keys.notif_level[key_id] & KPD_FIRST_PRESS_NOTIF )
   {
      /* Notify subscribers of the key pressed */
       kpd_send_key_event_message(key_id, KPD_KEY_PRESSED,
                                  KPD_FIRST_PRESS, subscriber_infos[owner_keypad_id]->mode,
                                  subscriber_infos[owner_keypad_id]->return_path);
   }

   /* If key pressed is the PWR key, the message "Released key" is immediately sent */
   if (key_id != KPD_SHORT_PRESS_PWR_KEY)
   {
      /* Loop infinitely until key is released */
      do
      {
         rvf_delay(RVF_MS_TO_TICKS(WAIT_TIME_LOOP));
         physical_key_pressed_id = kpd_scan_keypad();
         loop_counter++;

         /* Send message for repeat key */
         if (subscriber_infos[owner_keypad_id]->notified_keys.notif_level[key_id] & (KPD_LONG_PRESS_NOTIF|KPD_INFINITE_REPEAT_NOTIF))
         {
            if ((counter == 0) && (WAIT_TIME_LOOP*loop_counter >= subscriber_infos[owner_keypad_id]->notified_keys.long_press_time) )
            {
               kpd_send_key_event_message(key_id, KPD_KEY_PRESSED,
                                          KPD_LONG_PRESS, subscriber_infos[owner_keypad_id]->mode,
                                          subscriber_infos[owner_keypad_id]->return_path);
               counter ++;
            }
            else if (subscriber_infos[owner_keypad_id]->notified_keys.notif_level[key_id] & KPD_INFINITE_REPEAT_NOTIF)
            {
               if (WAIT_TIME_LOOP*loop_counter >= (UINT32)((counter*subscriber_infos[owner_keypad_id]->notified_keys.repeat_time + subscriber_infos[owner_keypad_id]->notified_keys.long_press_time)) )
               {
                  kpd_send_key_event_message(key_id, KPD_KEY_PRESSED,
                                             KPD_REPEAT_PRESS, subscriber_infos[owner_keypad_id]->mode,
                                             subscriber_infos[owner_keypad_id]->return_path);
                  counter ++;
               }
            }
         }

      } while (physical_key_pressed_id != KPD_PKEY_NULL);
   }

   if ( subscriber_infos[owner_keypad_id]->notified_keys.notif_level[key_id] & KPD_RELEASE_NOTIF )
   {
      /* Notify subscribers of the key released */
      kpd_send_key_event_message(key_id, KPD_KEY_RELEASED,
                                 KPD_INSIGNIFICANT_VALUE, subscriber_infos[owner_keypad_id]->mode,
                                 subscriber_infos[owner_keypad_id]->return_path);
   }

   /* On board,this function unmask keypad interrupt
      On Riviera tool, this function authorize to send new messages to keypad task */
   if (key_id != KPD_SHORT_PRESS_PWR_KEY)
   {
#if (CHIPSET == 12)
      kpd_software_reset();
      kpd_init_ctrl_reg(1, HARDWARE_DECODING, KPD_CLK_DIV32,
                        KPD_DETECTION_DISABLED, KPD_DETECTION_DISABLED,
                        KPD_DETECTION_DISABLED, KPD_DETECTION_DISABLED);
#endif
      kpd_acknowledge_key_pressed();
   }
}

/**
 * function: kpd_wait_for_key_release
 */
void kpd_wait_for_key_release(void)
{
   T_KPD_PHYSICAL_KEY_ID key_id;;

   do
   {
      rvf_delay(RVF_MS_TO_TICKS(WAIT_TIME_LOOP));
      key_id = kpd_scan_keypad();

   } while (key_id != KPD_PKEY_NULL);

   kpd_acknowledge_key_pressed();
}

/*@}*/
