/*!
  @file           RTEThread_ConsoleCommandWorker.cpp
  @author         StefanP
  @special area   Kernel Runtime Environment
  @brief          Console Worker Threads
  @see            

\if EMIT_LICENCE
    ========== licence begin  GPL
    Copyright (c) 2002-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end

\endif
*/




/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#include    "heo07.h"

#include    "RunTime/RTE_CompilerFeatures.h"
#include    "SAPDBCommon/Tracing/SAPDBTrace_Topic.hpp"
#include    "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include    "RunTime/RTE_MessageList.hpp"
#include    "RunTime/RTE_Message.hpp"
#include    "RunTime/RTE_Console_Thread_Messages.hpp"
#include    "RunTime/RTE_ConsoleCommon.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleCommandWorker.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleCommandQueue.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleConnectionList.h"
#include    "RunTime/Threading/RTEThread_ConsoleStandardWorker.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleWorkerBase.hpp"
#include    "RunTime/Threading/RTEThread_ConsoleRequest.h"

#if defined(_FASTCAP)
# include "fastcap.h"   /* nocheck */
#endif

extern SAPDBTrace_Topic Console_Trace;

/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/
#define CONSOLE_WORKER_STACK_SIZE (512*1024)

/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/



/*===========================================================================*
 *  LOCAL CLASSES, STRUCTURES, TYPES, UNIONS ...                             *
 *===========================================================================*/



/*===========================================================================*
 *  STATIC/INLINE FUNCTION PROTOTYPES                                        *
 *===========================================================================*/

  /*!
     @description    Main routine of console worker thread


                  Reads the requests from the command queue and hand it
                  over to the request processing

   */


//static void *ConsoleWorker ( void *arg );

  /*!
     @description    Processing of the Xcons requests


                  Determines the request type and calls special routines for the 
                  request processing

   */


static  void  ExecuteConsoleCommand             (RTEThread_ConsoleRequest const &       requestData,
                                                 SAPDB_UInt4 const                      threadIndex);

  /*!
     @description    Processing an open request


                  Opens a connection

   */


static  SAPDB_UInt4   ConsoleOpenConnection     (RTEThread_ConsoleRequest const &       requestData,
                                                 SAPDB_UInt4 const                      threadIndex,
                                                 SAPDBErr_MessageList &                 messageList);
  /*!
     @description    Processing an release request


                  Releases a connection

   */


static  SAPDB_UInt4   ConsoleReleaseConnection  (RTEThread_ConsoleRequest const &       requestData,
                                                 SAPDB_UInt4 const                      threadIndex,
                                                 SAPDBErr_MessageList &                 messageList);


/*===========================================================================*
 *  METHODS                                                                  *
 *===========================================================================*/

static  SAPDB_UInt4 ConsoleOpenConnection (RTEThread_ConsoleRequest const    &   requestData,
                                           SAPDB_UInt4 const                     threadIndex,
                                           SAPDBErr_MessageList              &   messageList)
{
    RTEThread_ConsoleWorkerBase            *pWorker;
    SAPDB_UInt4                             rc = RTE_CONS_NO_ERROR;
    RTEThread_ConsoleConnectionListItem    *pConnectionItem;

    SAPDBTRACE_ROUTINE_DEBUG("ConsoleOpenConnection", Console_Trace, 1);

    /*--- Searching free connection slot ------------------------------------*/
    rc = RTEThread_ConsoleConnectionList::Instance ().OpenConnection (RTE_CREATE_NEW, 
                                                                      requestData, 
                                                                      threadIndex,
                                                                      &pConnectionItem,
                                                                      messageList);

    if (RTE_CONS_FATAL_ERROR == rc)
    {
        RTE_Crash (messageList);
    }
    else if (RTE_CONS_ERROR == rc)
    {
        RTE_ConsoleStandardConnectPacket    connect;
        SAPDB_UInt4                         bytesRead;
        SAPDB_UInt4                         bytesTotal;
        
        switch (requestData.data.connectData.commType)
        {
            case RTE_CONS_COM_STANDARD:
                pWorker = new (RTEMem_Allocator::Instance()) RTEThread_ConsoleStandardWorker
                                (RTE_CONSOLE_SERVER_MODE, requestData.data.connectData.pid,
                                 RTE_COMM_UNDEF_REF, requestData.senderRef);
                                
                if (NULL == pWorker)
                {
                    messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_SYS_ALLOC_INST, 
                                                              "RTEThread_ConsoleStandardWorker");       
                    return rc;
                }

                break;
    /* Example:
    A support for a other communication types, e.g. XML, may be included at this point
            case RTE_CONS_COM_XML:
                break;
    */
            default:
                messageList = messageList + SAPDBErr_MessageList (RTE_CONTEXT, 
                                                                  RTEERR_THREAD_CONS_COMMUNICATION_TYPE, 
                                                                  SAPDB_ToString (requestData.data.connectData.commType));
                return rc;
        }
 
        if (!pWorker->Initialize (requestData.data.connectData.consoleShmID,
                                  messageList))
        {
            messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_INIT_WORKER);       
            destroy (pWorker, RTEMem_Allocator::Instance());
            return rc;
        }

        /* 
            For synchronisation with the client we have to get data from the client side first . 
            This is only be done for this purpose. The read data are not used here !!!!! 
        */

        if (RTE_CONS_NO_ERROR != (rc = pWorker->GetRequestData ((SAPDB_Byte *)(&connect.header), OPEN_REQ, 
                                                          sizeof (connect.header), bytesRead, bytesTotal, 
                                                          messageList)))
        {
            destroy (pWorker, RTEMem_Allocator::Instance());
            return rc;
        }

        pWorker->SendError (OPEN_REP, RTE_CONS_FATAL_ERROR, messageList);

        //Destroy Worker
        destroy (pWorker, RTEMem_Allocator::Instance());
    }
    else
    {
        /*--- Getting communication port to client (XCons) ------------------*/
        pWorker = pConnectionItem->GetWorker ();
        if (RTE_CONS_NO_ERROR != (rc = pWorker->Connect (pConnectionItem->GetHandle (), messageList)))
        {
            messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_CONNECT_FAILED); 
            if (!RTEThread_ConsoleConnectionList::Instance ().ReleaseConnection (pConnectionItem, 
                                                                                 pWorker->GetClientReference (),
                                                                                 messageList))
            {
                //This should not happen at this point => Inconsistency
                RTE_Crash (messageList);
            }

            return rc;
        }

        pWorker->SetBusyState (false);
    }

    return rc;
}

/*---------------------------------------------------------------------------*/

static  SAPDB_UInt4  ConsoleReleaseConnection (RTEThread_ConsoleRequest const    &   requestData,
                                               SAPDB_UInt4 const                     threadIndex,
                                               SAPDBErr_MessageList              &   messageList)
{
    RTEThread_ConsoleWorkerBase            *pWorker;
    RTEThread_ConsoleConnectionListItem    *pConnectionItem;
    SAPDB_UInt4                             rc = RTE_CONS_NO_ERROR;

    SAPDBTRACE_ROUTINE_DEBUG("ConsoleReleaseConnection", Console_Trace, 1);

    if (RTE_CONS_NO_ERROR 
        == RTEThread_ConsoleConnectionList::Instance ().OpenConnection (RTE_OPEN_EXISTING, 
                                                                        requestData,
                                                                        threadIndex,
                                                                        &pConnectionItem,
                                                                        messageList))
    {
        /*--- Getting worker for the request processing ------------------*/
        pWorker = pConnectionItem->GetWorker ();
        rc = pWorker->Disconnect (messageList); //Getting and sending Data from/to XCons
        if (RTE_CONS_NO_ERROR != rc)
        {
            messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_DISCONNECT_FAILED);
        }

        if (!RTEThread_ConsoleConnectionList::Instance().ReleaseConnection (pConnectionItem,
                                                                            pWorker->GetClientReference (),
                                                                            messageList))
        {
            //Should not be happen, since we got the connection above. Therefore the connection
            //could not already be released at this point => Inconsistency
            RTE_Crash (messageList);
        }
    }
    else
    {
        return RTE_CONS_ERROR;
    }

    return rc;
}

/*---------------------------------------------------------------------------*/

static  void    ExecuteConsoleCommand 
(
    RTEThread_ConsoleRequest const       &    request,
    SAPDB_UInt4 const                         threadIndex
)
{
    RTEThread_ConsoleWorkerBase            *pWorker;
    SAPDBErr_MessageList                    messageList;
    RTEThread_ConsoleConnectionListItem    *pConnectionItem;
    SAPDB_UInt4                             rc = RTE_CONS_NO_ERROR;
    
    SAPDBTRACE_ROUTINE_DEBUG("ExecuteConsoleCommand", Console_Trace, 1);

    if (OPEN_REQ == request.reqType)
    {
        SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex << ", open request: "
                                             << " Client PID: "     << (SAPDB_UInt8)request.data.connectData.pid
                                             << ", Client Ref.: "   << request.senderRef
                                             << ", Comm. type: "    << request.data.connectData.commType);
        
        rc = ConsoleOpenConnection (request, threadIndex, messageList);
        if (RTE_CONS_NO_ERROR != rc && RTE_CONS_TIMEOUT != rc)
        {
            messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_CONNECT);
            RTE_Message (messageList);
        }

        SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex 
                                             << ", open request of client PID " <<  (SAPDB_UInt8)request.data.connectData.pid
                                             << (rc ? " succeeded" : " failed"));

    }
    else if (CLOSE_REQ == request.reqType)
    {
        SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex << ", release request: "
                                             << " Client Ref.: "    << request.senderRef
                                             << ", Connection slot.: " << request.receiverRef
                                             << ", Conn. handle: "  << request.data.hConnect);

        rc = ConsoleReleaseConnection (request, threadIndex, messageList);

        if (RTE_CONS_NO_ERROR != rc && RTE_CONS_TIMEOUT != rc)
        {
            messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_RELEASE);
            RTE_Message (messageList);
        }

        SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex 
                                              << ", release request for connection handle " 
                                              <<  request.data.hConnect
                                              << (rc ? " succeeded" : " failed"));
    }
    else    //DATA_REQ
    {
        SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex << ", data request: "
                                             << " Client Ref.: "    << request.senderRef
                                             << ", Connection slot.: " << request.receiverRef
                                             << ", Conn. handle: "  << request.data.hConnect);
        //Connection may be deleted by the timeout scanner
        if (RTE_CONS_NO_ERROR 
            == RTEThread_ConsoleConnectionList::Instance ().OpenConnection (RTE_OPEN_EXISTING, 
                                                                            request,
                                                                            threadIndex,
                                                                            &pConnectionItem,
                                                                            messageList))
        {
            /*--- Getting worker for the request processing ------------------*/
            pWorker = pConnectionItem->GetWorker ();

            /* Process request from XCons */
            rc = pWorker->RequestProcessing (messageList);
            if (RTE_CONS_NO_ERROR != rc)
            {
                SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex 
                                                     << ", data request for connection handle " 
                                                     <<  request.data.hConnect
                                                     << " failed");

                messageList = messageList + SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_REQUEST);

                if (!RTEThread_ConsoleConnectionList::Instance ().ReleaseConnection (pConnectionItem, 
                                                                                     pWorker->GetClientReference (),   
                                                                                     messageList))
                {
                    SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex 
                                                         << ", data request: Releasing connection failed");
                    //Should not be happen, since we got the connection above. Therefore the connection
                    //could not already be released at this point => Inconsistency
                    RTE_Crash (messageList);
                }

                if (RTE_CONS_TIMEOUT != rc)
                {
                    RTE_Message (messageList);
                }
            }
            else
            {
                SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex 
                                                     << ", data request for connection handle " 
                                                     <<  request.data.hConnect
                                                     << " succeeded");
                pWorker->SetBusyState (false);
            }
        }
        else
        {
            SAPDBTRACE_WRITELN(Console_Trace, 5, "Worker thread " << threadIndex 
                                                  << ", open connection for connection handle " 
                                                  <<  request.data.hConnect
                                                  << " failed");
            RTE_Message (messageList);
        }
    }

    return;
}

/*---------------------------------------------------------------------------*/
/*!
     @description    Main routine of console worker thread


                  Reads the requests from the command queue and hand it
                  over to the request processing

*/


externC void *ConsoleWorker ( void *arg )
{
    RTEThread_ConsoleRequest            request;
    teo07_ThreadErr                     err = THR_OK_EO07;
	tsp00_ErrTextc                      errtext;
    ConsoleWorkerInfo               &   threadInfo = *(ConsoleWorkerInfo *)arg;
    
    SAPDBTRACE_ROUTINE_DEBUG("ConsoleWorker", Console_Trace, 1);

#   if defined(_FASTCAP)
    SAPDB_Char      workerName[30];

    strcpy (workerName,"ConsoleWorker_");
    strcat (workerName, SAPDB_ToString (threadInfo.index));
    CAPNameThread (workerName);
#   endif
    /* Let the worker thread run on normal priority if possible */
    sqlsetmythreadpriority(CONSOLE_WORKER_PRIO); 

    for (;;)
    {
        SAPDBTRACE_WRITELN(Console_Trace, 7, "Worker thread " << threadInfo.index 
                                                              << ", state: " 
                                                              << threadInfo.state);

        if (0 != (threadInfo.state & CONSOLE_WORKER_STATE_READY))
        {
            RTEThread_ConsoleCommandQueue::Instance().GetCommand (threadInfo, request);   
            SAPDBTRACE_WRITELN(Console_Trace, 7, "Worker thread " << threadInfo.index 
                                                                  << ", state: " 
                                                                  << threadInfo.state);
        }

        
        if (0 != (threadInfo.state & CONSOLE_WORKER_STATE_ACTIVE))
        {
            ExecuteConsoleCommand (request, threadInfo.index);
            RTEThread_ConsoleCommandQueue::Instance().GetCommand (threadInfo, request);
            SAPDBTRACE_WRITELN(Console_Trace, 7, "Worker thread " << threadInfo.index 
                                                                  << ", state: " 
                                                                  << threadInfo.state);
        }


        if (0 == (threadInfo.state & CONSOLE_WORKER_STATE_ACTIVE))
        {
            SAPDBTRACE_WRITELN(Console_Trace, 7, "Worker thread " << threadInfo.index << " suspended"); 

            sqlsuspendthread (threadInfo.thread, errtext, &err);
            if (THR_OK_EO07 != err)
            {
                RTE_Crash (SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_WORKER_THRD_SUSP, 
                                                SAPDB_ToString (threadInfo.index), 
                                                SAPDB_ToString (err), 
                                                errtext));
            }

            SAPDBTRACE_WRITELN(Console_Trace, 7, "Worker thread " << threadInfo.index << " resumed"); 
        }
    }

//    RTE_Message (SAPDBErr_MessageList(RTE_CONTEXT, RTEINFO_THREAD_CONS_WORKER_THRD_STOP, 
//                                      SAPDB_ToString (threadInfo.index)));

    sqlendthread(0);

    return NULL;
}

/*---------------------------------------------------------------------------*/

externC void *ConsoleWorkerThread ( void *arg )
{
    void * ret = 0;

#if defined (_WIN32)
    __try
    {
#endif
        ret = ConsoleWorker(arg);

#if defined (_WIN32)
    }
    __except( sql50k_excepthandler(GetExceptionInformation()) )
    {
        ;
    }
#endif

    return ret;
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool StartConsoleWorkerThread (ConsoleWorkerInfo &threadInfo, SAPDB_UInt4  threadIndex)
{
    tsp00_ErrTextc threadErrtext;
    teo07_ThreadErr threadOk;

    SAPDBTRACE_ROUTINE_DEBUG("StartConsoleWorkerThread", Console_Trace, 1);

    sqlbeginthread(CONSOLE_WORKER_STACK_SIZE,
                   ConsoleWorkerThread,
                   &threadInfo,
                   THR_CREATE_SUSPENDED_EO07,
                   &threadInfo.thread,
                   threadErrtext,
                   &threadOk );
    if ( threadOk != THR_OK_EO07 )
    {
        RTE_Message (SAPDBErr_MessageList(RTE_CONTEXT, RTEERR_THREAD_CONS_WORKER_THRD_START, 
                                          SAPDB_ToString (threadIndex), 
                                          SAPDB_ToString (threadOk), 
                                          threadErrtext));
        return false;
    }


    return true;
}


/*===========================================================================*
 *  END OF CODE                                                              *
 *===========================================================================*/
