/****************************************************************************
**
**  Name:  ERRFMT.C
**
**  Description:
**     Entry points for the replacement for Actor error handling.
**
**  Status:  CODED
**
**  $Log:   S:/tbird/mt2_186/errtext/errfmt.c_v  $
** 
**    Rev 1.0   16 Dec 1996 14:29:34   Judy
** Initial revision.
** 
**    Rev 1.14   10 Jun 1994 17:25:24   nghia
** Added check for reported error - Use E_NO_ERROR bit.
** 
**    Rev 1.13   11 Nov 1993 09:38:56   ron
** ppr 9071: focus on wrong thing let message box be bypassed.  Set focus to
** parent (fixes most situations, but should still provide a fn to pass in the
** actual window handle to use as the parent).
** 
**    Rev 1.12   19 Oct 1993 17:02:14   ron
** revised to call ErrGetFormattedErrorText instead of ErrGetErrorText (it was
** substituting "none" in for all %s arguments in ErrDisplayFormattedError).
** 
**    Rev 1.11   19 Oct 1993 14:18:32   ron
** small change to the last fix -- only use powerviews toolbar window handle
** if the one from GetFocus is NULL.
** 
**    Rev 1.10   07 Oct 1993 13:56:42   tom
** 8831: get parent handle from cliulib, allowing dialog when PV minimized
** 
**    Rev 1.9   06 Oct 1993 16:36:10   tom
** 8802, 8929: display memory verify info with memory verify error
** 
**    Rev 1.8   03 Aug 1993 15:09:44   ron
** ppr 8731, 8726 : wasn't formatting message when sent to shell
** 
**    Rev 1.7   27 Jul 1993 10:42:14   ron
** found another insidious bug -- wasn't masking out severity when comparing
** index for help entry point.
** 
**    Rev 1.6   21 Jul 1993 11:22:40   ron
** ppr8450: make error numbers and error types consistent: use error numbers
** for either INTERNAL errors or when reportErrCodes is TRUE.
** 
**    Rev 1.5   16 Jul 1993 08:57:58   tom
** ppr 8368: remove timestamp on critical errors
** 
**    Rev 1.4   28 Jun 1993 10:42:04   ron
** changes to get help buttons for all server errors
** 
**    Rev 1.3   10 Jun 1993 10:47:08   ron
** added an argument (buttonID return value) to ErrMessageBox.
** 
**    Rev 1.2   09 Jun 1993 17:01:42   ron
** PIP: translate error codes to rc offsets and help entry points
** 
**    Rev 1.1   03 Jun 1993 16:34:00   ron
** actor error consolidation -- working checkpoint
** 
**    Rev 1.0   25 May 1993 17:01:48   ron
** Initial revision.
** 
**  $Header:   S:/tbird/mt2_186/errtext/errfmt.c_v   1.0   16 Dec 1996 14:29:34   Judy  $
**
**  Copyright (C) 1993 Microtek International.  All rights reserved.
**
*****************************************************************************/


                  /****************************
                  *                           *
                  *      INCLUDE FILES        *
                  *                           *
                  ****************************/
#ifndef _BASEWIND_
#include "basewind.h"
#endif
#include <stdio.h>
#include <string.h>

#ifndef _MSGBOXEX_
#include "msgboxex.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif
#ifndef _CLIULIB_
#include "cliulib.h"
#endif
#ifndef _HEAP_
#include "heap.h"
#endif
#ifndef _ERRTEXT_
#include "errtext.h"
#endif
#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif
#ifndef _ERRCODEC_
#include "errcodec.h"
#endif
#ifndef _ERRHELP_
#include "errhelp.h"
#endif
#ifndef _ERRHLPTR_
#include "errhlptr.h"
#endif
#ifndef _HLPENTRY_
#include "hlpentry.h"
#endif
                  /****************************
                  *                           *
                  *    LOCAL DEFINITIONS      *
                  *                           *
                  ****************************/

STATIC U16 wExType; /* Type mask for extended styles */
STATIC U16 wType;   /* Type mask for normal styles */
FARPROC lpfErrHelpLauncher; /* MessageBox procedure addr */
HINSTANCE ErrhInstnce; /* Application instance handle */
DWORD ErrHelpEntry;
CHAR memoryVerifyInfo[E_ERRSIZE] = { '\0' };
   /* info to include in mem verify err msg */


                  /****************************
                  *                           *
                  *   EXTERNAL VARIABLES      *
                  *                           *
                  ****************************/

extern S16 ErrSendMessageToCLI(CHAR *);
extern HANDLE ErrLookupModule(U16 moduleID);
extern BOOL reportErrCodes;

                  /****************************
                  *                           *
                  *    LOCAL PROTOTYPES       *
                  *                           *
                  ****************************/

LONG FAR PASCAL ErrHelpLauncher(HWND, UINT, WPARAM, LPARAM);

                  /****************************
                  *                           *
                  *     EXECUTABLE CODE       *
                  *                           *
                  ****************************/

/* 
** Note on memory allocation.  All error text retrieved is loaded into
** a newly globally allocated buffer.  By being global, it can be passed
** to the CLI.  A single static buffer won't work because it will get
** overwritten when more than a single message box is up (and probably
** crash Windows).
*/

/*****************************************************************************
**
** ErrErrorToHelp
**
** Description:
**   Return the help entry point corresponding to the error code.
** Parameters:
**   errCode (in): encoded module, error type, and error number
**   hlpEntry(out): help entry to use
**
*****************************************************************************/
RETCODE EXPORT ErrErrorToHelp(RETCODE errCode, DWORD *hlpEntry) {
   U16 localHelpEntry;
   U16 maxTransOffset;
   U16 transOffset;
   /* 
    * The automatically generated header file "errhlptr.h" includes an
    * array of structs that translate error codes to help entry points.
    */
   maxTransOffset = MAX_HELP_TRANS;
   for (transOffset = 0; transOffset < maxTransOffset; transOffset++) {
      if (E_ERRORID(ErrHelpTranslation[transOffset].errcode) == 
            E_ERRORID(errCode)) {
         localHelpEntry = ErrHelpTranslation[transOffset].helpEntry;
         break;
      }
   }
   if (INTERNAL_ERROR(errCode)) {
      *hlpEntry = HE_INTERNAL_ERROR;
   }
   else {
      if (transOffset < maxTransOffset) { /* found in errhlptr.h */
         *hlpEntry = localHelpEntry;
      }
      else {
         *hlpEntry = ER_HELP_BASE + E_ERRTEXT_OFFSET + 
            E_ERRORID(errCode);
      }
   }
   return (GOOD);
}

/*****************************************************************************
**
** ErrDisplayFormattedError
**
** Description:
**   Decipher the error code, and display the error text either via CLI or 
**   a dynamically sized popup dialog, depending on the run mode.  The args
**   are optional strings to plug into the error text, if the error text
**   contains %s placeholders.  If a dialog box is used, the two buttons
**   are "&OK" and "&Help", and the help entry point is passed in as an
**   argument.
**
** Parameters:
**   errCode (in): encoded module, error type, and error number
**   displayMode (in): FORCE_POPUP, FORCE_CLI, or CHECK_MODE
**   arg1 (in): optional sprintf argument
**   arg2 (in): optional sprintf argument
**   arg3 (in): optional sprintf argument
**
*****************************************************************************/
RETCODE EXPORT ErrDisplayFormattedError(RETCODE errCode, 
   HANDLE_ERROR displayMode, LPSTR arg1, LPSTR arg2, LPSTR arg3,
   U16 style, S16 FAR *buttonID) {

   HANDLE hErrText;
   LPSTR lpErrText;
   HANDLE hCLIModule;
   U32 severityID;
   S16 moduleID;
   S8 tmbuf[E_ERRSIZE];
   HWND parent;
   static U16 callCount = 0;
   RETCODE err;

    /* 06/10/94 - Nghia
    ** If error code is already reported, just return
    ** Detect reported error
    */
    if (E_SEVERITYID(errCode) == E_NO_ERROR)
       return GOOD;
   /*
    * It's very unlikely this will be called while it's in the midst
    * of processing another error, but it could cause some havoc if it
    * is.  We just throw away errors if we're processing one already.
    */
   if (callCount > 0) {
      return (GOOD);
   }
   callCount++;
   /*
    * Beware of a loop -- if we return an error here, some caller could
    * potentially check return status and call back.  We will make sure
    * that ER_ERCODE_NOT_FOUND is only returned if the argument in the
    * call is not ER_ERCODE_NOT_FOUND, and otherwise return GOOD immediately
    * if an error code is passed in that we generate.
    */
   if (errCode == ER_OUT_OF_MEMORY || errCode == ER_WINDOWS_MEMLOCK) {
      callCount--;
      return (GOOD); /* prevent loop */
   }

/*----------------------------------------------------------------------*
 * NOTE: GlobalAlloc used because of TMalloc load dependency problems   *
 *----------------------------------------------------------------------*/

   if ((hErrText = GlobalAlloc(GHND, E_ERRSIZE)) == NULL) {
      callCount--;
      return (ER_OUT_OF_MEMORY);
   }
   if ((lpErrText = (LPSTR)GlobalLock(hErrText)) == NULL) {
      GlobalFree(hErrText);
      callCount--;
      return (ER_WINDOWS_MEMLOCK);
   }

   ErrErrorToHelp(errCode, &ErrHelpEntry);
   if (ErrGetFormattedErrorText(errCode, lpErrText, arg1, arg2, arg3) != GOOD) {
      GlobalUnlock(hErrText);
      GlobalFree(hErrText);
      callCount--;
      if (errCode == ER_ERCODE_NOT_FOUND) {
         return (GOOD); /* prevent loop */
      }
      else {
         return (ER_ERCODE_NOT_FOUND);
      }
   }
   
   /* Format base error text; special processing for memory verify */
   if (errCode == ER_MEMORY_VERIFY) {
      sprintf(tmbuf, lpErrText, memoryVerifyInfo);
      memoryVerifyInfo[0] = '\0';
   }
   else {
      sprintf(tmbuf, lpErrText, arg1, arg2, arg3);
   }

   /* now decide where to display */
   if (displayMode == CHECK_MODE)
      CLIGetActive(&hCLIModule);
   if (displayMode == FORCE_POPUP ||
      (displayMode == CHECK_MODE && hCLIModule == NULL)) {

      /* decide which icon to use */
      severityID = errCode & E_SEVERITY_MASK;

      /* revised standard type of error dialog to be OK since we
         don't field the user's button pressed */
      if (style == 0) {
         wType = MB_OK;
      }
      else {
         wType = style;
      }
      if (severityID == E_INFORMATIVE) {
         wType |= MB_ICONINFORMATION;
      }
      else if (severityID == E_WARNING) {
         wType |= MB_ICONEXCLAMATION;
      } 
      else {  /* severe/fatal/catastrophic */
         wType |= MB_ICONHAND;
      }

      lstrcpy(lpErrText, tmbuf);
      if (reportErrCodes) {
         lstrcat(tmbuf, " (0x%lX)");
         sprintf(lpErrText, tmbuf, errCode);
      }

      /* get module ID for caption */
      moduleID = E_MODULEID(errCode);
      if (ErrLookupModule(moduleID) == NULL) {
         GlobalUnlock(hErrText);
         GlobalFree(hErrText);
         callCount--;
         return (ER_ERCODE_NOT_FOUND);
      }
      wExType |= MB_EX_HELP;
      /* 
       * If we allow multiple instances, we will have to actually pass in
       * the instance handle in order to get this to work correctly. 
       */
      ErrhInstnce = 0; 
      lpfErrHelpLauncher = 
         MakeProcInstance((FARPROC)ErrHelpLauncher, ErrhInstnce);

      /* get the toolbar's handle */
      err = GOOD;
      if ((parent = GetFocus()) == NULL) {
         err = GetToolBarHandle(&parent);
      }
      else if (GetParent(parent) != NULL) {
         parent = GetParent(parent);
      }

      /* don't bring up message box if there's an error */
      if (err == GOOD) {
         *buttonID = MessageBoxEx(parent, (LPSTR) lpErrText, ERROR_CAPTION, 
            wType, wExType, (WNDPROC)lpfErrHelpLauncher);
      }
   } 
   else {
      lstrcpy(lpErrText, tmbuf);
      if (reportErrCodes) {
         lstrcat(tmbuf, " (0x%lX)");
         sprintf(lpErrText, tmbuf, errCode);
      }
      ErrSendMessageToCLI(lpErrText);
   }
   GlobalUnlock(hErrText);
   GlobalFree(hErrText);
   FreeProcInstance(lpfErrHelpLauncher);
   callCount--;

   return (GOOD);
}  /* ErrDisplayFormattedError */

/*****************************************************************************
**
** ErrHelpLauncher
**
** Description:
**   This is a call-back function from the MessageBoxEx() function.  It
**   can actually respond to any windows message, but we will only use
**   it to start Help.
**
** Parameters:
**   hwnd <in>: window handle of the dialog box
**   message <in>: windows message to process (we only do IDEXHELP)
**   wParam <in>: unused
**   lParam <in>: unused
**
*****************************************************************************/

LONG FAR PASCAL ErrHelpLauncher(HWND hwnd, UINT message,
   WPARAM wParam, LPARAM lParam)
{
   long dummy;
   dummy = lParam + 1;
   switch(message) {
      case WM_COMMAND:
         if(wParam == IDEXHELP)
            WinHelp(hwnd, "PWRVIEWS.HLP",
               HELP_CONTEXT, ErrHelpEntry);
         break;
   }
   return((long) (lParam == dummy));
} /* HelpLauncher */

/*****************************************************************************
** ErrGetFormattedErrorText
**
** Description:
**   Retrieve error text and plug in optional arguments.  The *caller* is 
**   expected to have allocated storage for the return string of size 
**   E_ERRSIZE.
** Parameters:
**   errCode (in): encoded module, error type, and error number
**   lperrString (in): caller-allocated buffer
**   arg1 (in): optional sprintf argument
**   arg2 (in): optional sprintf argument
**   arg3 (in): optional sprintf argument
*****************************************************************************/
RETCODE EXPORT ErrGetFormattedErrorText(RETCODE errorCode, LPSTR lperrString,
   LPSTR arg1, LPSTR arg2, LPSTR arg3) {

   RETCODE err;
   HANDLE hErrText;
   LPSTR lpErrText;
   S8 tmbuf[E_ERRSIZE];
   RETCODE errorComponent;

   if ((hErrText = GlobalAlloc(GHND, E_ERRSIZE)) == NULL)
      return (ER_OUT_OF_MEMORY);
   if ((lpErrText = (LPSTR)GlobalLock(hErrText)) == NULL) {
      GlobalFree(hErrText);
      return (ER_WINDOWS_MEMLOCK);
   }
   /* Get the error components */
   errorComponent = errorCode;
   if ((err = GetErrorComponents(&errorComponent, lpErrText)) != GOOD) {
      GlobalFree(hErrText);
      return(err);
   }
   /* Format the string to display; special processing for memory verify */
   if (errorCode == ER_MEMORY_VERIFY) {
      sprintf(tmbuf, lpErrText, memoryVerifyInfo);
      memoryVerifyInfo[0] = '\0';
   }
   else {
      sprintf(tmbuf, lpErrText, arg1, arg2, arg3);
   }
   if (INTERNAL_ERROR(errorComponent)) {
      sprintf(lperrString, I_STR_TEMPLATE, tmbuf, errorComponent);
   }
   else if (reportErrCodes) {
      sprintf(lperrString, STR_TEMPLATE_WITH_CODE, tmbuf, errorComponent);
   }
   else {
      sprintf(lperrString, STR_TEMPLATE, tmbuf);
   }
   return (GOOD);
}  /* ErrGetFormattedErrorText */

/*****************************************************************************
**
** ErrMessageBox
**
** Description:
**   Bring up a message box (including a help button) using the provided
**   caption, text, and style (buttons), and pass back the ID of the button
**   that was pressed.
**
** Parameters:
**   caption (in): caption of the dialog box
**   text (in): text of the message
**   style (in): which buttons (besides Help) to use
**   helpEntry (in): the entry point into pwrviews.hlp
**   buttonID (out): the ID of the button that was pressed
**
**
*****************************************************************************/
RETCODE EXPORT ErrMessageBox(LPSTR caption, LPSTR text, U16 style,
                             DWORD helpEntry, S16 FAR *buttonID) {

   HWND parent;
   static U16 callCount = 0;
   RETCODE err;

   /*
    * It's very unlikely this will be called while it's in the midst
    * of processing another message, but it could cause some havoc if it
    * is.  We just throw away others if we're processing one already.
    */
   if (callCount > 0) {
      return (GOOD);
   }
   callCount++;

/*----------------------------------------------------------------------*
 * NOTE: GlobalAlloc used because of TMalloc load dependency problems   *
 *----------------------------------------------------------------------*/

   wExType |= MB_EX_HELP;
   /* 
    * If we allow multiple instances, we will have to actually pass in
    * the instance handle in order to get this to work correctly. 
    */
   ErrhInstnce = 0; 
   lpfErrHelpLauncher = 
      MakeProcInstance((FARPROC)ErrHelpLauncher, ErrhInstnce);
   ErrHelpEntry = helpEntry;

   /* get the toolbar's handle */
   err = GOOD;
   if ((parent = GetFocus()) == NULL) {
      err = GetToolBarHandle(&parent);
   }
   else if (GetParent(parent) != NULL) {
      parent = GetParent(parent);
   }

   /* don't bring up message box if there's an error */
   if (err == GOOD) {
      *buttonID = MessageBoxEx(parent, text, caption, style,
         wExType, (WNDPROC)lpfErrHelpLauncher);
   }

   FreeProcInstance(lpfErrHelpLauncher);
   callCount--;

   return (GOOD);
}  /* ErrMessageBox */

/***************************************************************************
**
**  ErrSaveMemoryVerifyInfo
**
**  Status:  TESTED
**
**  description:
**     Save memory verification text for later display.
**
**  input:
**     memoryVerifyString:  memory verification text string.
**
**  output:
**     none
**
*****************************************************************************/
RETCODE EXPORT ErrSaveMemoryVerifyInfo(LPSTR memoryVerifyString) {

   lstrcpy((LPSTR)memoryVerifyInfo, memoryVerifyString);
   return (GOOD);
}

/************************************EOF************************************/
