/*! 
  -----------------------------------------------------------------------------
 
  module: vcn82.cpp
 
  -----------------------------------------------------------------------------
 
  responsible:  BerndV
 
  special area: DBMServer
 
  description:  implementation module for kernel event access
  
  version:      7.2.

  -----------------------------------------------------------------------------
 
                          Copyright (c) 1998-2005 SAP AG
 
  -----------------------------------------------------------------------------



    ========== licence begin  GPL
    Copyright (c) 1998-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




*/

/* 
  -----------------------------------------------------------------------------
  includes
  -----------------------------------------------------------------------------
*/

#include <stdio.h>

#include "geo47.h"

#include "hcn51.h"
#include "hcn80.h"
#include "hcn90.h"

#include "hcn82.h"

/* 
  -----------------------------------------------------------------------------
  specification private types and macros
  -----------------------------------------------------------------------------
*/

typedef struct tcn82PrioList {
  tsp31_event_prio_Enum    nIdent;
  const _TCHAR           * pName;
  _TCHAR                   cShort;
} tcn82PrioList;


// Identifier     Name                   cShort
#define PRIO_LIST_CN82 { \
{ sp31ep_nil    , _T("NIL")    , _T('N') }, \
{ sp31ep_low    , _T("LOW")    , _T('L') }, \
{ sp31ep_medium , _T("MEDIUM") , _T('M') }, \
{ sp31ep_high   , _T("HIGH")   , _T('H') }, \
{ sp31ep_nil    , NULL         , 0       }}

#define PRIO_DEFAULT_CN82    3
#define PRIO_NIL_CN82        0

typedef struct tcn82EventList {
  tsp31_event_ident_Enum   nIdent;
  const _TCHAR           * pName;
  tsp00_Int2               nMinValues;
  tsp00_Int2               nMaxValues;
  bool                     bSetable;
  bool                     bMultiPrio;
  bool                     bMultiValue;
  const _TCHAR           * pDescription;
  _TCHAR                   szSettings[513]; 
} tcn82EventList;

#define KEY_NAME      _T("NAME")
#define KEY_PRIORITY  _T("PRIORITY")
#define KEY_VALUE1    _T("VALUE1")
#define KEY_VALUE2    _T("VALUE2")
#define KEY_DATE      _T("DATE")
#define KEY_TIME      _T("TIME")
#define KEY_COUNT     _T("COUNT")
#define KEY_TEXT      _T("TEXT")
#define KEY_DESC      _T("DESCRIPTION")
#define CHAR_ASSIGN   _T('=')

#define EVENT_SEPARATOR   _T(':')
#define EVENT_PREFIX      _T('#')
#define EVENT_POSTFIX     _T(';')

#define EVENT_LIST_TITLE_CN82 _T("Name                \tState\tPrio\t1st\t2nd\tDescription\n")


// PTS 1124876 UH 2003-10-23 added LOG_ABOCE_LIMIT
// Identifier                        Name                  nMin nMax bSet  bMultP bMultV Description                            for Internal use
#define EVENT_LIST_CN82 { \
{ sp31ei_nil                      , _T("EMPTY")             , 0, 0, false, false, false, _T("Empty Event")                     ,""  }, \
{ sp31ei_alive                    , _T("ALIVE")             , 0, 0, false, false, false, _T("Kernel is alive")                 ,""  }, \
{ sp31ei_autosave                 , _T("AUTO_SAVE")         , 0, 0, false, false, false, _T("Autosave start/stop/on/off")      ,""  }, \
{ sp31ei_backup_pages             , _T("BACKUP_PAGES")      , 1, 1, true , false, false, _T("Next <n> pages moved")            ,""  }, \
{ sp31ei_datacache_log_hitrate    , _T("DC_LOG_HITRATE")    , 1, 1, false, false, false, _T("Log hitrate changed")             ,""  }, \
{ sp31ei_datacache_omslog_hitrate , _T("DC_OMSLOG_HITRATE") , 1, 1, false, false, false, _T("OMS-Log hitrate changed")         ,""  }, \
{ sp31ei_datacache_oms_hitrate    , _T("DC_OMS_HITRATE")    , 1, 1, false, false, false, _T("OMS hitrate changed")             ,""  }, \
{ sp31ei_db_filling_above_limit   , _T("DB_ABOVE_LIMIT")    , 1, 1, true , false, true , _T("Database above limit")            ,""  }, \
{ sp31ei_db_filling_below_limit   , _T("DB_BELOW_LIMIT")    , 1, 1, true , false, true , _T("Database below limit")            ,""  }, \
{ sp31ei_db_filling_changed       , _T("DB_FILL_CHANGE")    , 0, 0, false, false, false, _T("Database filling changed")        ,""  }, \
{ sp31ei_event                    , _T("EVENT")             , 0, 0, false, false, false, _T("Event config changed")            ,""  }, \
{ sp31ei_log_above_limit          , _T("LOG_ABOVE_LIMIT")   , 0, 0, true,  true,  false, _T("Log above limit")                 ,""  }, \
{ sp31ei_migration_progress       , _T("MIGR_PROGRESS")     , 0, 0, false, false, false, _T("Migration progress")              ,""  }, \
{ sp31ei_restart_shutdown         , _T("RESTART_SHUTDOWN")  , 0, 0, false, false, false, _T("Restart or shutdown")             ,""  }, \
{ sp31ei_savepoint                , _T("SAVEPOINT")         , 0, 0, false, false, false, _T("Savpoint written")                ,""  }, \
{ sp31ei_sessions                 , _T("SESSIONS")          , 0, 0, false, false, false, _T("Session")                         ,""  }, \
{ sp31ei_upd_stat_wanted          , _T("UPDATE_STATISTICS") , 0, 0, false, false, false, _T("Update statistics wanted")        ,""  }, \
{ sp31ei_warning                  , _T("WARNING")           , 0, 0, false, false, false, _T("Warning")                         ,""  }, \
{ sp31ei_error                    , _T("ERROR")             , 0, 0, false, false, false, _T("Error")                           ,""  }, \
{ sp31ei_error                    , NULL                    , 0, 0, true , false, false, NULL                                  ,""  }}

#define EVENT_SET_CN82         _T("SET EVENT")
#define EVENT_DELETE_CN82      _T("DELETE EVENT")
#define EVENT_SELECT_CN82      _T("SELECT VALUE,NUMERIC_VALUE FROM SYSDD.SERVER_DB_STATE WHERE DESCRIPTION='%s'")
#define SQL_NULL_VALUE_CN82    _T("(null)")
#define SQL_STRING_SEP_CN82    _T('\'')
#define EVENT_ON_CN82          _T("ON")
#define EVENT_OFF_CN82         _T("OFF")

/* 
  -----------------------------------------------------------------------------
  specification private functions
  -----------------------------------------------------------------------------
*/
static tcn00_Error cn82_AnalyzeParameters
      ( const _TCHAR   * szCommand,
        tcn82EventList * pEvent,
        tcn82PrioList  * pPriority,
        _TCHAR         * sz1stValue,
        long             size1stValue,
        _TCHAR         * sz2ndValue,
        long             size2ndValue);

static tcn00_Error cn82_EventChange
      ( VControlDataT  * vcontrol,
        CommandT       * command,
        char           * replyData,
        int            * replyLen,
        int              replyLenMax,
        bool             bSet );

static tcn00_Error cn82_EventConnect
      ( VControlDataT  * vcontrol,
        char           * replyData,
        int            * replyLen );
 
static void cn82_SaveEvent
      (const tsp00_DbNamec    szDbName, 
       const tcn82EventList * pEvent,
       const tcn82PrioList  * pPrio,
       const _TCHAR         * sz1stValue, 
       const _TCHAR         * sz2ndValue, 
       const bool             bSet);

static tcn00_Error cn82_RestoreOneEvent
      (tin01_sql_session   * pSession,
       const tsp00_DbNamec   szDBName,
       tcn82EventList      * pEvent );

static void cn82_EqualizeOneEvent
      (tin01_sql_session   * pSession,
       const tsp00_DbNamec   szDBName,
       tcn82EventList      * pEvent );

static void cn82_ListOneEvent
      (const tsp00_DbNamec   szDBName,
       tcn82EventList      * pEvent,
       _TCHAR              * pBuffer );

/* 
  -----------------------------------------------------------------------------
  function:     cn82EventList
  -----------------------------------------------------------------------------
 */
tcn00_Error cn82EventList
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  tcn00_Error       nFuncReturn  = OK_CN00;
  tcn82EventList    lstEvents[] = EVENT_LIST_CN82;
  tsp00_Int2          nIndex      = 0;
  _TCHAR          * pCurrent    = replyData;
  _TCHAR            pBuffer[10000];
  tin01_sql_session aSession;

  cn51DBMConfigValue::startBuffering(vcontrol->dbname);

   if (cn90DBState(vcontrol->dbname) == STATE_ONLINE_CN00) {

      // if db warm, then equalize database and dbm configuration
      if (cn80ConnectSQL ( vcontrol->dbname, &aSession) == OK_CN00) {

        nIndex = 0;
        while (lstEvents[nIndex].pName != NULL) {
          cn82_EqualizeOneEvent(&aSession, vcontrol->dbname, &lstEvents[nIndex]);
          nIndex++;
        } // end while
      } // end if

      cn80ReleaseSQL ( &aSession, false);

  } else {
    // if not then read configuration from file
    nIndex = 0;
    while (lstEvents[nIndex].pName != NULL) {
      tsp00_Namec          szConfig;
      cn51DBMConfigValue   cfgEvent (vcontrol->dbname, szConfig.rawAssign(lstEvents[nIndex].pName));

      _tcscpy(lstEvents[nIndex].szSettings, (tsp00_C512c) cfgEvent);

      nIndex++;
    } // end while

  } // end if

  // init OK Answer
  _stprintf (pCurrent, "%s%s%s", ANSWER_OK_CN00, LINE_SEPSTRING_CN00, (_TCHAR *) EVENT_LIST_TITLE_CN82);
  pCurrent = pCurrent + _tcslen(pCurrent);

  nIndex = 0;
  while (lstEvents[nIndex].pName != NULL && (pCurrent - replyData) < replyLenMax) {
    if (lstEvents[nIndex].bSetable) {
      cn82_ListOneEvent(vcontrol->dbname, &lstEvents[nIndex], pBuffer);
      if ((int) (pCurrent - replyData + _tcslen(pBuffer)) < replyLenMax) {
        _tcscpy(pCurrent, pBuffer);
      } // end if
      pCurrent = pCurrent + _tcslen(pBuffer);
    } // end if
    nIndex++;
  } // end while
  
  cn51DBMConfigValue::endBuffering(vcontrol->dbname);

  *replyLen = (int)_tcslen(replyData);

  return nFuncReturn;
} // end cn82EventList

/*
  -----------------------------------------------------------------------------
  function:     cn82EventSet
  -----------------------------------------------------------------------------
 */
tcn00_Error cn82EventSet
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  return cn82_EventChange (vcontrol,
                           command,
                           replyData,
                           replyLen,
                           replyLenMax,
                           true);
} // end cn82EventSet

/*
  -----------------------------------------------------------------------------
  function:     cn82EventDelete
  -----------------------------------------------------------------------------
 */
tcn00_Error cn82EventDelete
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  return cn82_EventChange (vcontrol,
                           command,
                           replyData,
                           replyLen,
                           replyLenMax,
                           false);
} // end cn82EventDelete

/*
  -----------------------------------------------------------------------------
  function:     cn82EventWait
  -----------------------------------------------------------------------------
 */
tcn00_Error cn82EventWait
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  tcn00_Error               nFuncReturn = OK_CN00;
  tin01_sql_session       * pSession    = (tin01_sql_session *) vcontrol->pEventSession;
  _TCHAR                  * pCurrent    = replyData;
  tcn82EventList            lstEvents[] = EVENT_LIST_CN82;
  tcn82PrioList             lstPrio[]   = PRIO_LIST_CN82;
  tsp00_Int2                  nIndex      = 0;
  tsp00_Int2                  nPrioIndex  = 0;
  bool                      bUnknown    = true;
  int                       rc;
  tsp00_ErrTextc              errtext;
  tsp00_KnlIdentifierc       errname;
  tsp00_Int2                  errcode;
  tsp00_Int2                  errpos;
  tsp31_event_description   aEvent;

  // connect
  if (pSession == NULL) {
    nFuncReturn = cn82_EventConnect(vcontrol, replyData, replyLen);
    pSession    = (tin01_sql_session *) vcontrol->pEventSession;
  } // end if

  // now we have a session, let's look for events
  if (nFuncReturn == OK_CN00) {
    rc = i29eventwait (pSession, &aEvent );
    // skip empty events
    while (rc == 0 && aEvent.sp31ed_ident == sp31ei_nil) {
      rc = i29eventwait (pSession, &aEvent );
    } // end if
    // something wrong? 
    if (rc != 0) {
      nFuncReturn = ERR_SQL_CN00;
      i29sqllasterr(pSession, errtext, errname, &errcode, &errpos);
      cn82FreeEventSession(vcontrol);
      cn90AnswerSQLError(replyData, replyLen, nFuncReturn, errtext, rc);
    } else {
       // init OK Answer
      _stprintf (pCurrent, "%s%s", ANSWER_OK_CN00, LINE_SEPSTRING_CN00);
      pCurrent = pCurrent + _tcslen(pCurrent);

      // search event information
      while (lstEvents[nIndex].pName != NULL && lstEvents[nIndex].nIdent != aEvent.sp31ed_ident) {
        nIndex++;
      } // end if
      if (lstEvents[nIndex].pName != NULL) {
        bUnknown = false;
      } // end if

      // print name
      if (bUnknown) {
        _stprintf (pCurrent, "%s%c%d%s", KEY_NAME, CHAR_ASSIGN, (int) aEvent.sp31ed_ident, LINE_SEPSTRING_CN00);
      } else {
        _stprintf (pCurrent, "%s%c%s%s", KEY_NAME, CHAR_ASSIGN, lstEvents[nIndex].pName, LINE_SEPSTRING_CN00);
      } // end if
      pCurrent = pCurrent + _tcslen(pCurrent);

      // print priority
      while (lstPrio[nPrioIndex].pName != NULL && lstPrio[nPrioIndex].nIdent != aEvent.sp31ed_priority) {
        nPrioIndex++;
      } // end if
      if (lstPrio[nPrioIndex].pName == NULL) {
        _stprintf (pCurrent, "%s%c%d%s", KEY_PRIORITY, CHAR_ASSIGN, (int) aEvent.sp31ed_priority, LINE_SEPSTRING_CN00);
      } else {
        _stprintf (pCurrent, "%s%c%s%s", KEY_PRIORITY, CHAR_ASSIGN, (_TCHAR *) lstPrio[nPrioIndex].pName, LINE_SEPSTRING_CN00);
      } // end if
      pCurrent = pCurrent + _tcslen(pCurrent);

      // Date
      _stprintf (pCurrent, "%s%c%.8s%s", KEY_DATE, CHAR_ASSIGN, (_TCHAR *) aEvent.sp31ed_date, LINE_SEPSTRING_CN00);
      pCurrent = pCurrent + _tcslen(pCurrent);

      // Time
      _stprintf (pCurrent, "%s%c%.6s%s", KEY_TIME, CHAR_ASSIGN, (_TCHAR *) &aEvent.sp31ed_time[2], LINE_SEPSTRING_CN00);
      pCurrent = pCurrent + _tcslen(pCurrent);

      // Value 1
      if (aEvent.sp31ed_value_1 != MAX_INT4_SP00) {
        _stprintf (pCurrent, "%s%c%d%s", KEY_VALUE1, CHAR_ASSIGN, (int) aEvent.sp31ed_value_1, LINE_SEPSTRING_CN00);
        pCurrent = pCurrent + _tcslen(pCurrent);
      } // end if 

      // Value 2
      if (aEvent.sp31ed_value_2 != MAX_INT4_SP00) {
        _stprintf (pCurrent, "%s%c%d%s", KEY_VALUE2, CHAR_ASSIGN, (int) aEvent.sp31ed_value_2, LINE_SEPSTRING_CN00);
        pCurrent = pCurrent + _tcslen(pCurrent);
      } // end if 
      
      // Count
      if (aEvent.sp31ed_eventcnt > 0) {
        _stprintf (pCurrent, "%s%c%d%s", KEY_COUNT, CHAR_ASSIGN, (int) aEvent.sp31ed_eventcnt, LINE_SEPSTRING_CN00);
        pCurrent = pCurrent + _tcslen(pCurrent);
      } // end if 

      // Text
      if (aEvent.sp31ed_text_len > 0) {
        _stprintf (pCurrent, "%s%c%.*s%s", KEY_TEXT, CHAR_ASSIGN, (int) aEvent.sp31ed_text_len, (_TCHAR *) aEvent.sp31ed_text_value, LINE_SEPSTRING_CN00);
        pCurrent = pCurrent + _tcslen(pCurrent);
      } // end if 

      // Description
      if (lstEvents[nIndex].pDescription != NULL) {
        _stprintf (pCurrent, "%s%c%s%s", KEY_DESC, CHAR_ASSIGN, (_TCHAR *) lstEvents[nIndex].pDescription, LINE_SEPSTRING_CN00);
        pCurrent = pCurrent + _tcslen(pCurrent);
      } // end if 

      *replyLen = (int)strlen(replyData);
    } // end if
  } // end if

  return nFuncReturn;
} // end cn82EventWait

/*
  -----------------------------------------------------------------------------
  function:     cn82EventRelease
  -----------------------------------------------------------------------------
 */
tcn00_Error cn82EventRelease
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  cn82FreeEventSession(vcontrol);
  return cn90AnswerOK (replyData, replyLen, NULL);
} // end cn82EventRelease

/*
  -----------------------------------------------------------------------------
  function:     cn82FreeEventSession
  -----------------------------------------------------------------------------
 */
void cn82FreeEventSession
      ( VControlDataT * vcontrol )
{
  tin01_sql_session       * pSession    = (tin01_sql_session *) vcontrol->pEventSession;

  if (pSession != NULL) {
    if (pSession->is_connected) {
      i29eventrelease(pSession);
    } // end if
    delete pSession;
    vcontrol->pEventSession = NULL;
  } // end if

} // end cn82FreeEventSession

/*
  -----------------------------------------------------------------------------
  function:     cn82_AnalyzeParameters
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn82_AnalyzeParameters
      ( const _TCHAR   * szCommand,
        tcn82EventList * pEvent,
        tcn82PrioList  * pPriority,
        _TCHAR         * sz1stValue,
        long             size1stValue,
        _TCHAR         * sz2ndValue,
        long             size2ndValue)
{
  tcn00_Error	         nFuncReturn = OK_CN00;
  long                 nIndex      = 0; 
  long                 nPrioIndex  = 0; 
  long                 nValue      = 0; 
  long                 nValueCount = 0; 
  tcn82EventList       lstEvents[] = EVENT_LIST_CN82;
  tcn82PrioList        lstPrio[] = PRIO_LIST_CN82;
  _TCHAR               szEvent[PARAMETER_MAXLEN_CN90];  
  _TCHAR               szPriority[PARAMETER_MAXLEN_CN90];  

  // read and check eventname
  cn90GetToken(szCommand, szEvent, 1, PARAMETER_MAXLEN_CN90);
  while (lstEvents[nIndex].pName != NULL && _tcsicmp(szEvent, lstEvents[nIndex].pName) != 0) {
    nIndex++;
  } // end if
  if (lstEvents[nIndex].pName == NULL) {
    nFuncReturn = ERR_PARAM_CN00;
  } // end if

  // read and check priority
  if (nFuncReturn == OK_CN00) {
    if (cn90GetToken(szCommand, szPriority, 2, PARAMETER_MAXLEN_CN90)) {
      while (lstPrio[nPrioIndex].pName != NULL && _tcsicmp(szPriority, lstPrio[nPrioIndex].pName) != 0) {
        nPrioIndex++;
      } // end if
      if (lstPrio[nPrioIndex].pName == NULL) {
        if (cn90AnalyzeNumber(szPriority, &nValue)) {
          nPrioIndex = PRIO_DEFAULT_CN82;
        } else {
          nFuncReturn = ERR_PARAM_CN00;
        } // end if
      } // end if
    } else {
      nPrioIndex = PRIO_DEFAULT_CN82;
    } // end if
  } // end if

  // read and check values
  if (nFuncReturn == OK_CN00) {
    nValueCount = nValueCount + cn90GetToken(szCommand, sz1stValue, 3, size1stValue)?1:0;
    nValueCount = nValueCount + cn90GetToken(szCommand, sz2ndValue, 4, size2ndValue)?1:0;

    if (_tcslen(szPriority) > 0 && cn90AnalyzeNumber(szPriority, &nValue)) {
       _tcscpy(sz2ndValue, sz1stValue);
       _tcscpy(sz1stValue, szPriority);
       _tcscpy(szPriority, "");
       nValueCount++;
    } // end if

    if (nValueCount < lstEvents[nIndex].nMinValues || nValueCount > lstEvents[nIndex].nMinValues) {
      nFuncReturn = ERR_PARAM_CN00;
    } // end if
  } // end if
      
  // set structures
  if (nFuncReturn == OK_CN00) {
    SAPDB_memcpy(pPriority, &lstPrio[nPrioIndex], sizeof(tcn82PrioList));
    SAPDB_memcpy(pEvent,    &lstEvents[nIndex],   sizeof(tcn82EventList));
  } // end if

  return nFuncReturn;
} // end cn82_AnalyzeParameters

/*
  -----------------------------------------------------------------------------
  function:     cn82_EventChange
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn82_EventChange
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax,
        bool            bSet)
{
  tcn00_Error	         nFuncReturn = OK_CN00;
  tin01_sql_session  * pSession;
  _TCHAR               szStatement[PARAMETER_MAXLEN_CN90];  
  tcn82EventList       aEvent;
  tcn82PrioList        aPrio;
  _TCHAR               sz1stValue[PARAMETER_MAXLEN_CN90]; 
  _TCHAR               sz2ndValue[PARAMETER_MAXLEN_CN90]; 


  // read and check parameters
  nFuncReturn = cn82_AnalyzeParameters(command->args, 
                                       &aEvent,
                                       &aPrio, 
                                       sz1stValue,
                                       PARAMETER_MAXLEN_CN90,
                                       sz2ndValue,
                                       PARAMETER_MAXLEN_CN90);
  
  // look for utility session
  if (nFuncReturn == OK_CN00) {
    nFuncReturn = cn80UtilSession(vcontrol, replyData, replyLen, &pSession);
  } else {
    cn90AnswerIError(replyData, replyLen, nFuncReturn);
  } // end if

  // create and send statement 
  if (nFuncReturn == OK_CN00) {
    _stprintf (szStatement, "%s %s %s %s %s", bSet ? EVENT_SET_CN82 : EVENT_DELETE_CN82, 
                                              (_TCHAR *) aEvent.pName,
                                              bSet ? (_TCHAR *) aPrio.pName : _T(""), 
                                              (_TCHAR *) sz1stValue, 
                                              (_TCHAR *) sz2ndValue);
    nFuncReturn = cn80ExecuteUtil(pSession, szStatement, replyData, replyLen); 
  } /* end if */

  if (nFuncReturn == OK_CN00) {
    // save event settings bSet ?
    cn82_SaveEvent(vcontrol->dbname, &aEvent, &aPrio, sz1stValue, sz2ndValue, bSet);
  
    // generate Answer
    cn90AnswerOK(replyData, replyLen, NULL);
  } /* end if */

  *replyLen = (int)strlen(replyData);

  return nFuncReturn;

} // end cn82_EventChange

/*
  -----------------------------------------------------------------------------
  function:     cn82_EventConnect
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn82_EventConnect
      ( VControlDataT * vcontrol,
        char          * replyData,
        int           * replyLen )
{
  tcn00_Error               nFuncReturn = OK_CN00;
  tin01_sql_session       * pSession    = NULL;
  int                       rc;
  tsp00_ErrTextc              errtext;
  tsp00_KnlIdentifierc       errname;
  tsp00_Int2                  errcode;
  tsp00_Int2                  errpos;

  // Alloc Session structure
  pSession = new tin01_sql_session;

  if (pSession == NULL) {
    nFuncReturn = cn90AnswerIError(replyData, replyLen, ERR_MEM_CN00);
  } else {
    vcontrol->pEventSession = (void *) pSession;
  } // end if

  // create session
  if (nFuncReturn == OK_CN00) {

    i29initsession(pSession, NULL);
    i29lasterr_on(pSession);
    rc = i29eventconnect (pSession, vcontrol->dbname);

    // something wrong? 
    if (rc != 0) {
      nFuncReturn = ERR_SQL_CN00;
      i29sqllasterr(pSession, errtext, errname, &errcode, &errpos);
      cn90AnswerSQLError(replyData, replyLen, nFuncReturn, errtext, rc);
      cn82FreeEventSession(vcontrol);
    } // end if
  } // end if

  return nFuncReturn;
} // end cn82_EventConnect

/*
  -----------------------------------------------------------------------------
  function:     cn82_SaveEvent
  -----------------------------------------------------------------------------
 */
static void cn82_SaveEvent
      (const tsp00_DbNamec    szDbName, 
       const tcn82EventList * pEvent,
       const tcn82PrioList  * pPrio,
       const _TCHAR         * sz1stValue, 
       const _TCHAR         * sz2ndValue, 
       const bool             bSet)
{
  tsp00_Namec               szConfig;
  cn51DBMConfigValue        cfgEvent (szDbName, szConfig.rawAssign(pEvent->pName));
  tsp00_C512c               szConfigString = cfgEvent;
  tsp00_C64c                szEntry;
  tsp00_C64c                szMatch;
  _TCHAR                  * pMatch;
  int                       nLength;
  int                       nMatchLength;
  int                       i;

  // generate entry
  _stprintf (szEntry, _T("%c%c%c%s%c%s%c"), EVENT_PREFIX,
                                            pPrio->cShort, 
                                            EVENT_SEPARATOR,
                                            sz1stValue,
                                            EVENT_SEPARATOR,
                                            sz2ndValue,
                                            EVENT_POSTFIX);

  // generate match for searching existing entry
  if (pEvent->bMultiPrio && pEvent->bMultiValue) {
    // look for prio and value
    _stprintf (szMatch, _T("%c%c%c%s%c%s%c"), EVENT_PREFIX,
                                              pPrio->cShort, 
                                              EVENT_SEPARATOR,
                                              sz1stValue,
                                              EVENT_SEPARATOR,
                                              sz2ndValue,
                                              EVENT_POSTFIX);
  } else if (!pEvent->bMultiPrio && pEvent->bMultiValue) {
    // look only for value
    _stprintf (szMatch, _T("%c%s%c%s%c"),     EVENT_SEPARATOR,
                                              sz1stValue,
                                              EVENT_SEPARATOR,
                                              sz2ndValue,
                                              EVENT_POSTFIX);
  } else if (pEvent->bMultiPrio && !pEvent->bMultiValue) {
    // look only for prio
    _stprintf (szMatch, _T("%c%c%c"),         EVENT_PREFIX,
                                              pPrio->cShort, 
                                              EVENT_SEPARATOR);
  } else if (!pEvent->bMultiPrio && !pEvent->bMultiValue) {
    // look only for entry
    _stprintf (szMatch, _T("%c"),             EVENT_PREFIX);
  } // end if

  // look for existing entry
  pMatch  = strstr(szConfigString, szMatch);

  // delete existing entry
  if (pMatch != NULL) {
    // find start of entry
    while (*pMatch != EVENT_PREFIX) {
      pMatch--;
    } // end while

    // find end of entry
    nMatchLength = 1;
    while (pMatch[nMatchLength] != EVENT_POSTFIX) {
      nMatchLength++;
    } // end while
    nMatchLength++;

    // remove entry
    nLength      = (int)_tcslen(pMatch);
    for (i = 0; i < nLength - nMatchLength + 1; i++) {
       pMatch[i] = pMatch[i + nMatchLength];
    } // end for
  } // end if

  // add entry to config string
  if (bSet) {
    strcat(szConfigString, szEntry);
  } // end if

  // save config string
  cfgEvent = szConfigString;   

} // end cn82_SaveEvent

/*
  -----------------------------------------------------------------------------
  function:     cn82RestoreEvents
  -----------------------------------------------------------------------------
 */
tcn00_Error cn82RestoreEvents
      (const tsp00_DbNamec   szDBName)
{
  tcn00_Error       nFuncReturn = OK_CN00;
  tcn82EventList    lstEvents[] = EVENT_LIST_CN82;
  tsp00_Int2          nIndex      = 0;
  tin01_sql_session aSession;

  nFuncReturn = cn80ConnectUtil ( szDBName, &aSession);

  while (lstEvents[nIndex].pName != NULL && nFuncReturn == OK_CN00) {
    nFuncReturn = cn82_RestoreOneEvent(&aSession, szDBName, &lstEvents[nIndex]);
    nIndex++;
  } // end while

  cn80ReleaseUtil ( &aSession, (nFuncReturn == OK_CN00));

  return nFuncReturn;
} // end cn82RestoreEvents

/*
  -----------------------------------------------------------------------------
  function:     cn82_RestoreOneEvent
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn82_RestoreOneEvent
      (tin01_sql_session   * pSession,
       const tsp00_DbNamec   szDBName,
       tcn82EventList      * pEvent )
{
  tcn00_Error          nFuncReturn = OK_CN00;
  tsp00_Int2             nPrioIndex      = 0;
  tsp00_Int2             nIndex          = 0;
  tsp00_Int2             nLocalIndex     = 0;
  tsp00_Namec          szConfig;
  cn51DBMConfigValue   cfgEvent (szDBName, szConfig.rawAssign(pEvent->pName));
  tsp00_C512c          szConfigString = cfgEvent;
  _TCHAR               sz1stValue[PARAMETER_MAXLEN_CN90]; 
  _TCHAR               sz2ndValue[PARAMETER_MAXLEN_CN90]; 
  tcn82PrioList        lstPrio[] = PRIO_LIST_CN82;
  _TCHAR               szStatement[PARAMETER_MAXLEN_CN90];  

  while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 && nFuncReturn == OK_CN00) {
    // find eventstart
    while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 &&
           szConfigString[nIndex] != EVENT_PREFIX            ) {
      nIndex++;
    } // end while
    if (szConfigString[nIndex] == EVENT_PREFIX) {
      // look for prio
      nIndex++;
      nPrioIndex = 0;
      while (lstPrio[nPrioIndex].pName != NULL && szConfigString[nIndex] != lstPrio[nPrioIndex].cShort) {
        nPrioIndex++;
      } // end if
      if (lstPrio[nPrioIndex].pName == NULL) {
        nPrioIndex = PRIO_DEFAULT_CN82;
      } // end if

      // look for value 1
      nIndex++;
      nIndex++;
      nLocalIndex = 0;
      while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 &&
             szConfigString[nIndex] != EVENT_SEPARATOR         ) {
        sz1stValue[nLocalIndex] = szConfigString[nIndex];
        nLocalIndex++;
        nIndex++;
      } // end while
      sz1stValue[nLocalIndex] = CHAR_STRINGTERM_CN90;

      // look for value 2
      nIndex++;
      nLocalIndex = 0;
      while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 &&
             szConfigString[nIndex] != EVENT_POSTFIX           ) {
        sz2ndValue[nLocalIndex] = szConfigString[nIndex];
        nLocalIndex++;
        nIndex++;
      } // end while
      sz2ndValue[nLocalIndex] = CHAR_STRINGTERM_CN90;

      // create and execute statement
      _stprintf (szStatement, "%s %s %s %s %s", EVENT_SET_CN82, 
                                                (_TCHAR *) pEvent->pName,
                                                (_TCHAR *) lstPrio[nPrioIndex].pName, 
                                                (_TCHAR *) sz1stValue, 
                                                (_TCHAR *) sz2ndValue);

      nFuncReturn = cn80ExecuteUtil(pSession, szStatement); 
    } // end if
  } // end while

  return nFuncReturn;
} // end cn82_RestoreOneEvent

/*
  -----------------------------------------------------------------------------
  function:     cn82_EqualizeOneEvent
  -----------------------------------------------------------------------------
 */
static void cn82_EqualizeOneEvent
      (tin01_sql_session   * pSession,
       const tsp00_DbNamec   szDBName,
       tcn82EventList      * pEvent )
{
  _TCHAR               szStatement[PARAMETER_MAXLEN_CN90];  
  _TCHAR               szPriority[PARAMETER_MAXLEN_CN90];  
  _TCHAR               sz1stValue[PARAMETER_MAXLEN_CN90];  
  _TCHAR               sz2ndValue[PARAMETER_MAXLEN_CN90];  
  _TCHAR               pBuffer[10000];
  int                  nFilled;
  tsp00_Namec          szConfig;
  cn51DBMConfigValue   cfgEvent (szDBName, szConfig.rawAssign(pEvent->pName));
  tsp00_C64c           szEntry;
  tsp00_C512c          szConfigString;
  int                  nIndex;
  _TCHAR             * pLine = NULL;
  long                 nPrioIndex  = 0; 
  tcn82PrioList        lstPrio[] = PRIO_LIST_CN82;

  // clear config value
  szConfigString[0] = CHAR_STRINGTERM_CN90;
  
  // look in database
  _stprintf (szStatement, EVENT_SELECT_CN82, (_TCHAR *) pEvent->pName);
  
  if (cn80ExecuteSQL(pSession, 
                     szStatement, 
                     pBuffer, 
                     10000, 
                     nFilled) == OK_CN00) {

    // analyze result
    pLine = strtok(pBuffer, "\n");
    // skip OK Line
    pLine = strtok(NULL, "\n");
    // skip END/CONTINUE Line
    pLine = strtok(NULL, "\n");
    while (pLine != NULL) {

      if (_tcslen(pLine) > 0) {

        // read priority
        pLine++;
        nIndex = 0;
        while (*pLine != SQL_STRING_SEP_CN82) {
          szPriority[nIndex] = *pLine;
          nIndex++;
          pLine++;
        } // end while
        szPriority[nIndex] = CHAR_STRINGTERM_CN90;

        while (lstPrio[nPrioIndex].pName != NULL && _tcsicmp(szPriority, lstPrio[nPrioIndex].pName) != 0) {
          nPrioIndex++;
        } // end if
        if (lstPrio[nPrioIndex].pName == NULL) {
          nPrioIndex = PRIO_DEFAULT_CN82;
        } // end if

        // read 1st value
        pLine++;
        pLine++;
        nIndex = 0;
        while (*pLine != CHAR_STRINGTERM_CN90) {
          sz1stValue[nIndex] = *pLine;
          nIndex++;
          pLine++;
        } // end while
        sz1stValue[nIndex] = CHAR_STRINGTERM_CN90;
        if (_tcsicmp(sz1stValue, SQL_NULL_VALUE_CN82) == 0) {
          sz1stValue[0] = CHAR_STRINGTERM_CN90;
        } // end if

        // 2nd value
        sz2ndValue[0] = CHAR_STRINGTERM_CN90;
   


        // add event to config string
        _stprintf (szEntry, _T("%c%c%c%s%c%s%c"), EVENT_PREFIX,
                                                  lstPrio[nPrioIndex].cShort, 
                                                  EVENT_SEPARATOR,
                                                  sz1stValue,
                                                  EVENT_SEPARATOR,
                                                  sz2ndValue,
                                                  EVENT_POSTFIX);
        strcat(szConfigString, szEntry);

        pLine = strtok(NULL, "\n");

      } // end if
    } // end while
  } // end if

  // save config string
  cfgEvent = szConfigString;

  // put config string in event stucture
  _tcscpy(pEvent->szSettings, szConfigString);

} // end cn82_EqualizeOneEvent

/*
  -----------------------------------------------------------------------------
  function:     cn82_ListOneEvent
  -----------------------------------------------------------------------------
 */
static void cn82_ListOneEvent
      (const tsp00_DbNamec   szDBName,
       tcn82EventList      * pEvent,
       _TCHAR              * pBuffer )
{
  tsp00_Int2             nPrioIndex      = 0;
  tsp00_Int2             nIndex          = 0;
  tsp00_Int2             nLocalIndex     = 0;
//  tsp00_Namec          szConfig;
//  cn51DBMConfigValue   cfgEvent (szDBName, szConfig.rawAssign(pEvent->pName));
//  tsp00_C512c          szConfigString = cfgEvent;
  tsp00_C512c          szConfigString;
  _TCHAR               sz1stValue[PARAMETER_MAXLEN_CN90]; 
  _TCHAR               sz2ndValue[PARAMETER_MAXLEN_CN90]; 
  tcn82PrioList        lstPrio[] = PRIO_LIST_CN82;
  _TCHAR              * pCurrent = pBuffer;

  szConfigString.rawAssign(pEvent->szSettings);

  while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90) {
    // find eventstart
    while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 &&
           szConfigString[nIndex] != EVENT_PREFIX            ) {
      nIndex++;
    } // end while
    if (szConfigString[nIndex] == EVENT_PREFIX) {
      // look for prio
      nIndex++;
      nPrioIndex = 0;
      while (lstPrio[nPrioIndex].pName != NULL && szConfigString[nIndex] != lstPrio[nPrioIndex].cShort) {
        nPrioIndex++;
      } // end if
      if (lstPrio[nPrioIndex].pName == NULL) {
        nPrioIndex = PRIO_DEFAULT_CN82;
      } // end if

      // look for value 1
      nIndex++;
      nIndex++;
      nLocalIndex = 0;
      while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 &&
             szConfigString[nIndex] != EVENT_SEPARATOR         ) {
        sz1stValue[nLocalIndex] = szConfigString[nIndex];
        nLocalIndex++;
        nIndex++;
      } // end while
      sz1stValue[nLocalIndex] = CHAR_STRINGTERM_CN90;

      // look for value 2
      nIndex++;
      nLocalIndex = 0;
      while (szConfigString[nIndex] != CHAR_STRINGTERM_CN90 &&
             szConfigString[nIndex] != EVENT_POSTFIX           ) {
        sz2ndValue[nLocalIndex] = szConfigString[nIndex];
        nLocalIndex++;
        nIndex++;
      } // end while
      sz2ndValue[nLocalIndex] = CHAR_STRINGTERM_CN90;

      // print list entry
      _stprintf (pCurrent, "%-20s%s%s%s%s%s%s%s%s%s%s%s", 
                           (_TCHAR *) pEvent->pName,
                           VALUE_SEPSTRING_CN00,
                           EVENT_ON_CN82,
                           VALUE_SEPSTRING_CN00,
                           (_TCHAR *) lstPrio[nPrioIndex].pName, 
                           VALUE_SEPSTRING_CN00,
                           (_TCHAR *) sz1stValue, 
                           VALUE_SEPSTRING_CN00,
                           (_TCHAR *) sz2ndValue,
                           VALUE_SEPSTRING_CN00,
                           (_TCHAR *) pEvent->pDescription,
                           LINE_SEPSTRING_CN00);
      pCurrent = pCurrent + _tcslen(pCurrent);
    } // end if
  } // end while

  if (pCurrent == pBuffer) {
    // print list entry
    _stprintf (pCurrent, "%-20s%s%s%s%s%s%s%s%s%s%s%s", 
                         (_TCHAR *) pEvent->pName,
                         VALUE_SEPSTRING_CN00,
                         EVENT_OFF_CN82,
                         VALUE_SEPSTRING_CN00,
                         (_TCHAR *) lstPrio[PRIO_NIL_CN82].pName, 
                         VALUE_SEPSTRING_CN00,
                         (pEvent->nMaxValues < 1) ? "": "0", 
                         VALUE_SEPSTRING_CN00,
                         (pEvent->nMaxValues < 2) ? "": "0", 
                         VALUE_SEPSTRING_CN00,
                         (_TCHAR *) pEvent->pDescription,
                         LINE_SEPSTRING_CN00);
  } // end if 

} // end cn82_ListOneEvent
