/** @file */
/* ----------------------------------------------------------------------------

Copyright (C) Siemens AG 1994-2001 * ALL RIGHTS RESERVED

  This software is protected by the inclusion of the above copyright
  notice. This software may not be provided or otherwise made available
  to, or used by, any other person. No title to or ownership of the
  software is  hereby  transferred.
  The information contained in this document is considered the
  CONFIDENTIAL and PROPRIETARY information of Siemens AG and may
  not be disclosed or discussed with anyone who is not employed by
  Siemens AG, unless the individual / company
  (i) has an express need to know such information, and
  (ii) disclosure of information is subject to the terms of a duly
  executed Confidentiality and Non-Disclosure Agreement between
  Siemens AG and the individual / company.

Created using template RCSfile: templ_c.c,v, Revision: 1.9, genxfile V1.11

.AUTHOR Jens Scharnow (KLF-5918)

.FILENAME simumtt.c




.CSVERSION 1.1

.VERSION 01.01.01

.DATE 2003-07-15

.SHORT_DESCR   Socket-triggered handler for the communication between simulation and MTT

.SW_COMPONENT  PCSimu

.SW_TYPE       C-Module

.EXIT_CODES

.CHANGE_CONTROL 
Version  Date        Changed by
         Reason of change
--------------------------------------------------------------------------------
01.00.00 2003-04-17 Jens Scharnow (KLF-5918) (KLF_EXT)
         Initial Version
01.00.01 2003-05-22 Walter Stroell (VIE)
         mem.h additionally included
01.01.01 2003-07-15 Jens Scharnow (KLF)
         added changes for registry support and improved command parser
------------------------------------------------------------------------------*/

/**
  \file   simumtt.c
  \brief  

  This file implements the handler for the communication between the MTT (mobile test tool) and the PC simulation.
  The handler works as follows:
  The communication between MTT and the simulation is socket based. If a message is sent by MTT to the simulation, the
  mtt_tcp_notify message will be called. A MTT command (press key, release key or fetch display) will then be put into 
  an internal command queue. The actual execution of the command has to be performed from an GBS process to ensure that
  the keyboard and display driver are in a proper state. The MTT handler sends therefore a message to a GBS process 
  (currently the calendar process) to trigger the execution. This process will then again trigger the MTT handler to 
  commit the execution. The MTT handler will then start executing the commands from it's command queue.
*/


/* -----------------------------------------------------------------------------
   INCLUDES
------------------------------------------------------------------------------*/
#include <global.h>

#if MOBSIM > 0

#include "mem.h"
#include "procid.h"
#include "glob_msg.h"
#include <stdio.h>
#include <windows.h>
#include "gbs.h"
#include "winpumsg.h"
#include "simumttp.h"
#include "kbddrv.h"

// #define DBG_DEBUGGING_ACTIVE ON
#include "debug.h"

/* -----------------------------------------------------------------------------
    DEFINES
------------------------------------------------------------------------------*/

// closing of socket differs from Win32 to unix, so map these
#if !defined( _WIN32) && !defined( __WIN32__)
#define closesocket close
#endif



#if DBG_DEBUGGING_ACTIVE
// use this macro to debug-print the current state of the command queue
#define DBG_QCOUNT()    DBG_TRACE("Queue: size %i (%i,%i)", mg_nQueueCount, mg_nQueueHead, mg_nQueueTail);SIM_MTT_PrintQueue()
#else
#define DBG_QCOUNT()  
#endif


#ifndef OFFICE_HANDLER_PID
#define MTT_GBS_PROCESS  VCAL_HANDLER_PID
#else
#define MTT_GBS_PROCESS  OFFICE_HANDLER_PID
#endif

/* -----------------------------------------------------------------------------
    TYPES
------------------------------------------------------------------------------*/

typedef enum MTT_commandTypeEn
{
    MTT_KEY_PRESS,
    MTT_KEY_RELEASE,
    MTT_FETCH_DISPLAY
} MTT_commandType;          ///< type of a command of the command queue


typedef struct
{
    MTT_commandType  enCommandType;
    unsigned char    ucCommandData;
} MTT_command;              ///< command of the command queue


/* -----------------------------------------------------------------------------
    FILE LOCAL CONSTANTS
------------------------------------------------------------------------------*/

#define MTT_NO_COMMAND_DATA  0

#define MTT_COMMAND_QUEUE_SIZE 30

const char* MTT_SIMU_DELAY_STRINGS[MTT_NUM_DELAY_TIMES]=
{
    "MTT_SIMU_DELAY_PRESS",
    "MTT_SIMU_DELAY_RELEASE",
    "MTT_SIMU_DELAY_FETCH_DISPLAY"
};
/* -----------------------------------------------------------------------------
    FILE LOCAL VARIABLES
------------------------------------------------------------------------------*/

XSTATIC MTT_command mg_MTTCommandQueue[MTT_COMMAND_QUEUE_SIZE]; ///< the command queue
XSTATIC unsigned int mg_nQueueHead; ///< first position in queue
XSTATIC unsigned int mg_nQueueTail; ///< first free position in queue
XSTATIC unsigned int mg_nQueueCount; ///< number of elements in queue

XSTATIC int mg_SIM_MTTServerSocket;
XSTATIC int mg_SIM_MTTClientSocket;
XSTATIC Timer mg_MTTCommandTimer;

XSTATIC int mg_nSIM_MTTPort;
XSTATIC int mg_CommandDelayTimes[MTT_NUM_DELAY_TIMES];
/* -----------------------------------------------------------------------------
    FILE LOCAL PROTOTYPES
------------------------------------------------------------------------------*/
extern HWND DispGetWinHandle(void);

// functions which are called on an asynchronous tcp message
XSTATIC int mtt_tcp_accept(int nSocket);
XSTATIC int mtt_tcp_connect(int nSocket);
XSTATIC int mtt_tcp_read(int nSocket);

// functions for command handling
XSTATIC void mtt_handleCommand(MTT_command tCommand);
XSTATIC bool mtt_queueCommand(MTT_command tCommand);

// functions for queue manipulation
XSTATIC void mtt_enQueue(MTT_command tCommand);
XSTATIC void mtt_deQueue(MTT_command* tCommand);

// timer handling
XSTATIC void mtt_startTimerIfNeccessary( void );
XSTATIC void mtt_timerHandler(Timer* pTimer);

// transforming of command formats
XSTATIC bool mtt_transformToSiemensCode(const char cCommandChar, char *cSiemensChar);

// debugging
XSTATIC void SIM_MTT_PrintQueue( void );

/* -----------------------------------------------------------------------------
    IMPLEMENTATION SECTION EXPORTED FUNCTIONS
------------------------------------------------------------------------------*/

/**
 *  Function which has to be called when a message for the Simu-MTT handler comes in via a socket
 * 
 *  \param iMessage message id
 *  \param wParam   usually the socket
 *  \param lParam   usually the message type
 * \return 1 if message can be handled succesfully, else 0
 */
int mtt_tcp_notify(UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    int nResultCode;

    DBG_TRACE("mtt_tcp_notify called");
    if (iMessage != MTT_TCP_NOTIFY_MSG)
    {
        nResultCode = 0;
    }
    else
    {
        nResultCode = 1;

        switch (lParam)
        {
        case FD_ACCEPT:
            DBG_TRACE("accept");
            nResultCode=mtt_tcp_accept((int)wParam);
            break;
        case FD_CONNECT:
            DBG_TRACE("connect");
            nResultCode=mtt_tcp_connect((int)wParam);
            break;
        case FD_READ:
            DBG_TRACE("read");
            nResultCode=mtt_tcp_read((int)wParam);
            break;
        default:
            DBG_TRACE("unsupported lparam: %i",lParam);
        }
    }

    return nResultCode;

}


/**
 * activate TCP/IP server for MTT
 * 
 *  \param nPortNo  the number of the port to be opened
 */
void mtt_tcp_activate(int nPortNo, int nDelayTimes[MTT_NUM_DELAY_TIMES])
{
    struct sockaddr_in fsin;
    unsigned int alen = sizeof(fsin);
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    int i;

    // store the port number
    mg_nSIM_MTTPort=nPortNo;

    // store the delay times
    for (i=0; i < MTT_NUM_DELAY_TIMES; i++)
    {
        mg_CommandDelayTimes[i]=nDelayTimes[i];
    }

    // WinSock has to be activated with a given Version number
    wVersionRequested = MAKEWORD(2, 2);

    // Start WinSock API
    err = WSAStartup( wVersionRequested, &wsaData);
    if (err != 0)
    {
        /* FIXME: Tell the user that we could not find a usable */
        /* WinSock DLL.                                         */
        return;
    }

    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 ||
            HIBYTE(wsaData.wVersion) != 2)
    {
        /* FIXME: Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        WSACleanup();
    }

    _fmemset(&fsin, 0, alen);

    fsin.sin_family = AF_INET;
    fsin.sin_addr.s_addr = INADDR_ANY;
    fsin.sin_port = htons(nPortNo);

    mg_SIM_MTTServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    /* non blocking tcp */
    err = WSAAsyncSelect (mg_SIM_MTTServerSocket, DispGetWinHandle(), MTT_TCP_NOTIFY_MSG, FD_ACCEPT);

    if (bind(mg_SIM_MTTServerSocket, (struct sockaddr *)&fsin, alen) < 0)
    {
        return;
    }

    if (listen(mg_SIM_MTTServerSocket, 1) < 0)
    {
        return;
    }
}


/**
 * function which has to be called to initiate the processing of the internal MTT command queue
 * 
 */
void mtt_trigger_execution( void )
{
    DBG_TRACE("Starting delayed timer");
    mtt_startTimerIfNeccessary();
}

/* -----------------------------------------------------------------------------
    IMPLEMENTATION SECTION LOCAL FUNCTIONS
------------------------------------------------------------------------------*/


/**
 *  accept incoming TCP connection from MTT
 * 
 *  \param nSocket  number of the socket
 *  \return 1 if message can be handled succesfully, else 0
 */
XSTATIC int mtt_tcp_accept(int nSocket)
{
    struct sockaddr_in fsin;
    unsigned int alen = sizeof(fsin);
    int nErrc;

    DBG_TRACE("mtt_tcp_accept: called with socket %i", nSocket);

    _fmemset(&fsin, 0, alen);

    fsin.sin_family = AF_INET;
    fsin.sin_addr.s_addr = INADDR_ANY;
    fsin.sin_port = htons(mg_nSIM_MTTPort);

    if (mg_SIM_MTTClientSocket != INVALID_SOCKET)
    {
        (void)closesocket(mg_SIM_MTTClientSocket);
        mg_SIM_MTTClientSocket = INVALID_SOCKET;
    }

    if ((mg_SIM_MTTClientSocket = accept(nSocket, (struct sockaddr *)&fsin, &alen)) > 0)
    {
        /* non blocking tcp */
        nErrc = WSAAsyncSelect(mg_SIM_MTTClientSocket, DispGetWinHandle(), MTT_TCP_NOTIFY_MSG, FD_READ);
    }
    return nErrc;
}



/**
 *   function is called, when MTT client wants to connect
 * 
 *  \param nSocket  number of the socket
 *  \return 1 if message can be handled succesfully, else 0
 */
int mtt_tcp_connect(int nSocket)
{
    DBG_TRACE("mtt_tcp_connect: called with socket %i", nSocket);
    return 1;
}

/**
 *  function is called, when MTT client wants to read
 * 
 *  \param nSocket  number of the socket
 *  \return 1 if message can be handled succesfully, else 0
 */
int mtt_tcp_read(int nSocket)
{
    unsigned char cBuf[MTT_MAX_PACKET_SIZE];
    unsigned long nMsgLen;
    unsigned long nCurrentPosInCommand;
    char cCommandKey;
    MTT_command tCmd;
    BOOL bCmdResult;
    int nRetval=0;

    DBG_TRACE("mtt_tcp_read: called with socket %i", nSocket);
    nMsgLen = recv(nSocket, cBuf, MTT_MAX_PACKET_SIZE, 0);
    DBG_TRACE("Message length: %i", nMsgLen);
    cBuf[nMsgLen]=0;
    DBG_TRACE("Message content: >%s<", cBuf);

    nCurrentPosInCommand=0;

    // now parse the incoming command string

    while (nCurrentPosInCommand < nMsgLen)
    {
        // the first character is checked for an "fetch-screen" command
        cCommandKey=cBuf[nCurrentPosInCommand];
        nCurrentPosInCommand++; // first char parsed
        if (cCommandKey == SIM_MTT_FETCH_SCREEN_CHAR)
        {
            DBG_TRACE("received fetch screen command");
            tCmd.enCommandType=MTT_FETCH_DISPLAY;
            tCmd.ucCommandData=MTT_NO_COMMAND_DATA;
        }
        else
        {
            // otherwise a key command is provided.
            // key command are like "kc", 
            // where 'k' is a char describing the key and 'c' describes the command, i.e. press or release

            // check whether a second char is available
            if (nCurrentPosInCommand >= nMsgLen)
            {
                // error: command string already fully parsed
                DBG_TRACE("Insufficient command");
                nRetval=0;
                break; // leave parse loop
            }

            DBG_TRACE("received key command");

            // check second char, whether it contains press or relase command
            if (toupper(cBuf[nCurrentPosInCommand]) == SIM_MTT_PRESS_KEY_CHAR)
            {
                tCmd.enCommandType=MTT_KEY_PRESS;
            }
            else if (toupper(cBuf[nCurrentPosInCommand]) == SIM_MTT_RELEASE_KEY_CHAR)
            {
                tCmd.enCommandType=MTT_KEY_RELEASE;
            } 
            else
            {
                // invalid command
                DBG_TRACE("Invalid key command: >%c<", cBuf[1]);
                nRetval=0;
                break; // leave parse loop
            }

            nCurrentPosInCommand++;  // second char parsed
            
        
            // set the first key as command data and convert it to internal format
            if (mtt_transformToSiemensCode(cCommandKey, &tCmd.ucCommandData)!= TRUE)
            {
                // conversion failed, so an invalid command was given
                return 0;
            }
        } // cCommandKey == SIM_MTT_FETCH_SCREEN_CHAR
    
        // queue the command
        bCmdResult=mtt_queueCommand(tCmd);
        if (bCmdResult)
        {
            nRetval=1;
        }
        else
        {
            nRetval=0; // failure. Leave the loop
            break; 
        }
    }

    DBG_TRACE("command string parsed");

    return nRetval;
} // mtt_tcp_read


/**
 *  This function will queue the given command. If currently no command is in the internal queue, execution of commands
 *  will be triggered
 *  \param tCommand the command which will be queued
 *
 *  \return FALSE if command cannot be queued, else TRUE
 */
XSTATIC bool mtt_queueCommand(MTT_command tCommand)
{
    bool bEmptyQueue;
    // check whether queue is full
    if (mg_nQueueCount == MTT_COMMAND_QUEUE_SIZE)
    {
        return FALSE;
    }

    bEmptyQueue=(mg_nQueueCount == 0);
   
    DBG_TRACE("enqueue command");
    mtt_enQueue(tCommand);

   if (bEmptyQueue)
   {
       // queue has been empty before enqueueing the new command. Post message to MMI to initiate handling of commands
       GbsPostMessage(MTT_GBS_PROCESS, M_MTT_TRIGGER, 0);
    }

    return TRUE;
}

/**
 *  Function will physically queue the command in the internal command queue
 * 
 *  \param tCommand the command to be queued
 */
XSTATIC void mtt_enQueue(MTT_command tCommand)
{
    unsigned int nNewPos;
    DBG_TRACE("mtt_enqueue called");
    if (mg_nQueueCount==MTT_COMMAND_QUEUE_SIZE)
    {
        // precondition violated
        return;
    }
    nNewPos=mg_nQueueTail;
    if (nNewPos == MTT_COMMAND_QUEUE_SIZE)
    {
        nNewPos = 0;
    }
    mg_MTTCommandQueue[nNewPos]=tCommand;
    mg_nQueueTail=nNewPos+1;
    mg_nQueueCount++;
    DBG_QCOUNT();
}


/**
 *  gets the top entry from the queue
 * 
 *  \pre   queue is not empty
 *  \param tCommand the command (return value)
 */
XSTATIC void mtt_deQueue(MTT_command* tCommand)
{
    DBG_TRACE("mtt_deQueue called");
    (*tCommand)=mg_MTTCommandQueue[mg_nQueueHead];
    mg_nQueueHead=(mg_nQueueHead+1)%MTT_COMMAND_QUEUE_SIZE;
    mg_nQueueCount--;
    DBG_QCOUNT();
}


/**
 *  Function handles one command directly
 * 
 *  \param tCommand the command to be handled
 */
XSTATIC void mtt_handleCommand(MTT_command tCommand)
{
    int nSentChars=0;
    // handle key press
    if (tCommand.enCommandType==MTT_KEY_PRESS)
    {
        DBG_TRACE("cmd: MTT_KEY_PRESS");

        KeyboardSimulatePressedKey(tCommand.ucCommandData);
    } // MTT_KEY_PRESS
    else if (tCommand.enCommandType==MTT_KEY_RELEASE)
    {
        DBG_TRACE("cmd: MTT_KEY_RELEASE");

        KeyboardSimulateReleasedKey(tCommand.ucCommandData);

    }//MTT_KEY_RELEASE
    else if (tCommand.enCommandType==MTT_FETCH_DISPLAY)
    {
        unsigned char cBuf[MTT_MAX_PACKET_SIZE];
        DBG_TRACE("cmd: MTT_FETCH_DISPLAY");
        DBG_TRACE("Buffer address: %p", DispBuff);
        memcpy (&cBuf, DispBuff, MTT_MAX_PACKET_SIZE);
        nSentChars=send(mg_SIM_MTTClientSocket, cBuf, MTT_MAX_PACKET_SIZE, 0);
    } //MTT_FETCH_DISPLAY
    else 
    {
        // invalid command
        DBG_TRACE("Invalid command in Queue!");
        return;
    }
    DBG_TRACE("Delivered %i chars", nSentChars);
    mtt_startTimerIfNeccessary();
} // mtt_handleCommand



/**
 *  function starts a timer, if the queue is not empty. After expiration of the timer, the next command from the 
 *  command queue is handled. The duration of the timer depends on the command type. 
 *
 *  \see mg_CommandDelayTimes
 */
XSTATIC void mtt_startTimerIfNeccessary( void )
{
    if (mg_nQueueCount > 0)
    {
        // if queue is empty, there's no need to start a timer
        int nTimeoutMillis=mg_CommandDelayTimes[mg_MTTCommandQueue[mg_nQueueHead].enCommandType];
        DBG_TRACE("Setting timer(%d)",nTimeoutMillis);
        GbsStartTimerMilliSecond(&mg_MTTCommandTimer, nTimeoutMillis, &mtt_timerHandler);
    }
}// mtt_startTimerIfNeccessary


/**
 *  This handle is called after the command timer expires
 * 
 *  \see mtt_startTimerIfNeccessary
 */
XSTATIC void mtt_timerHandler(Timer* pTimer)
{
    MTT_command tCommand;
    DBG_QCOUNT();
    
    if (mg_nQueueCount == 0)
    {
        DBG_TRACE("Timeout occured although queue is epmty!");
        return;
    }
    mtt_deQueue(&tCommand);
    GbsLockScheduler();
    mtt_handleCommand(tCommand);
    GbsUnlockScheduler();
}//mtt_timerHandler


/**
 *  Function converts an external key description (as used in the AT command AT+CKPD) into an internal format
 * 
 *  \param cCommandChar the source command key in AT+CKPD format
 *  \param cSiemensChar the destination command key in internal format
 *  \return True, if the transformation work, i.e. the given command key is valid
 */
XSTATIC bool mtt_transformToSiemensCode(const char cCommandChar, char *cSiemensChar)
{
    int i;
    BOOL bKeyFound=FALSE;
    
    // iterate through list up to the place where the key is found
    for (i = 0;  (mg_SIM_MTTKeymap[i][0] != SIM_MTT_COMMAND_NOT_FOUND); i++)
    {
        if (toupper(cCommandChar) == mg_SIM_MTTKeymap[i][0])
        {
            bKeyFound=TRUE;
            *cSiemensChar = (unsigned char)mg_SIM_MTTKeymap[i][1];
            break;
        }
    }
    return bKeyFound;
}

#if DBG_DEBUGGING_ACTIVE

/**
 *  This function can be used to print the content of the command queue
 * 
 */
XSTATIC void SIM_MTT_PrintQueue( void )
{
    unsigned int i;
    unsigned int nCurPos;
    char szDebBuf[200];
    
    MTT_command tCmd;
    
    (void)sprintf(szDebBuf,"Queue: [");
    nCurPos=mg_nQueueHead;
    for (i=0; i <mg_nQueueCount; i++)
    {
        char szCmdBuf[10];

        (void)strcat(szDebBuf, "(");
        tCmd=mg_MTTCommandQueue[nCurPos];        
        switch (tCmd.enCommandType)
        {            
        case MTT_KEY_PRESS:
            (void)sprintf(szCmdBuf,"p>%i<", tCmd.ucCommandData);
            break;
        case MTT_KEY_RELEASE:
            (void)sprintf(szCmdBuf,"r>%i<", tCmd.ucCommandData);
            break;
        case MTT_FETCH_DISPLAY:
            (void)sprintf(szCmdBuf,"fetch", tCmd.ucCommandData);
            break;
        default:
            (void)sprintf(szCmdBuf,"unkn", tCmd.ucCommandData);
        }
        (void)strcat(szDebBuf, szCmdBuf);
        (void)strcat(szDebBuf, ")");
        if (i<(mg_nQueueCount-1))
        {
            (void)strcat(szDebBuf, ",");
        }
        
        if (nCurPos == MTT_COMMAND_QUEUE_SIZE)
        {
            nCurPos = 0;
        }
        else
        {
            nCurPos++;
        }        
    }
    (void)strcat(szDebBuf, "]");
    DBG_TRACE("%s", szDebBuf);
}

#endif //DBG_DEBUGGING_ACTIVE
/* -----------------------------------------------------------------------------
    END OF FILE simumtt.c
------------------------------------------------------------------------------*/

#endif
