/*  Project Tec3270

	 Copyright © 1998-2005. All Rights Reserved.

	 SUBSYSTEM:    WHllApi.dll library
	 FILE:         whllcli.cpp
	 AUTHOR:       Alcindo Ferreira Filho


	 OVERVIEW
	 ========
	 Source file for implementing  Class TecWHllClient
*/

#include "whllall.h"

#ifndef _WIN32
#define MAX_PATH MAXPATH
#endif

// Diagnostics
DIAG_DEFINE_GROUP(TecWHllClient,1,0);

TecWHllClient::TecWHllClient(HINSTANCE dllInstance, ALFFDate::DateFormat fmt) :
      ALFFDdeClient("TEC3270 Server", "WHllApi")
{
 dateFormat = fmt;

 itemWHllCommand = DdeString("WHllCommand");
 wHllInstance = dllInstance;

 wHllAppOptions = new TecWHLLAppParms();
 hddeBuffer = NULL;
 pTrcFile = NULL;
 commandCount = 0;
 char description[STRINGMSGLEN+1];
 ::LoadString(wHllInstance,IDS_WHLLAPIDESCRIPTION, description, STRINGMSGLEN);

 wHllDescription = new char[strlen(description)];
 strcpy (wHllDescription, description);

 SetCancelBlockingCall(false);
 currentCmd = 0;
}

TecWHllClient::~TecWHllClient()
{
 FreeString(itemWHllCommand);
 delete wHllAppOptions;
 if (pTrcFile != NULL)
  {
	char tempChar[STRINGMSGLEN+1];
	char crlf[3] = {CR, LF, 0x00};
	pTrcFile->LogText(crlf, 2);
	::LoadString(wHllInstance,IDS_ALFFWHLLTRACEEND, tempChar, STRINGMSGLEN);
	pTrcFile->LogText(tempChar, strlen(tempChar));
	pTrcFile->Close();
	delete pTrcFile;
  }
 if (hddeBuffer)
   delete [] hddeBuffer;
}

bool
TecWHllClient::WHllCleanup()
{
  // Quando o TecWHll32 sai sem executar o Cleanup ele termina OK !!
  // (Somente quando executa SetSessParm com TRON e em seguida cancela !!)
  // Verificar se não é o caso de estar ocorrendo alguma coisa no close do
  // TFile (mudou no OWL6 todo !!) ou se na deleçao do pTRCFIle ocorre alguma
  // coisa indevida !!
 if ( (wHllAppOptions->traceOn)
  &&  (pTrcFile) )
  {
    // Trace Option is on and Trace File is available
    char wHllMessage[STRINGMSGLEN+1];
    char tempChar[STRINGMSGLEN+1];
    char crlf[3] = {CR, LF, 0x00};

    // WHLLAPI Cleanup Trace Message:
    ::LoadString(wHllInstance,IDS_ALFFWHLLCLEANUP,tempChar,STRINGMSGLEN);
    pTrcFile->LogText(crlf, 2);
    pTrcFile->SetLogTitle(tempChar, dateFormat);

    if (commandCount == 1)
      ::LoadString(wHllInstance,IDS_ALFFWHLLONECMD, tempChar, 32);
    else ::LoadString(wHllInstance,IDS_ALFFWHLLCMDSQTY, tempChar, 32);
    sprintf(wHllMessage,tempChar, commandCount);
    pTrcFile->LogText(wHllMessage, strlen(wHllMessage));
    pTrcFile->LogText(crlf, 2);

    ::LoadString(wHllInstance,IDS_ALFFWHLLTRACEEND, tempChar, STRINGMSGLEN);
    pTrcFile->LogText(tempChar, strlen(tempChar));
    pTrcFile->Close();
    delete pTrcFile;
    pTrcFile = NULL;

    wHllAppOptions->traceOn = false;
    wHllAppOptions->fPause  = true;
  }
 return true;
}


bool
TecWHllClient::ExecuteOnServer(LPWORD lpwCommand, LPSTR lpbyString,
                               LPWORD lpwLength, LPWORD lpwReturnCode)
{
 /*
  * We need to build the HDDEDATA command with the correct parameters,
  * depending upon Win HLLAPI function that was requested and SendCommand
  * to be processed by the server.
  *
  * After sending the command to Tec3270 Server, we need to get the
  * response data from it via GetResultOnServer (RequestData).
  *
  * Buffer Layout for exchanging data with server:
  *
  *   Field Name:  Comm  Ret   Extd  Data      Data
  *                Code  Code  Error Length    Buffer Bytes
  *               |LL,HH|LL,HH|LL,HH|LL,HH|___________..........._|
  *               |-----|-----|-----|-----|-----------------------|
  *   Byte Pos.:  0     2     4     6     8                       n
  */

 WORD bufLen;

 // If this an function with no parms, bufLen must be 0(zero)
 if (hasParms)
	bufLen = *lpwLength;
 else bufLen = 0;

 if (hddeBuffer)
   delete [] hddeBuffer;
 hddeBuffer = new unsigned char[bufLen+DDEHDRBUFFERLEN];

 //Command Code (LL,HH) (bytes 0 e 1)
 memcpy(hddeBuffer+CCODEOFFSET, &*lpwCommand, 2);

 //Return Code or PS Position depending upon function
 if (lpwReturnCode)
	 memcpy(hddeBuffer+RETCODEOFFSET, &*lpwReturnCode, 2);
 else memset(hddeBuffer+RETCODEOFFSET,0x00,2);

 //Extended error from server - Set to 0(zero))
 memset(hddeBuffer+EXTDERROROFFSET, NULL, 2);

 //Text Length (may be 0(zero)) or other meaning...
 if (lpwLength)
	 memcpy(hddeBuffer+DATALENOFFSET, &*lpwLength, 2);
 else memset(hddeBuffer+DATALENOFFSET, 0x00, 2);

 //Text String
 if ( hasParms )
  memcpy(hddeBuffer+DATAOFFSET, lpbyString, *lpwLength);

 HDDEDATA makeData = MakeData(hddeBuffer, bufLen+DDEHDRBUFFERLEN, itemWHllCommand);

 delete [] hddeBuffer;
 hddeBuffer = NULL;

 return ALFFDdeClient::SendData(itemWHllCommand, makeData);
}

bool
TecWHllClient::GetResultOnServer(LPWORD lpwCommand, LPSTR lpbyString,
				 LPWORD lpwLength, LPWORD lpwReturnCode)
{
  HDDEDATA hDdeData = RequestData(itemWHllCommand);
  if (hDdeData)
   {
     bool success;
     int bufLen = GetData(hDdeData,0,0);
     if (bufLen)
      {
	if (hddeBuffer)
	 delete [] hddeBuffer;
	hddeBuffer = new unsigned char[bufLen+1];
	GetData(hDdeData, hddeBuffer, bufLen);
	// Checking for command integrity
	if (*lpwCommand == (WORD)*hddeBuffer)
	 {
	  memcpy(lpwReturnCode, hddeBuffer+RETCODEOFFSET, 2);
	  WORD extError;
	  memcpy(&extError, hddeBuffer+EXTDERROROFFSET, 2);
	  SetExtendedError(extError);
	  memcpy(lpwLength, hddeBuffer+DATALENOFFSET, 2);
	  if ((receiveString) && (*lpwLength > 0))
//			  &&(*lpwCommand != SETSESSIONPARAMETERS)
//           &&(*lpwCommand != SENDKEY) )
			 // SETSESSIONPARAMETERS return from Tec3270 server some
			 // WHllApi client options like TRON/TROFF;
			 //
          // SENDKEY alter the keystrokes sequence each time it
          // returns to server;
          //
          // these info must not be returned to caller.
           {
            memcpy(lpbyString, hddeBuffer+DATAOFFSET, *lpwLength);
           }
	  success = true;
	}
       else success = false;
      }
     else success = false;

     // We must free the HDDEDATA returned, even if there is no buflen !!
     FreeData(hDdeData);

     return success;
   }
  else return false;
}

WORD
TecWHllClient::WHllCommand(LPWORD lpwCommand, LPSTR lpbyString,
							  LPWORD lpwLength, LPWORD lpwReturnCode)
{
  bool  executedOk, mayUseEOT = false, parmError = false, endProcess;
  DWORD uTempoInicio, uTempoAtual, uTimeout;
  WORD   tempCommand = OEMFUNCTION;
  string tempString = "";
  WORD   stringOffset, receivedLen = 0, tempLen = 0;
  WORD   tempRC;

  // Some commands, like PAUSE has timeout, but other no,
  // so we should init timeout value to 0 and change it on
  // command processing on following switch block
  uTimeout = 0;

  commandCount++;

  // Need to guarantee that some obrigatory parms are present
  // (at least lpCommand and lpwReturnCode)
  if (lpwReturnCode == NULL)
	 {
	  tempRC = WHLLPARAMETERERROR;
	  if (lpbyString)
           {
             if (lpwLength)
                 tempString.assign(lpbyString,0,*lpwLength);
             else tempString.assign("??????");
           }
	  if (lpwCommand)
		 tempCommand = *lpwCommand;
	  if (lpwLength)
		 receivedLen = tempLen = *lpwLength;
	  parmError = true;
	 }
  else if (lpwCommand == NULL)
	 {
	  *lpwReturnCode = tempRC = WHLLPARAMETERERROR;
	  if (lpbyString)
           {
             if (lpwLength)
		 tempString.assign(lpbyString,0,*lpwLength);
             else tempString.assign("??????");
           }
	  if (lpwLength)
		 receivedLen = tempLen = *lpwLength;
	  parmError = true;
	 }
  else tempRC = *lpwReturnCode;


  if (!parmError)
	{
	 // At this point is guaranteed that lpwCommand and
	 // lpwReturnCode are present.

	 // Verify function code before doing some action
	 switch (*lpwCommand)
	  {
		// Following functions are already available.
		// Any other will return "WHLLNOTSUPPORTED".
		case     WAIT:
                            if (wHllAppOptions->waitPeriod == TWAIT)
                                uTimeout = 60000;  // 60 seconds
                            else if (wHllAppOptions->waitPeriod == LWAIT)
                                uTimeout  = -1;    // Wait Forever (until WHLLPSBUSY be cleared)
                            else if (wHllAppOptions->waitPeriod == NWAIT)
                                uTimeout  = 0;     // No Wait time
                            else  // something wrong has happened !!
                             {
                              *lpwReturnCode = WHLLSYSERROR;
                              SetExtendedError(TEC3270_WHLLINVALIDWAITOPT);
                              return WHLLSYSERROR;
                             }
                            uTempoInicio = GetTickCount();
                            mayUseEOT = false;
			    hasParms = false;
                            receiveString = false;
                            executedOk = true;
                            break;
		case     PAUSE:
			    if (lpwLength)
			       // Pause function specifies timeout on multiple of 0,5 seconds
                               uTimeout = (*lpwLength * 1000)/2;
                            else uTimeout = 0;
			    uTempoInicio = GetTickCount();
		case     QUERYCURSORLOC:
		case     SETCURSOR:
		case     DISCONNECTPS:
		case     RESETSYSTEM:
			    mayUseEOT = false;
			    hasParms = false;
                            receiveString = false;
			    executedOk = true;
			    break;

		case     STOPHOSTNOTIFICATION:
		case     QUERYHOSTUPDATE:
		case     CONNECTPS:
			    // Above functions has no obligation to inform string length
			    // that may default to 1 when it is null;
			    if (lpwLength == NULL)
			      tempLen = 1;
                            if (*lpwCommand == CONNECTPS)
			     {
			      // Just for CONNECTPS
			      uTempoInicio = GetTickCount();
			      uTimeout     = 25*1000;  // 25 seconds
			     }
		case     STARTHOSTNOTIFICATION:
		case     SETSESSIONPARAMETERS:
			    mayUseEOT = false;
			    hasParms = true;
                            receiveString = false;
               		    executedOk = true;
			    break;

		case     COPYPS:
		case     COPYPSTOSTR:
		case     QUERYCLIENTCERTIFICATE:
		case     QUERYSERVERCERTIFICATE:
			    mayUseEOT = false;
			    hasParms = true;
                            receiveString = true;
			    executedOk = true;
			    break;

		case     SENDKEY:
			    *lpwReturnCode = WHLLOK;
			    // Just for SENDKEY when session is busy
			    uTempoInicio = GetTickCount();
			    uTimeout     = 25*1000;  // 30 seconds
		case     COPYSTRTOPS:
			    mayUseEOT = true;
			    hasParms = true;
                            receiveString = false;
			    executedOk = true;
			    break;

		default:    executedOk = false;
			    break;
	  }

	 tempCommand = *lpwCommand;

	 if (!executedOk)
	  {
		*lpwReturnCode =  tempRC = WHLLNOTSUPPORTED;
		if (lpbyString)
                 {
                   if (lpwLength)
		      tempString.assign(lpbyString,0,*lpwLength);
                   else tempString.assign("??????");
                 }
		if (lpwLength)
		  tempLen = *lpwLength;
	  }
	 else if (hasParms)
	  {
		// Verifying Parms parameters Integrity
		if (lpbyString == NULL)
		 {
		  *lpwReturnCode =  tempRC = WHLLPARAMETERERROR;
		  if (lpwLength)
			receivedLen = tempLen = *lpwLength;
		  parmError = true;
		 }
		else
		 {
		  // Verify STRLEN/STREOT Options
		  if (mayUseEOT && wHllAppOptions->strEOT)
		   {
		    char far* eotPtr = (char*) _fmemchr(lpbyString, wHllAppOptions->eotChar, MAXBUFFERSIZE);
		    if (eotPtr)
		     {
			receivedLen = tempLen  = (WORD) (eotPtr-lpbyString);
			tempString.assign(lpbyString,0,tempLen);
		      }
		     else
		      {
			receivedLen = tempLen = 0;
			tempString.assign("");
			parmError = true;
		      }
                   }
		  else if (lpwLength)
			{
			 receivedLen = tempLen = *lpwLength;
			 tempString.assign(lpbyString,0,tempLen);
			}
		  else if (tempLen)
			 tempString.assign(lpbyString,0,tempLen);
		  //else tempLen = 0;  (if !tempLen,  it is 0 !!)
		 }
	  }
	 else // it hasn't parms
	  {
		if (lpwLength)
		  receivedLen = tempLen = *lpwLength;
		else
		 {
		  if (*lpwCommand == QUERYCURSORLOC)
			{
			 *lpwReturnCode =  tempRC = WHLLPARAMETERERROR;
			 executedOk = false;
			}
		  else receivedLen = tempLen = 0;
		 }
	  }
	}

  /////////////////////////////////////////////////////////////////////////////
  // If there is an trace option defined, trace the command just received
  if ( (wHllAppOptions->traceOn)
   &&  (pTrcFile) )
   {
    // Trace Option is on and Trace File is available
    char wHllMessage[STRINGMSGLEN+1];
    char tempChar[STRINGMSGLEN+1];
    char crlf[2] = {CR, LF};

    // Command Code Trace:
    char commandName[32];
    ::LoadString(wHllInstance,IDS_WHLLCOMMANDTRACE,tempChar,STRINGMSGLEN);
    ::LoadString(wHllInstance,WHLLAPICMDBASE+tempCommand, commandName, 32);
    sprintf(wHllMessage,tempChar, commandName);
    pTrcFile->LogText(crlf, 2);
    pTrcFile->LogText(wHllMessage, strlen(wHllMessage));

    // Parms Trace:
    if (hasParms)
     pTrcFile->LogData('S',tempString.c_str(), tempLen);

    // Parm Length or other meaning
    ::LoadString(wHllInstance,IDS_WHLLPARMLENGTHTRACE,tempChar,STRINGMSGLEN);
    sprintf(wHllMessage, tempChar, receivedLen);
    pTrcFile->LogText(wHllMessage, strlen(wHllMessage));

    //PS Position:
    ::LoadString(wHllInstance,IDS_WHLLPSPOSITIONTRACE,tempChar,STRINGMSGLEN);
    sprintf(wHllMessage, tempChar, tempRC);
    pTrcFile->LogText(wHllMessage, strlen(wHllMessage));
   }
  //////////////////////////////////////////////////////////////////////////////

  //Reset Extended Error
  SetExtendedError(TEC3270_WHLLNOERROR);

  // If DDEML is not connected try to reconnect it
  if (executedOk && !parmError)
   {
    if (IsConnected())
       executedOk = true;
    else executedOk = Connect();
    endProcess = false;
   }
  else
   {
    endProcess = true;
    parmError = true;
   }

  currentCmd = *lpwCommand;
  stringOffset = 0;
  while (!endProcess)
   {
    if (executedOk)
     {
      if ( (*lpwCommand != PAUSE) || (!wHllAppOptions->fPause))
	// We need to call server side for every function, except when function
	// is PAUSE and FPAUSE is set, because it just counts time ....
	//
	// Server does not need to process STREOT as it is already processed here
	// so we need to pass string length calculated above.
	executedOk = ExecuteOnServer(lpwCommand, lpbyString+stringOffset, &tempLen, lpwReturnCode);
      else executedOk = true;

      if (executedOk)
       {
	if ( (*lpwCommand != PAUSE) || (!wHllAppOptions->fPause))
	  // We need to call server side for every function, except when function
	  // is PAUSE and FPAUSE is set, because it just counts time ....
	  {
	    executedOk = GetResultOnServer(lpwCommand, lpbyString, &tempLen, lpwReturnCode);
	    // Length parm must be returned to caller on some functions
	    if ((lpwLength)
	      &&(*lpwCommand != SENDKEY) )
		*lpwLength = tempLen;
          }
        if (executedOk)
	  {
           // Some functions return info from server to be used by
	   // WHLLAPI client: SETSESSIONPARAMETERS and PAUSE
           //////////  SETSESSIONPARAMETERS -  Function 9  ///////////////////////
           if (*lpwCommand == SETSESSIONPARAMETERS)
	    {
             if (*lpwReturnCode != WHLLSYSERROR)
              {
               // Recover App Options that is returned by server
               // Trace Option
               if (hddeBuffer[DATAOFFSET+TRACEOFFSET] == 0x01)
		{
		 if (!wHllAppOptions->traceOn)
		  {
		   if (StartHLLTrace(true))
		      wHllAppOptions->traceOn = true;
                   else
		    {
                     *lpwReturnCode = WHLLSYSERROR;
                     SetExtendedError(TEC3270_WHLLTRACEFILEERROR);
                    }
                  }
                }
               else  // (!newAppOptions->traceOn)
		{
		 if (wHllAppOptions->traceOn)
		  {
		   if (StartHLLTrace(false))
		      wHllAppOptions->traceOn = false;
                   else
		    {
		     *lpwReturnCode = WHLLSYSERROR;
                     SetExtendedError(TEC3270_WHLLTRACEFILEERROR);
                    }
		  }
		}
               // Pause option
               if (hddeBuffer[DATAOFFSET+PAUSEOFFSET] == 0x01)
		   wHllAppOptions->fPause = true;
               else wHllAppOptions->fPause = false;

               // STRLEN / STREOT Option
               if (hddeBuffer[DATAOFFSET+STREOTOFFSET] == 0x01)
		{
		 wHllAppOptions->strEOT = true;
		 wHllAppOptions->eotChar = hddeBuffer[DATAOFFSET+EOTCHAROFFSET];
		}
               else wHllAppOptions->strEOT = false;

               // TWAIT / LWAIT / NWAIT
	       wHllAppOptions->waitPeriod = hddeBuffer[DATAOFFSET+WAITOFFSET];
              }
             endProcess = true;
	    }
           //////////  PAUSE -  Function 18  /////////////////////////////////////
	   else if (*lpwCommand == PAUSE)
	    {
	     // PAUSE Function
	     if ( (!wHllAppOptions->fPause)
	        &&(hddeBuffer[DATAOFFSET+PSCHANGEDOFFSET] == 0x01) )
              {
		 // PS has changed : end PAUSE ...
		 *lpwReturnCode = tempRC = WHLLPSCHANGED;
		 endProcess = true;
              }
             else
              {
               // This is an full Pause (not interrupted by PS changes)
               uTempoAtual = GetTickCount();
               if ((uTempoAtual - uTempoInicio) > uTimeout)
		{
		 *lpwReturnCode = tempRC = WHLLOK;
		 endProcess = true;
		}
              }
            } // if (... = PAUSE)

           //////////  RESETSYSTEM -  Function 21  ///////////////////////////////////////
	   else if (*lpwCommand == RESETSYSTEM)
	    {
	     // if trace is on turn it off
	     if (wHllAppOptions->traceOn)
	      {
               if (StartHLLTrace(false))
		   wHllAppOptions->traceOn = false;
               else
		{
                 *lpwReturnCode = WHLLSYSERROR;
		 SetExtendedError(TEC3270_WHLLTRACEFILEERROR);
		}
              }
	     // Reset App Options
             delete wHllAppOptions;
             wHllAppOptions = new TecWHLLAppParms();

             endProcess = true;
            }

           //////////  CONNECTPS -  Function 1  //////////////////////////////////
	   else if (*lpwCommand == CONNECTPS)
	    {
	     // lpwReturnCode variable contains the flag for re-calling
             // server until connections was successfully or unsuccessfully
             // done. if it turns off (CONNECTPSDONE) we must consider returnCode
             // WHLLOK received as definitive.
             if (*lpwReturnCode == WHLLOK)
              {
               // This is an timeout check for connection be done
               uTempoAtual = GetTickCount();
               if ((uTempoAtual - uTempoInicio) > uTimeout)
		{
		 *lpwReturnCode = tempRC = WHLLSYSERROR;
		 SetExtendedError(TEC3270_WHLLCONNECTIONTIMEDOUT);
		 endProcess = true;
		}
               else
		  // Call CONNECTPS on Server again
		  *lpwReturnCode  = CONNECTPSCHECK;
              }
             else if (*lpwReturnCode == CONNECTPSDONE)
              {
               *lpwReturnCode = tempRC = WHLLOK;
               endProcess = true;
              }
             else endProcess = true;
            }
           //////////  SENDKEY -  Function 3  ////////////////////////////////////
           else if (*lpwCommand == SENDKEY)
	    {
	     if ((tempLen < receivedLen)  // (receivedLen > 0)
              &&((*lpwReturnCode == WHLLPSBUSY)
              ||(*lpwReturnCode == WHLLINHIBITED) ) )
	      {
               // This is an timeout check for sequence being sent
               uTempoAtual = GetTickCount();
               if ((uTempoAtual - uTempoInicio) > uTimeout)
 		   endProcess = true;
               else
		{
		 // We need to process the rest of keystrokes
		 stringOffset += tempLen;  // set offset of next sequence
		 receivedLen -= tempLen;   // discount sequence length just processed
		 tempLen = receivedLen;    // set new length for processing further seqs
		 *lpwReturnCode = SENDKEYNEXT;
		}
              }
	     else if (tempLen > 0)
	      {
               *lpwReturnCode = tempRC = WHLLOK;
               endProcess = true;        // Ok. All keystrokes sequences were processed.
              }
             else endProcess = true;     // Some error occurred on function
            }
           //////////  WAIT -  Function 4  ///////////////////////////////////////
           else if (*lpwCommand == WAIT)
            {
             if ((*lpwReturnCode == WHLLPSBUSY)
               ||(*lpwReturnCode == WHLLINHIBITED) )
              {
               if (uTimeout > 0)  // Wait until timeout or returnCode be cleared (TWAIT)
                {
             	 // This is an timeout check for sequence being sent
		 uTempoAtual = GetTickCount();
		 if ((uTempoAtual - uTempoInicio) > uTimeout)
 		     endProcess = true;
                }
               else if (uTimeout == 0) // No wait time (NWAIT): return to caller
               endProcess = true;
               //else // Wait until returnCode be cleared (LWAIT) (MAY BE FOREVER !!!)
              }
             else endProcess = true;
            }
           else endProcess = true;

	   // Checking for Cancel Blocking Call
	   if (IsSetCancelBlocking())
	    {
              SetCancelBlockingCall(false);
              *lpwReturnCode = tempRC = WHLLCANCEL;
              endProcess = true;
            }
          }
	 else
	  {
           SetExtendedError(GetDdeSavedLastError());

	   TRACEX(TecWHllClient,0,"WHllClient: Transação RequestData (WHLLCOMMAND) com erro -> " << WHllGetExtendedError());

	   *lpwReturnCode = tempRC =  WHLLSYSERROR;
	   //Disconnect();
	   executedOk = false;
	   endProcess = true;
	  }
       }
      else
       {
	SetExtendedError(GetDdeSavedLastError());

	TRACEX(TecWHllClient,0,"TecWHllClient: Transação SendData (WHLLCOMMAND) com erro -> " << WHllGetExtendedError());

	*lpwReturnCode = tempRC =  WHLLSYSERROR;
	//Disconnect();
	executedOk = false;
	endProcess = true;
       }
     }
    else
     {
      SetExtendedError(GetDdeSavedLastError());
      *lpwReturnCode = tempRC = WHLLSYSERROR;
      endProcess = true;
     }
   } // end while (uTime......)
  currentCmd = 0;

  //////////////////////////////////////////////////////////////////////////////
  /*  Do not need this process becayse tempLen and tempString are no more used
			  // Build returned parm from
			  if (mayUseEOT && wHllAppOptions->strEOT)
				{
				 char far* eotPtr = (char*) _fmemchr(lpbyString, wHllAppOptions->eotChar, MAXBUFFERSIZE);
				 if (eotPtr)
				  {
					tempLen      = (WORD) (eotPtr-lpbyString);
					tempString   = string(*lpbyString,0,tempLen);
				  }
				 else
				  {
					tempLen = 0;
					tempString = "";
				  }
				}
			  else if (lpwLength)
				{
				 tempLen = *lpwLength;
				 tempString = string(lpbyString, 0, tempLen-1);
				}
			  else tempLen = 0;
	*/
  /////////////////////////////////////////////////////////////////////////////

  // If there is an trace option defined, trace the command just executed
  if ( !parmError
       &&(wHllAppOptions->traceOn)
       &&(pTrcFile) )
   {
    // Trace Option is on and Trace File is available
    char wHllMessage[STRINGMSGLEN+1];
    char tempChar[STRINGMSGLEN+1];
    char retCode [STRINGMSGLEN+1];
    char crlf[2] = {CR, LF};

    if (*lpwCommand == SETSESSIONPARAMETERS)
     {
      // Command Code Trace:
      char commandName[32];
      ::LoadString(wHllInstance, IDS_WHLLCOMMANDTRACE, tempChar, STRINGMSGLEN);
      ::LoadString(wHllInstance, WHLLAPICMDBASE+*lpwCommand, commandName, 32);
      sprintf(wHllMessage,tempChar, commandName);
      pTrcFile->LogText(crlf, 2);
      pTrcFile->LogText(wHllMessage, strlen(wHllMessage));
     }

    // Parms Trace:
    if (receiveString)
     {
      // Some commands that return no data are executed part on server
      // and part on client and data are exchanged on data area
      // that must not be traced
      if  (*lpwCommand == SETSESSIONPARAMETERS)
	pTrcFile->LogData('R',(char*)lpbyString, receivedLen);
      else pTrcFile->LogData('R',(char*)lpbyString, *lpwLength);
     }
    // Parm Length
    ::LoadString(wHllInstance,IDS_WHLLPARMLENGTHRECVTRACE,tempChar,STRINGMSGLEN);
    sprintf(wHllMessage, tempChar, *lpwLength);
    pTrcFile->LogText(wHllMessage, strlen(wHllMessage));

    //Return Code:
    ::LoadString(wHllInstance,IDS_WHLLRETCODETRACE,tempChar,STRINGMSGLEN);
    ::LoadString(wHllInstance,WHLLAPIRETCODEBASE+*lpwReturnCode,retCode,STRINGMSGLEN);
    sprintf(wHllMessage, tempChar, retCode);
    pTrcFile->LogText(wHllMessage, strlen(wHllMessage));

    // Extended Error (Only when ReturnCode == WHLLSYSERROR)
    if (*lpwReturnCode == WHLLSYSERROR)
     {
      ::LoadString(wHllInstance,IDS_WHLLEXTENDEDERROR,tempChar,STRINGMSGLEN);
      sprintf(wHllMessage, tempChar, extendedError);
      pTrcFile->LogText(wHllMessage, strlen(wHllMessage));
     }
   }
  /////////////////////////////////////////////////////////////////////////////

  // Delete and reset hdeBuffer
  if (hddeBuffer)
   delete [] hddeBuffer;
  hddeBuffer = NULL;

  return *lpwReturnCode;
}

bool
TecWHllClient::StartHLLTrace(bool traceON)
{
 if (traceON)
  {
    char   timeStamp[20];
    string wFileName = "WHLLAPI_";
    time_t timer;
    struct tm *agora;
    time(&timer);
    agora = localtime(&timer);
    sprintf (timeStamp, "%02d%02d%04d_%02d%02d%02d.trc",
    		agora->tm_mday, agora->tm_mon+1, agora->tm_year+1900,
                agora->tm_hour, agora->tm_min, agora->tm_sec);
    wFileName += timeStamp;

    pTrcFile = new ALFFLogFile();
    int rcOpenTrace = pTrcFile->OpenTrace(wFileName.c_str());
    if (rcOpenTrace == SUCCESSFULOPEN)
     {
      /*
       * Setup Log File Title For Identifying What IS Going On
       */
      char trcHeader[STRINGMSGLEN];
      char msgHeader[STRINGMSGLEN];
      char thisAppName[MAX_PATH+1];
      ::LoadString(wHllInstance, IDS_WHLLAPIDESCRIPTION, trcHeader, STRINGMSGLEN);
      pTrcFile->LogText(trcHeader, strlen(trcHeader));

      int nameSize = ::GetModuleFileName(wHllInstance, thisAppName,	MAX_PATH);
      if (nameSize)
      thisAppName[nameSize+1] = NULL;
      ::LoadString(wHllInstance, IDS_ALFFWHLLTRACEHDR, trcHeader, STRINGMSGLEN);
      sprintf(msgHeader,trcHeader,thisAppName);
      pTrcFile->SetLogTitle(msgHeader, dateFormat);
      return true;
     }
    else  return false;
   }
  else return WHllCleanup();
}

void
TecWHllClient::NotifyDisconnect(HCONV appId)
{
 ALFFDdeClient::NotifyDisconnect(appId);
}


bool
TecWHllClient::StartAdvisePSChanges()
{
 return StartAdvise(itemWHllCommand);
}


void
TecWHllClient::StopAdvisePSChanges()
{
 StopAdvise(itemWHllCommand);
}

bool
TecWHllClient::CancelCurrentTransaction()
{
  if ((currentCmd)
	  && ( (currentCmd == PAUSE)
		  ||(currentCmd == SENDKEY)
		  ||(currentCmd == WAIT)
		  ||(currentCmd == CONNECTPS) )  )
	{
	  SetCancelBlockingCall(true);
	  return true;
	}
  else
	{
	 SetExtendedError(TEC3270_WHLLNOTBLOCKING);
	 return false;
	}
}
