#ifndef _ALFFDDE_H
#define _ALFFDDE_H
/*  Project Biblio-Tec

	 SUBSYSTEM:    TEC30r.dll Library (TecLib)
	 FILE:         alffdde.h
	 AUTHOR:       Alcindo Ferreira Filho

	 OVERVIEW:
	 Class definition of ALFFDDeError and ALFFDde, base classes for
    ALFFDdeClient and ALFFDdeServer.

    COPYRIGHT:    © 1998,2003-2005
                  Alcindo Ferreira Filho
                  <alcindo@inttec.com.br>

    LICENSE:      Biblio-Tec license (BiblTec.txt)
*/
#include <owl\module.h>
#include <checks.h>
#include <ddeml.h>
#include <classlib\arrays.h>

#include "alffobj.h"
#include "alffexpt.h"

//
// Five second timeout to compensate for Trace
//
#ifdef __TRACE
#define DDE_TIMEOUT 300000   // 300 seconds - just for debbuging
#else
#define DDE_TIMEOUT 70000    // 70 seconds - highest timeout
#endif                       // (WHLLAPI highest timeout is 60s)


//
// constant for arbitrary string
//
const int strLength = 81;
//
// Little exception class
//
class _TECLIBCLASS ALFFDdeError : public ALFFException
  {
    public:
      ALFFDdeError(string s);
      ~ALFFDdeError();
      string error;
  };
//
// Conversation to instance lookup
//
class _TECLIBCLASS ALFFDde; // forward declaration -
                            // good for pointer
struct _TECLIBCLASS Conversation
  {
    Conversation(ALFFDde* i,HCONV c):
      instance(i),
      conversation(c)
      {}
    ALFFDde* instance;
    HCONV conversation;
    int operator == (const Conversation& c) const
      {
        return this == &c;
      }
  };
//
// Topic to instance lookup. The design principle is that
// each instance provides a unique topic.
//
struct _TECLIBCLASS Topic
  {
    Topic(ALFFDde* i,HSZ t):
      instance(i),topic(t)
      {}
    ALFFDde* instance;
    HSZ topic;
    int operator == (const Topic& t) const
      {
        return this == &t;
      }
  };
//
// Store for conversations
//
typedef TIArrayAsVector<Conversation> Conversations;
//
// Store for topics
//
typedef TIArrayAsVector<Topic> Topics;

//
// Anything static can be used directly in the callback
// function
//
//
// The base DDE class
//
class _TECLIBCLASS ALFFDde : public ALFFObject
  {
    public:
      ALFFDde();
      // Virtual ensures that the derived classes destructors
      // are called
      virtual ~ALFFDde();
      virtual char far* GetClassName() {return "ALFFDde";}

      //
      // Make a DDEML string
      //
      static HSZ DdeString(const char * string);

      //
      // Free a DDEML string
      //
      void FreeString(HSZ item) { ::DdeFreeStringHandle(instance, item); }

      //
      //  See if the text in the strings are the same
      //
      static bool StringsMatch(HSZ item1, HSZ item2);

      //
      // Make DDEML data item
      //
      static HDDEDATA MakeData(LPBYTE source,DWORD length,HSZ item);

      //
      // Get Data
      //
      static DWORD GetData(HDDEDATA data, LPBYTE dataPtr, DWORD length);

      //
      // Extract length of data
      //
      DWORD GetDataLength(HDDEDATA data);

      //
      //  Free data handle (not normally required)
      //
      static bool FreeData(HDDEDATA data) { return ::DdeFreeDataHandle(data); }

      //
      //  Get DDE Last Error
		//
      void         ResetSavedLastError()  {ddeLastError = 0;}
		unsigned int GetDdeSavedLastError() {return ddeLastError;}
		unsigned int GetLastError()
			{ddeLastError = ::DdeGetLastError(instance); return ddeLastError; }
      //
      //  POSSIBLE ERROR CODES
      //
      /*
      Value									Meaning
		DMLERR_ADVACKTIMEOUT				A request for a synchronous advise transaction
      										has timed out.
		DMLERR_BUSY							The response to the transaction caused
      										the DDE_FBUSY flag to be set.
		DMLERR_DATAACKTIMEOUT			A request for a synchronous data transaction
      										has timed out.
		DMLERR_DLL_NOT_INITIALIZED		A DDEML function was called without first
      										calling the DdeInitialize function,
                                    or an invalid instance identifier was
                                    passed to a DDEML function.
		DMLERR_DLL_USAGE					An application initialized as APPCLASS_MONITOR
      										has attempted to perform a dynamic data exchange
                                    (DDE) transaction, or an application initialized
                                    as APPCMD_CLIENTONLY has attempted to perform
                                    server transactions.
		DMLERR_EXECACKTIMEOUT			A request for a synchronous execute transaction
      										has timed out.
		DMLERR_INVALIDPARAMETER			A parameter failed to be validated by the DDEML.
      											Some of the possible causes follow:
                                       The application used a data handle initialized with a different item name handle than was required by the transaction.
                                       The application used a data handle that was initialized with a different clipboard data format than was required by the transaction.
                                       The application used a client-side conversation handle with a server-side function or vice versa.
                                       The application used a freed data handle or string handle.
                                       More than one instance of the application used the same object.
		DMLERR_LOW_MEMORY					A DDEML application has created a prolonged
      										race condition (in which the server application
                                    outruns the client), causing large amounts of
                                    memory to be consumed.
		DMLERR_MEMORY_ERROR				A memory allocation has failed.
		DMLERR_NO_CONV_ESTABLISHED		A client's attempt to establish a conversation
      										has failed.
		DMLERR_NOTPROCESSED				A transaction has failed.
		DMLERR_POKEACKTIMEOUT			A request for a synchronous poke transaction
      										has timed out.
		DMLERR_POSTMSG_FAILED			An internal call to the PostMessage function
      										has failed.
		DMLERR_REENTRANCY					An application instance with a synchronous
      										transaction already in progress attempted to
                                    initiate another synchronous transaction,
                                    or the DdeEnableCallback function was called
                                    from within a DDEML callback function.
		DMLERR_SERVER_DIED				A server-side transaction was attempted on
      										a conversation terminated by the client,
                                    or the server terminated before completing
                                    a transaction.
		DMLERR_SYS_ERROR					An internal error has occurred in the DDEML.
		DMLERR_UNADVACKTIMEOUT			A request to end an advise transaction has
      										timed out.
		DMLERR_UNFOUND_QUEUE_ID			An invalid transaction identifier was passed
      										to a DDEML function. Once the application
                                    has returned from an XTYP_XACT_COMPLETE
                                    callback, the transaction identifier for
                                    that callback function  is no longer valid.
      */
    protected:
      //
      // Find out what is in a DDEML string
      //
      static char* QueryString(HSZ ddeString,char * string);
      //
      // Tell class it has disconnected
      //
      virtual void NotifyDisconnect(HCONV convId) {};
//
// Receive data from Advise (Client)
// Receive data from POKE   (Server)
//
      virtual HDDEDATA ReceivedData(HCONV convId, HSZ item, HDDEDATA data);

//
// Execute Command  (Server)
//
      virtual HDDEDATA ExecuteCommand(HDDEDATA hCommand);
      
//
// Return data when requested (Server)
//
      virtual HDDEDATA ReturnData(HCONV convId, HSZ ddeItem);
//
// Respond to request to start and stop Advise loop
//
      virtual bool StartServerAdvise(HCONV convId, HSZ ddeItem);
      virtual bool StopServerAdvise(HCONV convId, HSZ ddeItem);
//
// Does this application provide topics (Server)
//
      static bool ProvidesTopic(HSZ queryService,HSZ queryTopic);
//
// Does this instance provide items (Server)?
//
      virtual bool ProvidesItem(HSZ queryItem)
        {
          return FALSE;
        };
//
// DDEML instance handle
//
      static DWORD instance;
//
// Service (Server only)
//
      static HSZ service;
      
      static Conversations conversations;
      static Topics topics;


     private:
//
// Count of class instances for final cleanup
//
		static int ddeCount;
//
// DDE Last Error for Client And Server Retrieving
//
      unsigned int ddeLastError;

//
// Find which class instance is holding this conversation
//
      static ALFFDde* FindInstance(HCONV conversation);
//
// The callback which distributes DDEML responses
//
      static HDDEDATA CALLBACK
        DdeCallBack(WORD,WORD,HCONV,HSZ,HSZ,HDDEDATA,DWORD,DWORD);
};
#endif
