// Jet3d - JMiniApp
// Filename:error_manager.cpp
/***************************************************************
Copyright (C) 2000 Novasoft Consulting

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

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*****************************************************************/



#include "jmini_app.h"




/*****************************************************************************/
/*                             PROCESSING FUNCTIONS                          */
/*****************************************************************************/ 


/*----------------------------------------------------------------------------
  Name:		GetTextForID(HRESULT ahr_id, char *ac_error)
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::GetTextForID(HRESULT ahr_id, char *ac_error)
{
 FuncResult	funcResult;
 char		*lc_temp;
 char		lc_version[MAX_DXVERSION_STRING];
 bool		lb_cleanup;

 // Init.
 lb_cleanup = false;
 lc_temp = NULL; 


 switch (ahr_id)
 {
  // Also includes DD_OK, D3D_OK and D3DRM_OK.
  case FUNCR_OK:
   lc_temp = TEXT("The request completed successfully");
   break;
    
  // Application defined errors
  case FUNCR_GENERAL:
   lc_temp = TEXT("A general error occured");
   break;

  case FUNCR_UNDEFRESULT:
   lc_temp = TEXT("A funtion did not update the return code parameter");
   break;
  
  case FUNCR_OBJECTNOTCREATED:
   lc_temp = TEXT("An attempted was made to use a NULL object");
   break;

  case FUNCR_NOTIMPLEMENTED:
	lc_temp = TEXT("This feature has yet to be implemented");
   break;
	
  case FUNCR_BITMAPLOADFAILED:
	lc_temp = TEXT("Could not load bitmap file");
   break;

  case FUNCR_COULDNOTALLOCMEM:
	lc_temp = TEXT("Out of system memory");
   break;
   
  case FUNCR_OBJECTNOTINITIALIZED:
	lc_temp = TEXT("Tried to use an uninitialized object");
   break;

  case FUNCR_OBJECTALREADYINITIALIZED:
	lc_temp = TEXT("Tried to initialize an initialized object");
   break;

  case FUNCR_UNKNOWNOBJECTTYPE:
	lc_temp = TEXT("Unknown object type found");
   break;
  
  case FUNCR_INVAILDARGS:
	lc_temp = TEXT("Invalid arguments were passed to a function");
   break;

  case FUNCR_OUTOFARRAYSPACE:
	lc_temp = TEXT("Out of mememory");
   break;
  
  case FUNCR_COULDNOTFINDELEMENT:
	lc_temp = TEXT("Could not find element");
   break;

  case FUNCR_SERVICENOTRUNNING:
   lc_temp = TEXT("A request was issued to a service that had not been started");
   break;

  case FUNCR_SERVICEALREADYRUNNING:
   lc_temp = TEXT("An attempt was made to start a running service");
   break;
 
  case FUNCR_INVALIDINISETTINGS:
   lc_temp = TEXT("Invalid INI file settings, please re-run the Level Selector");
   break;
   

  case FUNCR_NOTATREQUIREDDXLEVEL: 
	lc_temp = new char[MAX_ERROR_TXT];

	GetDxString(&funcResult, REQUIRED_DX, &lc_version[0]);
	if(funcResult.GetFailed())
	 strcpy(lc_temp, funcResult.ic_userTxt);
    else
	{
	 strcpy(lc_temp, "You must have at least DirectX ");
	 strcat(lc_temp, lc_version);
	 strcat(lc_temp, " installed to run this application");
	}
	
	lb_cleanup = true;
	break;
  
 
 
  default:
   // Unknown DD/D3D/D3DRM/App Error
   lc_temp = TEXT("An unknown error occured");
   break;
 }

 
 // Update passed string.
 strcpy(ac_error, lc_temp);

 if(lc_temp && lb_cleanup)
  delete lc_temp;
} 

/*----------------------------------------------------------------------------
  Name:		GetDxString(FuncResult *funcResult, DWORD adw_msg, char *ac_msg)
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::GetDxString(FuncResult *funcResult, DWORD adw_msg, char *ac_msg)
{
 char		*lc_temp;

 // Init.
 funcResult->SetUndef();
 
  switch(adw_msg)
  {
   case 0:
     // This means DirectX isn't installed or isn't installed properly.	
	 lc_temp = TEXT("0.0");
	 break;

   case 0x100:
    lc_temp = TEXT("1.0");
    break;

   case 0x200:
    lc_temp = TEXT("2.0");
    break;
	
	case 0x300:
    lc_temp = TEXT("3.0");
    break;
   
   case 0x500:
    lc_temp = TEXT("5.0");
    break;
	
   case 0x600:
    lc_temp = TEXT("6.0");
    break;

   case 0x700:
    lc_temp = TEXT("7.0");
    break; 

   default:
	// We weren't expecting this!
	funcResult->SetError("Unknown version of DirectX installed");
	ac_msg = NULL;
	goto ReturnOut;
  }



 // Update passed string.
 strcpy(ac_msg, lc_temp);
 
  
ReturnOut:
 return;
}

/*----------------------------------------------------------------------------
  Name:		LogError(FuncResult *funcResult, const FuncResult *errorInfo)
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::LogError(FuncResult *funcResult, const FuncResult *errorInfo)
{  
 char			lc_logMsg[MAX_LOG_MSG];
 char			lc_errorMsg[MAX_ERROR_TXT];
 char			lc_lineNum[5];
 LogMessageType	logMsgType;
 
 // Init.
 funcResult->SetUndef();

 
 if(errorInfo->ic_userTxt)
 {
  strcpy(lc_logMsg, "\nUser Text: ");
  strcat(lc_logMsg, errorInfo->ic_userTxt);
  strcat(lc_logMsg, "\nGenerated error text: ");
 }
 else
  strcpy(lc_logMsg, "Generated Error Text: ");



 // We will always have an error id.
 GetTextForID(errorInfo->ihr_msgId, &lc_errorMsg[0]);
 strcat(lc_logMsg, lc_errorMsg);
 

 // See if we have any additional info.
 if(errorInfo->ic_errorFileString)
 {
  strcat(lc_logMsg, "\nFile name: ");
  strcat(lc_logMsg, errorInfo->ic_errorFileString);

  if(errorInfo->ii_lineNum != 0)
  {
   strcat(lc_logMsg, "\nLine number: ");
   wsprintf(lc_lineNum, "%d", errorInfo->ii_lineNum); 
   strcat(lc_logMsg, lc_lineNum);
  }
 }



 // Set the type of log message.
 if(errorInfo->GetInitError())
  logMsgType = INIT_ERROR;
 else
  logMsgType = RUNTIME_ERROR;


 LogMessage(funcResult, lc_logMsg, logMsgType);
 if(funcResult->GetFailed())
  goto ReturnOut;

 // Ok.
 funcResult->SetOk();

ReturnOut:
 return;
} 

/*----------------------------------------------------------------------------
  Name:		LogMessage(FuncResult *funcResult, char *ac_msg, LogMessageType logMsgType)
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::LogMessage(FuncResult *funcResult, char *ac_msg, LogMessageType logMsgType)
{
 SYSTEMTIME time;
 char	lc_msg[MAX_LOG_MSG];	
 char	lc_time[MAX_LOG_MSG];


 // Init.
 funcResult->SetUndef();
 

 // Get the current time.
 GetLocalTime(&time);
 sprintf(lc_time, "%u:%u:%u", time.wHour, time.wMinute, time.wSecond);  


 switch(logMsgType)
 {
  case INIT_ERROR:
   strcpy(lc_msg, "\n\nINITIALIZATION ERROR LOG - ");
   strcat(lc_msg, lc_time);
   break;
    
  case RUNTIME_ERROR:
   strcpy(lc_msg, "\n\nRUNTIME ERROR LOG - ");
   strcat(lc_msg, lc_time);
   break;

  case NON_ERROR:
   strcpy(lc_msg, "\n\nLOG MESSAGE - ");
   strcat(lc_msg, lc_time);
   break;

  default:
   // error
   funcResult->SetError(FUNCR_INVAILDARGS, __LINE__, __FILE__);
   goto ReturnOut;
 }

 // Add the actual msg to the msg type. 
 strcat(lc_msg, ac_msg);
 
 WriteToLog(funcResult, lc_msg);
 if(funcResult->GetFailed())
  goto ReturnOut;
	
 

 // Ok.
 funcResult->SetOk();

ReturnOut:
 return;
}

/*----------------------------------------------------------------------------
  Name:		WriteToLog(FuncResult *funcResult, char *ac_msg)
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::WriteToLog(FuncResult *funcResult, char *ac_msg)
{
 DWORD	ldw_bytesWritten;

 // Init.
 funcResult->SetUndef();

 if(WriteFile(fileHandle, ac_msg, strlen(ac_msg), &ldw_bytesWritten, NULL) == 0)
 {
  funcResult->SetError("Could not write to log file", __LINE__, __FILE__);
  goto ReturnOut;
 }

 // Ok.
 funcResult->SetOk();

ReturnOut:
 return;
}


/*****************************************************************************/
/*							 INTERFACE FUNCTIONS      	          		     */
/*****************************************************************************/
ErrorManager::ErrorManager(void)
{
 ib_logging = false;
 fileHandle = NULL;
}

ErrorManager::~ErrorManager(void)
{
 if(fileHandle)
 {
  CloseHandle(fileHandle);
  fileHandle = NULL;
 }
}

/*----------------------------------------------------------------------------
  Name:		StartLogging(FuncResult *funcResult)
  Purpose:	Create new log file.		
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::StartLogging(FuncResult *funcResult)
{
 SYSTEMTIME time;
 char		lc_fileName[MAX_PATH];
 char		lc_pathName[MAX_PATH];
 char		lc_startMsg[MAX_LOG_MSG];
 char		lc_time[MAX_LOG_MSG];
 DWORD		ldw_errorCode;

 
 if(ib_logging)
 {
  funcResult->SetError("Error Manager is already logging", __LINE__, __FILE__);
  goto ReturnOut;
 }

 
 if(CreateDirectory(LOG_FILE_DIR, NULL) == 0)
 {
  ldw_errorCode = GetLastError();
   
  if(ldw_errorCode != ERROR_ALREADY_EXISTS)
  {
   funcResult->SetError("Could not create file logging directory", __LINE__, __FILE__);
   goto ReturnOut;
  }
 }

 GetLocalTime(&time);

 strcpy(lc_pathName, LOG_FILE_DIR);
 strcat(lc_pathName, "\\");

 sprintf(lc_fileName, "%u_%u_%u %u_%u_%u", time.wDay, time.wMonth, 
	     time.wYear, time.wHour, time.wMinute, time.wSecond);
 strcat(lc_fileName, ".doc");

 strcat(lc_pathName, lc_fileName);
 
 
 fileHandle = CreateFile(lc_pathName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 if(fileHandle == INVALID_HANDLE_VALUE)
 {
  funcResult->SetError("Could not create file for logging", __LINE__, __FILE__);
  goto ReturnOut;
 }

 // Write start time to log file.
 strcpy(lc_startMsg, "**************************Logging Started - ");
 sprintf(lc_time, "%u:%u:%u", time.wHour, time.wMinute, time.wSecond);  
 strcat(lc_startMsg, lc_time);
 strcat(lc_startMsg, "**************************");

 WriteToLog(funcResult, lc_startMsg);
 if(funcResult->GetFailed())
  goto ReturnOut;


 // Ok.
 funcResult->SetOk();
 ib_logging = true; // We are now logging all errors.

ReturnOut:
 return;
}

/*----------------------------------------------------------------------------
  Name:		StopLogging(FuncResult *funcResult)
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
void
ErrorManager::StopLogging(FuncResult *funcResult)
{
 char		lc_msg[MAX_LOG_MSG];
 char		lc_time[MAX_LOG_MSG];
 SYSTEMTIME	time;


 // Make sure we're logging.
 if(!ib_logging)
 {
  funcResult->SetError("The Error Manager is not logging", __LINE__, __FILE__);
  goto ReturnOut;
 }


 // Write stop time to log file.
 GetLocalTime(&time);

 
 strcpy(lc_msg, "\n\n**************************Logging Stopped - ");
 sprintf(lc_time, "%u:%u:%u", time.wHour, time.wMinute, time.wSecond);  
 strcat(lc_msg, lc_time);
 strcat(lc_msg, "**************************");

 WriteToLog(funcResult, lc_msg);
 if(funcResult->GetFailed())
  goto ReturnOut;



 // Ok.
 funcResult->SetOk();
 ib_logging = false; // We are not logging errors.

ReturnOut:
 return;
}


/*----------------------------------------------------------------------------
  Name:		FatalError(FuncResult *errorInfo)
  Purpose:
  Notes:	    

  1. We won't even try to handle any errors caused by trying to handle an error
     because we'd just get stuck in a looooooooop.			
----------------------------------------------------------------------------*/
void		
ErrorManager::FatalError(FuncResult *errorInfo)
{
 FuncResult funcResult;
 char		lc_errorMsg[MAX_ERROR_TXT];
 char		lc_errorTxt[MAX_ERROR_TXT];
 char		lc_lineNum[5];


 if(ib_logging)
  LogError(&funcResult, errorInfo);
 

 if(errorInfo->ic_userTxt)
 {
  strcpy(lc_errorTxt, "User Text: ");
  strcat(lc_errorTxt, errorInfo->ic_userTxt);
  strcat(lc_errorTxt, "\n\nGenerated Error Text:\n\t");
 }
 else
  strcpy(lc_errorTxt, "Generated Error Text:\n\t");


 // We will always have an error id.
 GetTextForID(errorInfo->ihr_msgId, &lc_errorMsg[0]);
 strcat(lc_errorTxt, lc_errorMsg);
 

 // See if we have any additional info.
 if(errorInfo->ic_errorFileString)
 {
  strcat(lc_errorTxt, "\n\tFile name: ");
  strcat(lc_errorTxt, errorInfo->ic_errorFileString);

  if(errorInfo->ii_lineNum != 0)
  {
   strcat(lc_errorTxt, "\n\tLine number: ");
   wsprintf(lc_lineNum, "%4d", errorInfo->ii_lineNum); 
   strcat(lc_errorTxt, lc_lineNum);
  }
 }

 worldManager->QuitWithError(lc_errorTxt, errorInfo->GetInitError());
}

/*----------------------------------------------------------------------------
  Name:		Log(char *ac_txt)  
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
HRESULT
ErrorManager::Log(char *ac_txt)  
{  
 HRESULT	lhr_result;
 FuncResult funcResult;
 char		lc_logMsg[MAX_LOG_MSG];
  
 
 // See if we are logging.
 if(!ib_logging)
 {
  // NOTE -- It isn't an error if a request is made to log when we're not logging.
  // This is because it's just a service than can be switch on or off by the app.
  lhr_result = FUNCR_OK;
  goto ReturnOut;
 }

 strcpy(lc_logMsg, "\nUser Text: ");

 if(ac_txt)
  strcat(lc_logMsg, ac_txt);
 

 
 LogMessage(&funcResult, lc_logMsg, NON_ERROR);
 if(funcResult.GetFailed())
 {
  lhr_result = funcResult.ihr_msgId;
  goto ReturnOut;
 }

 // Ok.
 lhr_result = FUNCR_OK;

ReturnOut:
 return lhr_result;
}  
 
/*----------------------------------------------------------------------------
  Name:		Log(char *ac_txt, char *ac_txt2) 
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/

HRESULT
ErrorManager::Log(char *ac_txt, char *ac_txt2)  
{  
 HRESULT	lhr_result;
 FuncResult funcResult;
 char		lc_logMsg[MAX_LOG_MSG];
  
 
 // See if we are logging.
 if(!ib_logging)
 {
  // NOTE -- It isn't an error if a request is made to log when we're not logging.
  // This is because it's just a service than can be switch on or off by the app.
  lhr_result = FUNCR_OK;
  goto ReturnOut;
 }

 strcpy(lc_logMsg, "\nUser Text: ");

 if(ac_txt)
  strcat(lc_logMsg, ac_txt);
 
 if(ac_txt2)
  strcat(lc_logMsg, ac_txt2);
 
 
 LogMessage(&funcResult, lc_logMsg, NON_ERROR);
 if(funcResult.GetFailed())
 {
  lhr_result = funcResult.ihr_msgId;
  goto ReturnOut;
 }

 // Ok.
 lhr_result = FUNCR_OK;

ReturnOut:
 return lhr_result;
}  
 
/*----------------------------------------------------------------------------
  Name:		Log(char *ac_txt, char *ac_txt2, char *ac_txt3)   
  Purpose:
  Notes:      

  1.  
----------------------------------------------------------------------------*/
HRESULT
ErrorManager::Log(char *ac_txt, char *ac_txt2, char *ac_txt3)  
{  
 HRESULT	lhr_result;
 FuncResult funcResult;
 char		lc_logMsg[MAX_LOG_MSG];
  
 
 // See if we are logging.
 if(!ib_logging)
 {
  // NOTE -- It isn't an error if a request is made to log when we're not logging.
  // This is because it's just a service than can be switch on or off by the app.
  lhr_result = FUNCR_OK;
  goto ReturnOut;
 }

 strcpy(lc_logMsg, "\nUser Text: ");

 if(ac_txt)
  strcat(lc_logMsg, ac_txt);
 
 if(ac_txt2)
  strcat(lc_logMsg, ac_txt2);

 if(ac_txt3)
  strcat(lc_logMsg, ac_txt3);
 

 
 LogMessage(&funcResult, lc_logMsg, NON_ERROR);
 if(funcResult.GetFailed())
 {
  lhr_result = funcResult.ihr_msgId;
  goto ReturnOut;
 }

 // Ok.
 lhr_result = FUNCR_OK;

ReturnOut:
 return lhr_result;
}  


