/***************************************************************************
 * TWAIN Data Source Manager version 2.1
 * Manages image acquisition data sources used by a machine. 
 * Copyright © 2007 TWAIN Working Group:
 * Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company, 
 * Fujitsu Computer Products of America, JFL Peripheral Solutions Inc., 
 * Ricoh Corporation, and Xerox Corporation.
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact the TWAIN Working Group by emailing the Technical Subcommittee at 
 * twainwg@twain.org or mailing us at 13090 Hwy 9, Suite 3, Boulder Creek, CA 95006.
 *
 ***************************************************************************/

/**
* @file dsm.h
* Everything we need to make our .cpp files happy.
* 
* @author TWAIN Working Group
* @date March 2007
*/

#ifndef __DSM_H__
#define __DSM_H__


/**
* @defgroup Enviroment the computer enviroment
* First off, figure out what compiler we're running and on which
* platform we think we're running it.  We assume that you're building
* on the same platform you intend to run, so if you are cross compiling
* you will likely have a bit of work to do here...
* @{
*/

/**
* @defgroup Compilers Compilers we support...
* @{
*/
#define TWNDSM_CMP_VISUALCPP    0x1001 ///< Preferably 2005+
#define TWNDSM_CMP_GNUGPP       0x1002 ///< Preferably v4.x+
//@}

/**
* @defgroup Platforms Platforms we support...
* @{
*/
#define TWNDSM_OS_WINDOWS       0x2001 ///< Preferably Win2K+
#define TWNDSM_OS_MACOSX        0x2002 ///< Preferably 10.4+
#define TWNDSM_OS_LINUX         0x2003 ///< Preferably 2.6+ kernel
//@}


/**
* If the user defines TWNDSM_CMP in their make file or project,
* then we'll assume they want to take responsibility for picking
* how we'll build the system.  At this point it seems like the
* compiler definition is used to select which native library calls
* we're dealing with, while the os definition is more about
* where we'll expect to find stuff on the running system, like
* directories...
*/
#ifndef TWNDSM_CMP

/**
* @def TWNDSM_CMP 
* The compliler used
* 
* @def TWNDSM_CMP_VERSION 
* The version of the compliler used
* 
* @def TWNDSM_OS 
* The Operating system of the compliler used
* 
* @def TWNDSM_OS_64BIT 
* defined to 1 if system is 64 bit
* 
* @def TWNDSM_OS_32BIT 
* defined to 1 if system is 32 bit
*/
 
  // GNU g++
  #if defined(__GNUC__)
    #define TWNDSM_CMP              TWNDSM_CMP_GNUGPP
    #define TWNDSM_CMP_VERSION      __GNUC__
    #if defined(__APPLE__)
      #define TWNDSM_OS             TWNDSM_OS_MACOSX
    #else
      #define TWNDSM_OS             TWNDSM_OS_LINUX
    #endif
    #if defined(__x86_64__) || defined(__LP64__)
      #define TWNDSM_OS_64BIT   1
    #else
      #define TWNDSM_OS_32BIT   1
    #endif

  // Visual Studio C++
  #elif defined(_MSC_VER)
    #define TWNDSM_CMP              TWNDSM_CMP_VISUALCPP
    #define TWNDSM_CMP_VERSION      _MSC_VER
    #define TWNDSM_OS               TWNDSM_OS_WINDOWS
    #if defined(_M_X64) || defined(_M_IA64)
      #define TWNDSM_OS_64BIT   1
    #else
      #define TWNDSM_OS_32BIT   1
    #endif

  // ruh-roh...
  #else
    Sorry, we do not recognize this system...
  #endif
#endif



/**
*  Pull in the system specific headers...
*/
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
  #ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
  #endif
  #include <windows.h>
  #include <direct.h>
  #include <share.h>

#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
  #include <dirent.h>
  #include <dlfcn.h>
  #include <unistd.h>
  #include <errno.h>
  #include <stdarg.h>
  #include <time.h>
  #include <sys/syscall.h>
  #include <sys/time.h>
  #define gettid() syscall(SYS_gettid)

#else
  #error Sorry, we do not recognize this system...
#endif

// End @defgroup Enviroment 
//@}


/**
* We use resource.h to specify version info on all platforms...
*/
#include "resource.h"



/**
* These headers are available on all platforms...
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>

/**
* This is for IDEs like Visual Studio .Net 2003, that does not understand the SAL Annotations
*/
#ifndef __in
  #define __in
  #define __out
  #define __in_opt
#endif



/**
* Don't forget to include TWAIN...
*/
#include "twain.h"


/**
* @defgroup CrossPlatformFunc Cross platform functions, defines, and macroes
* @{
*
*
* @def DllExport
* set system dll export configuration __declspec( dllexport )
* 
* @def NCHARS
* The number of characters in a charter array
* 
* @def PATH_SEPERATOR
* the operating system's symble used as a path seperator
* 
* @def LOADLIBRARY(lib, hook, DSid)
* Call system loadibrary function.  OS abstraction macro that tries to load a library.
* @param[in] lib path and name of library
* @param[in] hook true if we want to attempt to hook this library
* @param[in] DSid if hooking is the ID of the DS we are hooking
* 
* @def LOADFUNCTION(lib, func)
* Call system GetProcAddress function.  OS abstraction macro that tries to locate the addess of a funtion name.
* @param[in] lib path and name of library
* @param[in] func name of the funtion
*
* @def UNLOADLIBRARY(lib, unhook, DSid)
* Call system FreeLibrary function.  OS abstraction macro that tries to release the library.
* @param[in] lib library modual to unload
* @param[in] unhook true if we want to attempt to unhook this library
* @param[in] DSid if unhooking is the ID of the DS we are unhooking
* 
* @def READ
* OS abstraction macro that calls system _read function.
* 
* @def CLOSE
* OS abstraction macro that calls system _close function.
* 
* @def SNPRINTF
* OS abstraction macro that calls system _snprintf function.
* 
* @def UNLINK
* OS abstraction macro that calls system _unlink function.
*
* @def STRNICMP
* OS abstraction macro that calls system _strnicmp function.
*
* @def DSMENTRY 
* the DSM entry point type
*
* @def GETTHREADID
* get the thread ID
*
* @def FOPEN
* @param[out] pf pointer to the file to store the opened file
* @param[in] name the path and name of the file to open
* @param[in] mode the mode to open the file
*
* @def kTWAIN_DS_DIR
* The path to where TWAIN Data Sources are stored on the system
*/
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)

  // Define TW_IDENTITY.Id
  #define TWID_T TW_UINT32
  #define TWIDDEST_T TW_UINT32

  // For 64-bit systems we work the same as on Linux/MacOSX...
  #if TWNDSM_OS_64BIT
    #define LOADLIBRARY(lib,hook,DSID) LoadLibrary(lib)
    #define UNLOADLIBRARY(hmodule,unhook,DSID) FreeLibrary((HMODULE)hmodule)

    // For 32-bit systems we use a hooking mechanism to help 1.x
    // drivers find the new TWAINDSM.DLL...
  #else
    HMODULE InstallTwain32DllHooks
    (
      const char* const _lib,
      const bool _hook,
      const TWID_T _DSID
    );
    BOOL UninstallTwain32DllHooks
    (
      const HMODULE _hmodule,
      const bool _unhook,
      const TWID_T _DSID
    );
    #define LOADLIBRARY(lib,hook,DSID) InstallTwain32DllHooks(lib,hook,DSID)
    #define UNLOADLIBRARY(hmodule,unhook,DSID) UninstallTwain32DllHooks((HMODULE)hmodule,unhook,DSID)
  #endif

  #define DllExport __declspec( dllexport )
  #define NCHARS(s) sizeof(s)/sizeof(s[0])
  #define PATH_SEPERATOR '\\'
 
  #define LOADFUNCTION(lib, func) GetProcAddress((HMODULE)lib, func)
  #define READ _read
  #define CLOSE _close
  #if (TWNDSM_CMP_VERSION >= 1400)
    #define SNPRINTF _snprintf_s
  #else
    #define SNPRINTF _snprintf
  #endif
  #define UNLINK _unlink
  #define STRNICMP _strnicmp
  #define DSMENTRY TW_UINT16 FAR PASCAL
  #define GETTHREADID ::GetCurrentThreadId
  #define FOPEN(pf, name, mode) pf = _fsopen(name, mode, _SH_DENYNO)
  #ifndef kTWAIN_DS_DIR
    #if TWNDSM_OS_64BIT
      #define kTWAIN_DS_DIR "twain_64"
    #else
      #define kTWAIN_DS_DIR "twain_32"
    #endif
  #endif

#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
  #define DllExport
  #define NCHARS(s) sizeof(s)/sizeof(s[0])
  #define PATH_SEPERATOR '/'
#if (TWNDSM_OS == TWNDSM_OS_MACOSX)
  #define LOADLIBRARY(lib,hook,DSID) \
    CFBundleCreate(0, CFURLCreateWithFileSystemPath(0, CFStringCreateWithCStringNoCopy(0, _pPath, kCFStringEncodingUTF8, 0), kCFURLPOSIXPathStyle, TRUE))
  #define UNLOADLIBRARY(lib,unhook,DSID) 0; CFRelease((CFBundleRef)(lib))
#else
  #define LOADLIBRARY(lib,hook,DSID) dlopen(lib, RTLD_LAZY)
  #define UNLOADLIBRARY(lib,unhook,DSID) dlclose(lib)
#endif
  #define LOADFUNCTION(lib, func) dlsym(lib, func)
  #define READ read
  #define CLOSE close
  #define SNPRINTF snprintf
  #define UNLINK unlink
  #define STRNICMP strncasecmp
  #define GETTHREADID gettid
  #define FOPEN(pf,name,mode) pf = fopen(name,mode)
  #ifndef kTWAIN_DS_DIR
    #if (TWNDSM_OS == TWNDSM_OS_MACOSX)
      #define kTWAIN_DS_DIR "/Library/Image Capture/TWAIN Data Sources"
    #else
      #define kTWAIN_DS_DIR "/usr/local/lib/twain"
    #endif
  #endif
  typedef unsigned int UINT;
  typedef void* HINSTANCE;
  typedef void* HWND;
  #define DSMENTRY FAR PASCAL TW_UINT16

  #if (TWNDSM_OS == TWNDSM_OS_MACOSX)
    #if TWNDSM_OS_64BIT
      #define TWID_T unsigned long long
      #define TWIDDEST_T TW_MEMREF
    #else
      #define TWID_T unsigned long
      #define TWIDDEST_T TW_MEMREF
    #endif
  #else
    #define TWID_T TW_UINT32
    #define TWIDDEST_T TW_UINT32
  #endif


  #if !defined(TRUE)
    #define FALSE   0
    #define TRUE    1
  #endif

#else
  #error Sorry, we do not recognize this system...
#endif



/**
* @defgroup StringFunctions use secure string functions if we have them
* We want to use secure string functions whenever possible, if g++
* every includes a set I think it would be excellent to switch over
* to it, but at least with Windows using them we stand a better
* chance of finding boo-boos...
* @{
*
* @def SSTRCPY
* Secure String copy
* @param[out] d destination string
* @param[in] z size of destination in char
* @param[in] s the source string
* 
* @def SSTRCAT
* Secure String catinate
* @param[out] d destination string
* @param[in] z size of destination in char
* @param[in] s the source string
* 
* @def SSTRNCPY
* Secure String n copy
* @param[out] d destination string
* @param[in] z size of destination in char
* @param[in] s the source string
* @param[in] m the number of char to copy
* 
* @def SGETENV
* Secure Get enviroment varable
* @param[out] d destination string
* @param[in] z size of destination in char
* @param[in] n the source string
* 
*/
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP) && (TWNDSM_CMP_VERSION >= 1400)
  #define SSTRCPY(d,z,s) strncpy_s(d,z,s,_TRUNCATE)
  #define SSTRCAT(d,z,s) strncat_s(d,z,s,_TRUNCATE)
  #define SSTRNCPY(d,z,s,m) strncpy_s(d,z,s,m)
  #define SGETENV(d,z,n) ::GetEnvironmentVariable(n,d,z)
  inline int SSNPRINTF(char *d, const size_t z, const size_t c, const char* const f,...)
  {
      int result;
      va_list valist;
      va_start(valist,f);
      result = _vsnprintf_s(d,z,c,f,valist);
      va_end(valist);
      return result;
  }


/**
* These functions are insecure, but everybody has them, so we
* don't need an else/error section like we use everywhere else...
*/
#elif __APPLE__
  #define SSTRCPY(d,z,s) strlcpy(d,s,z)
  #define SSTRCAT(d,z,s) strcat(d,s)
  #define SSTRNCPY(d,z,s,m) strncpy(d,s,m)
  #define SGETENV(d,z,n) strcpy(d,getenv(n)?getenv(n):"")
  inline int SSNPRINTF(char *d, const size_t, const size_t c, const char* const f,...)
  {
      int result;
      va_list valist;
      va_start(valist,f);
      #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
        result = _vsnprintf(d,c,f,valist);
      #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
        result = vsnprintf(d,c,f,valist);
      #else
        #error Sorry, we do not recognize this system...
      #endif
      va_end(valist);
      return result;
  }


/**
* These functions are insecure, but everybody has them, so we
* don't need an else/error section like we use everywhere else...
*/
#else
  #define SSTRCPY(d,z,s) strcpy(d,s)
  #define SSTRCAT(d,z,s) strcat(d,s)
  #define SSTRNCPY(d,z,s,m) strncpy(d,s,m)
  #define SGETENV(d,z,n) strcpy(d,getenv(n)?getenv(n):"")
  inline int SSNPRINTF(char *d, const size_t, const size_t c, const char* const f,...)
  {
      int result;
      va_list valist;
      va_start(valist,f);
      #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
        result = _vsnprintf(d,c,f,valist);
      #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
        result = vsnprintf(d,c,f,valist);
      #else
        #error Sorry, we do not recognize this system...
      #endif
      va_end(valist);
      return result;
  }
#endif
// End @defgroup StringFunctions
//@}

// End @defgroup CrossPlatformFunc 
//@}



/**
*@defgroup Logging logging defines and functions
* These aren't logging levels, these are definitions to tell us whether
* or not to assert.
* @see kLOG
*/

/** 
* write info messages to LogFile. 
*/
#define kLOGINFO   0,__FILE__,__LINE__

/** 
* write error messages to LogFile. 
*/
#define kLOGERR    1,__FILE__,__LINE__

/**
* Define to write messages to LogFile.
* @see CTwnDsmLog
*/
#define kLOG(a) if (g_ptwndsmlog) g_ptwndsmlog->Log a

// End @defgroup Logging
//@}


/**
* Display message to user.  Use this if logging is not an
* option, and this is the only way to track a problem!!!
* @see kLOG
*/
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
  #define kPANIC(msg) ::MessageBox(NULL,msg,"TWAIN Data Source Manager",MB_OK);
#elif  (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
  #define kPANIC(msg) fprintf(stderr,"TWAIN Data Source Manager: %s\r\n",msg);
#else
  #error Sorry, we do not recognize this system...
#endif


/**
* Maximum number of Data Sources that can be opened under one
* application.  This item seems useful, though the number
* seems rather high.
*/
#define MAX_NUM_DS 50


/**
* Possible States of the DSM.
* The three possible states of the Data Source Manager.  We don't
* want to know about the other states, because that would add
* needless complexity.
*/
typedef enum
{
  dsmState_PreSession = 1, /**< Source Manager not loaded. */
  dsmState_Loaded     = 2, /**< Source Manager is loaded, but not open. */
  dsmState_Open       = 3  /**< Source Manager is open. */
} DSM_State;

/**
* This function wraps the function loading calls. Linux has a 
* special way to check dlsym failures.
*/
void* DSM_LoadFunction(void* _pHandle, const char* _pszSymbol);

/**
* @class CTwnDsmLog
* Our logging class.  We use the impl to encapsulate the private
* portions of the class, which doesn't matter for this class so
* much as it does for the next one.  Then we give ourselves an
* extern, because life is easier if we treat this object as globally
* accessible (think of it like a service).
*/
class CTwnDsmLogImpl;
class CTwnDsmLog
{
  public:

    /**
    * The CTwnDsmLog constructor.
    */
    CTwnDsmLog();

    /**
    * The CTwnDsmLog destructor.
    */
    ~CTwnDsmLog();

    /**
    * The logging function.  This should only be access through
    * the kLOG macro...
    * @param[in] _doassert decides if we assert or not
    * @param[in] _file the source file of the message 
    * @param[in] _line the source line of the message 
    * @param[in] _format the format of the message (same as sprintf) 
    * @param[in] ... arguments to the format of the message 
    */
    void Log(const int         _doassert,
             const char* const _file,
             const int         _line,
             const char* const _format,
             ...);

    /**
    * Indent the logging to help with seeing recursive calls
    * param[in] nChange Either +1 or -1 
    */
    void Indent(int nChange);

  private:

    /**
    * The implementation pointer helps with encapulation.
    */
    CTwnDsmLogImpl *m_ptwndsmlogimpl;
};
extern CTwnDsmLog *g_ptwndsmlog;



/**
* @class CTwnDsmApps
* Class to hold list of connected applications.
* In 32bit enviroments each application will connect to a seperate
* instance of DSM data but with this list it allows ONE application
* to connect several time, as long as it uses a different name with
* each connection.  I'm still not sure why you'd want to do that,
* but there it is.  This class is intended to hide the gory details
* of how we're storing the data, so an impl is used.
*/
class CTwnDsmAppsImpl;
class CTwnDsmApps
{
  public:

    /**
    * The CTwnDsmApps constructor.
    */
    CTwnDsmApps();

    /**
    * The CTwnDsmApps destructor.
    */
    ~CTwnDsmApps();

    /**
    * Add an application.
    * This supports MSG_OPENDSM.
    * @param[out] _pAppId Origin of message
    * @param[in] _MemRef the HWND on Window, null otherwise
    * @return a valid TWRC_xxxx return code
    */
    TW_UINT16 AddApp(TW_IDENTITY *_pAppId,
                     TW_MEMREF _MemRef);

    /**
    * Remove an application.
    * This supports MSG_CLOSEDSM.
    * @param[in] _pAppId Origin of message
    * @return a valid TWRC_xxxx return code
    */
    TW_UINT16 RemoveApp(TW_IDENTITY *_pAppId);

    /**
    * Loads a DS from disk and adds it to a global list of DS's.
    * @param[in] _pAppId Origin of message
    * @param[in] _DsId the source index of the library to open
    * @return a valid TWRC_xxxx return code
    */
    TW_INT16 LoadDS(TW_IDENTITY *_pAppId,
                    TWID_T      _DsId);

    /**
    * Unloads a DS and frees all its resources...
    * @param[in] _pAppId Origin of message
    * @param[in] _DsId the source index
    */
    void UnloadDS(TW_IDENTITY *_pAppId,
                  TWID_T      _DsId);

    /**
    * Validate that an id is in range...
    * @param[in] _pAppId id of App to test
    * @return TRUE if valid, else FALSE
    */
    TW_BOOL AppValidateId(TW_IDENTITY *_pAppId);

    /**
    * Validate that the App ID and DS ID are in range...
    * @param[in] _pAppId id of App to test
    * @param[in] _pDSId id of DS to test
    * @return TRUE if valid, else FALSE
    */
    TW_BOOL AppValidateIds(TW_IDENTITY *_pAppId, TW_IDENTITY *_pDSId);

    /**
    * Return a pointer to the application's identity.
    * Yeah, I know, this sorta violates encapsulation, but we do not
    * want to get silly about this...
    * @param[in] _pAppId id of identity to get
    * @return pointer to identity or NULL
    */
    TW_IDENTITY *AppGetIdentity(TW_IDENTITY *_pAppId);

    /**
    * Get the condition code, then reset it internally to TWCC_SUCCESS,
    * so you can only get it once, per the specification...
    * @param[in] _pAppId id of app, or NULL if we have no apps
    * @return TWCC_ value
    */
    TW_UINT16 AppGetConditionCode(TW_IDENTITY *_pAppId);

    /**
    * Set the condition code
    * @param[in] _pAppId id of app, or NULL if we have no apps
    * @param[in] _conditioncode the code to use
    */
    void AppSetConditionCode(TW_IDENTITY *_pAppId,
                             TW_UINT16 _conditioncode);

    /**
    * Get the state of the DSM for all applications
    * @return DSM_State, Open if at least one application has DSM open
    */
    DSM_State AppGetState();

    /**
    * Get the state of the DSM for the specified application
    * @param[in] _pAppId id of app
    * @return DSM_State of the application
    */
    DSM_State AppGetState(TW_IDENTITY *_pAppId);

    /**
    * Get the hwnd sent in with the call to MSG_OPENDSM
    * @param[in] _pAppId id of app
    * @return hwnd for the application that is calling us
    */
    void *AppHwnd(TW_IDENTITY *_pAppId);

    /**
    * Get the number of drivers we found as the result of a
    * successful call to LoadDS with _boolKeepOpen set to
    * false (meaning that we were just browsing)...
    * @param[in] _pAppId id of app
    * @return DSM_State of the application
    */
    TWID_T AppGetNumDs(TW_IDENTITY *_pAppId);

    /**
    * Poke the application to wake it up when sending a
    * DAT_NULL message to it...
    * @param[in] _pAppId id of app
    */
    void AppWakeup(TW_IDENTITY *_pAppId);

    /**
    * Get a pointer to the identity of the specified driver...
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return pointer to drivers identity or NULL
    */
    TW_IDENTITY *DsGetIdentity(TW_IDENTITY *_pAppId,
                               TWID_T       _DsId);

    /**
    * Get a pointer to the DS_Entry function of the specified driver...
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return pointer to DS_Entry for this driver or NULL
    */
    DSENTRYPROC  DsGetEntryProc(TW_IDENTITY *_pAppId,
                                TWID_T       _DsId);

    /**
    * Get a pointer to the driver file path and name, which is guaranteed to
    * be unique, even if the ProductName's aren't for some horrible
    * reason...
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return pointer to file path and name for this driver or NULL
    */
    char *DsGetPath(TW_IDENTITY *_pAppId,
                    TWID_T       _DsId);

    /**
    * Get a pointer to TW_CALLBACK structure for the specified driver...
    * reason...
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return pointer to the callback structure for this driver or NULL
    */
    TW_CALLBACK2 *DsCallback2Get(TW_IDENTITY *_pAppId,
                                 TWID_T       _DsId);

    /**
    * Test if the driver has a callback pending for attention...
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return TRUE if the driver needs its callback called
    */
    TW_BOOL DsCallbackIsWaiting(TW_IDENTITY *_pAppId,
                                TWID_T       _DsId);

    /**
    * Set the callback flag for the driver to TRUE if the callback
    * needs to have its callback called, and set it to FALSE after
    * the call has been made...
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @param[in] _Waiting the new state for the waiting flag
    */
    void DsCallbackSetWaiting(TW_IDENTITY *_pAppId,
                              TWID_T       _DsId,
                              TW_BOOL     _Waiting);

    /**
    * Check if the DS is still processing last message
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return TRUE if the DS has not finished processing message
    */
    TW_BOOL DsIsProcessingMessage(TW_IDENTITY *_pAppId,
                                  TWID_T       _DsId);

    /**
    * Set the ProcessingMessage flag.
    * This is how we know the DS is not done processing the previous message
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @param[in] _Processing the new state for the processing flag
    */
    void DsSetProcessingMessage(TW_IDENTITY *_pAppId,
                                TWID_T       _DsId,
                                TW_BOOL      _Processing);

    /**
    * Check if the App is still processing last callback.
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @return TRUE if the App has not finished processing callback
    */
    TW_BOOL DsIsAppProcessingCallback(TW_IDENTITY *_pAppId,
                                      TWID_T       _DsId);

    /**
    * Set the AppProcessingCallback flag.
    * This is how we know the App is not done processing the previous callback
    * @param[in] _pAppId id of app
    * @param[in] _DsId numeric id of driver
    * @param[in] _Processing the new state for the processing flag
    */
    void DsSetAppProcessingCallback(TW_IDENTITY *_pAppId,
                                    TWID_T       _DsId,
                                    TW_BOOL      _Processing);

    /**
    * Get number of allocated App slots (Last valid App ID +1)
    * @return number of allocated App slots (Last valid App ID +1)
    */
    TWID_T AppGetNumApp();

  private:

    /**
    * The implementation pointer helps with encapulation.
    */
    CTwnDsmAppsImpl *m_ptwndsmappsimpl;
};



/**
* This is the main class for the Data Source Manager.  Unlike the
* other classes this one isn't using an impl interface.  The
* rationale is that DSM_Entry is the true interface point, nobody
* who calls the DSM has to know anything about the implementation.
* So there's no benefit (except a programmer's desire for
* consistency) to putting in the impl.  I'm resisting that on the
* theory that if I don't need it, why make things more complex.
* YMMV...
*/
class CTwnDsm
{
    //
    // All of our public functions go here...
    //
    public:

        /**
        * Our CTwnDsm constructor...
        */
        CTwnDsm();

        /**
        * Our CTwnDsm destructor...
        */
        ~CTwnDsm();

        /**
        * The guts of the DSM_Entry, the resource management portion
        * resides in a our DSM_Entry entry point, which isn't a part
        * of this class.  Hopefully it's not confusing that they have
        * the same name...
        * @param[in] _pOrigin Origin of message in this case a DS
        * @param[in] _pDest destination of message in this case an App
        * @param[in] _DG message id: DG_xxxx
        * @param[in] _DAT message id: DAT_xxxx
        * @param[in] _MSG message id: MSG_xxxx
        * @param[in] _pData the Data
        * @return a valid TWRC_xxxx return code
        */
        TW_UINT16 DSM_Entry(TW_IDENTITY  *_pOrigin,
                            TW_IDENTITY  *_pDest,
                            TW_UINT32    _DG,
                            TW_UINT16    _DAT,
                            TW_UINT16    _MSG,
                            TW_MEMREF    _pData);

        #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
        /**
        * Selection dialog, for apps that don't want to do GetFirst
        * GetNext.  This is only public because of the way that
        * dialogs are implemented.
        * @param[in] _hWnd Window handle of the dialog
        * @param[in] _Message message
        * @param[in] _wParam wparam
        * @param[in] _lParam lparam
        * @return FALSE if we processed the message
        */
            BOOL CALLBACK SelectDlgProc(HWND _hWnd,
                                        UINT _Message,
                                        WPARAM _wParam,
                                        LPARAM _lParam);
        #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
            // We don't have one of these...
        #else
            #error Sorry, we do not recognize this system...
        #endif

        /**
        * Get the state of the DSM by checking the state of all applications
        * @return DSM_State, Open if at least one application has DSM open
        */
        DSM_State DSMGetState();


    //
    // All of our private functions go here...
    //
    private:

        /**
        * Handles DAT_NULL calls from DS for Application.
        * @param[in] _pAppId Origin of message
        * @param[in] _pDsId TW_IDENTITY structure
        * @param[in] _MSG message id: MSG_xxxx
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Null(TW_IDENTITY *_pAppId,
                          TW_IDENTITY *_pDsId,
                          TW_UINT16 _MSG);

        /**
        * Returns the current DSM status. Resets pod.m_ConditionCode to
        * TWCC_SUCCESS per the specification.
        * @param[in] _pAppId Orgin of message
        * @param[in] _MSG message id: MSG_xxxx
        * @param[out] _pStatus TW_STATUS structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Status(TW_IDENTITY *_pAppId,
                            TW_UINT16  _MSG,
                            TW_STATUS *_pStatus);

        /**
        * Initializes or closes the DSM
        * @param[in] _pAppId Orgin of message
        * @param[in] _MSG message id: MSG_xxxx
        * @param[in] _MemRef for Windows during MSG_OPENDSM it is HWND, null otherwise
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Parent(TW_IDENTITY *_pAppId,
                            TW_UINT16 _MSG,
                            TW_MEMREF _MemRef);

        /**
        * Source operations
        * @param[in] _pAppId Origin of message
        * @param[in] _MSG message id: MSG_xxxx
        * @param[in] _pDsId TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Identity(TW_IDENTITY *_pAppId,
                              TW_UINT16 _MSG,
                              TW_IDENTITY *_pDsId);

        /**
        * This routine will return the path to a DS.  
        * This is here for backwards compatibility. DAT_TWUNKIDENTITY is 
        * undocumented.  It was used by the Twunking layer.  Some old 
        * applications use it to get the path to the DS.  We need to 
        * continue to support it.
        * @param[in] _pAppId Origin of message
        * @param[in] _MSG message id: MSG_GET
        * @param[in,out] _pTwunkId TW_TWUNKIDENTITY structure with a valid TW_IDENTITY, returns path
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_TwunkIdentity(TW_IDENTITY *_pAppId,
                                   TW_UINT16 _MSG,
                                   TW_TWUNKIDENTITY *_pTwunkId);

        /**
        * Gets entry points
        * @param[in] _pAppId Origin of message
        * @param[in] _MSG message id: MSG_xxxx
        * @param[out] _pEntrypoint TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Entrypoint(TW_IDENTITY *_pAppId,
                                TW_UINT16 _MSG,
                                TW_ENTRYPOINT *_pEntrypoint);

        /**
        * Register application's callback.
        * @param[in] _pAppId Origin of message
        * @param[in] _pDsId TW_IDENTITY structure
        * @param[in] _MSG message id: MSG_xxxx valid = MSG_REGISTER_CALLBACK
        * @param[in] _pData pointer to a callback struct
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Callback(TW_IDENTITY *_pAppId,
                              TW_IDENTITY *_pDsId,
                              TW_UINT16 _MSG,
                              TW_CALLBACK *_pData);

        /**
        * Register application's callback.
        * @param[in] _pAppId Origin of message
        * @param[in] _pDsId TW_IDENTITY structure
        * @param[in] _MSG message id: MSG_xxxx valid = MSG_REGISTER_CALLBACK
        * @param[in] _pData pointer to a callback2 struct
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_Callback2(TW_IDENTITY *_pAppId,
                              TW_IDENTITY *_pDsId,
                              TW_UINT16 _MSG,
                              TW_CALLBACK2 *_pData);

        /**
        * Opens the Data Source specified by pDSIdentity.  
        * pDSIdentity must be valid, but if a null name and id
        * is 0 then open default.
        * @param[in] _pAppId Origin of message
        * @param[in] _pDsId TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 OpenDS(TW_IDENTITY *_pAppId,
                        TW_IDENTITY *_pDsId);

        /**
        * Closes the Data Source specified by pDSIdentity.
        * @param[in] _pAppId Origin of message
        * @param[in] _pDsId TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 CloseDS(TW_IDENTITY *_pAppId,
                         TW_IDENTITY *_pDsId);

        /**
        * Displays the source select dialog and sets the default source.
        * @param[in] _pAppId Origin of message
        * @param[in,out] _pDsId TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_SelectDS(TW_IDENTITY *_pAppId,
                              TW_IDENTITY *_pDsId);

        /**
        * Set the default source.
        * @param[in] _pAppId Origin of message
        * @param[in] _pDsId TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_SetDefaultDS(TW_IDENTITY *_pAppId,
                                  TW_IDENTITY *_pDsId);

        /**
        * Goes through the applications supported data sources looking for one that has
        * the exact same name as product name in the passed in identity. Will update the
        * _pDsId structure to match the name.
        * @param[in] _pAppId Origin of message
        * @param[in,out] _pDsId TW_IDENTITY structure
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 GetDSFromProductName(TW_IDENTITY *_pAppId,
                                      TW_IDENTITY *_pDsId);

        /**
        * Copies the applications first available source into _pDsId.
        * @param[in] _pAppId The origin identity structure
        * @param[out] _pDsId the identity structure to copy data into
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_GetFirst(TW_IDENTITY *_pAppId,
                              TW_IDENTITY *_pDsId);

        /**
        * Copies the applications next available source into _pDsId. A call to
        * DSM_GetFirst must have been made at least once before calling this function.
        * @param[in] _pAppId The origin identity structure
        * @param[out] _pDsId the identity structure to copy data into
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 DSM_GetNext(TW_IDENTITY *_pAppId,
                             TW_IDENTITY *_pDsId);

        /**
        * This routine will check if the current default source matches the
        * applications supported groups.  If it does it will copy it into the default
        * Source's identity (_pDsId), otherwise this routine will search for a source that
        * does match the app's supported groups and copy it into _pDsId.
        * @param[in] _pAppId The application identity
        * @param[in,out] _pDsId A pointer reference that will be set to point to the default identity.
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 GetMatchingDefault(TW_IDENTITY *_pAppId,
                                    TW_IDENTITY *_pDsId);

        /**
        * Return back the tw_identity of the current source.  In state 3
        * this will be the default source.  In state 4 this will be the
        * currently opened source.
        * @param[in] _pAppId The application identity
        * @param[in,out] _pDsId A pointer reference that will be set to point to the current identity.
        * @return a valid TWRC_xxxx return code
        */
        TW_INT16 GetIdentity(TW_IDENTITY *_pAppId,
                             TW_IDENTITY *_pDsId);

        /**
        * prints to stdout information about the triplets.
        * @param[in] _pOrigin the Orgin to print the Product Name
        * @param[in] _pDest the Destination to print the Product Name
        * @param[in] _DG the Data Group
        * @param[in] _DAT the Data Argument Type
        * @param[in] _MSG the Message
        * @param[in] _pData the Data
        * @return return true if actually printed triplet
        */
        bool printTripletsInfo(const TW_IDENTITY *_pOrigin,
                               const TW_IDENTITY *_pDest,
                               const TW_UINT32 _DG,
                               const TW_UINT16 _DAT,
                               const TW_UINT16 _MSG,
                               const TW_MEMREF _pData);

        /**
        * prints to stdout information about result of processing the triplets.
        * @param[in] _DG the Data Group
        * @param[in] _DAT the Data Argument Type
        * @param[in] _MSG the Message
        * @param[in] _pData the Data
        * @param[in] _RC the Return Code after 
        */
        void printResults(const TW_UINT32 _DG,
                          const TW_UINT16 _DAT,
                          const TW_UINT16 _MSG,
                          const TW_MEMREF _pData,
                          const TW_UINT16 _RC);

        /**
        * Translates the _MSG passed in into a string and returns it
        * @param[out] _szMsg string to copy into
        * @param[in] _nChars max chars in _szMsg
        * @param[in] _MSG the TWAIN message to translate
        */
        void StringFromMsg(char *_szMsg,
                           const int _nChars,
                           const TW_UINT16 _MSG);

        /**
        * Translates the _DAT passed in into a string and returns it
        * @param[out] _szDat string to copy into
        * @param[in] _nChars max chars in _szDat
        * @param[in] _DAT the TWAIN data argument type to translate
        */
        void StringFromDat(char *_szDat,
                           const int _nChars,
                           const TW_UINT16 _DAT);

        /**
        * Translates the _DG passed in into a string and returns it
        * @param[out] _szDg string to copy into
        * @param[in] _nChars max chars in _szDg
        * @param[in] _DG the TWAIN data group to translate
        */
        void StringFromDg(char *_szDg,
                          const int _nChars,
                          const TW_UINT32 _DG);

        /**
        * Translates the _Cap passed in into a string and returns it
        * @param[out] _szCap string to copy into
        * @param[in] _nChars max chars in _szCap
        * @param[in] _Cap the TWAIN Capability to translate
        */
        void StringFromCap(char *_szCap,
                           const int _nChars,
                           const TW_UINT16 _Cap);

        /**
        * Translates the _ConType and _hContainer passed in into a string and returns it
        * @param[out] _szConType string to copy into
        * @param[in] _nChars max chars in _szCap
        * @param[in] _ConType the TWAIN Container Type to translate
        */
        void StringFromConType(char     *_szConType,
                               const int       _nChars,
                               const TW_UINT16 _ConType);

        /**
        * Translates the rc passed in into a string and returns it
        * @param[out] _szRc string to copy into
        * @param[in] _nChars max chars in szRc
        * @param[in] _rc the TWAIN Return Code to translate
        */
        void StringFromRC(char *_szRc,
                          const int _nChars,
                          const TW_UINT16 _rc);

        /**
        * Translates the Condition Code passed in into a string and returns it
        * @param[out] _szCondCode string to copy into
        * @param[in] _nChars max chars in szRc
        * @param[in] _cc the TWAIN Condition Code to translate
        */
        void StringFromConditionCode(char *_szCondCode,
                                     const int _nChars,
                                     const TW_UINT16 _cc);


    //
    // All of our attributes should be private.  Encapsulation
    // is a good thing...  :)
    //
    private:

        /*
        **  If you add a class in future, declare it here and not
        **  in the pod, or the memset we do on pod will ruin your
        **  day...
        */

        /**
        *  We use a pod system because it help prevents us from
        *  making dumb initialization mistakes.
        */
        struct _pod
        {
            /**
            * The class takes care of our list of applications and drivers.
            */
            CTwnDsmApps *m_ptwndsmapps;

            /**
            * The path to the default DS.  The Default DS is identified when
            * the DSM is opened.  A new Default is saved if SelectDlg is used.
            * So this value will be compared against DsGetPath()...
            */
            char m_DefaultDSPath[FILENAME_MAX];

            /**
            * The next id to test for GetFirst/GetNext...
            */
            TWID_T m_nextDsId;

            /**
            * The DS ID we end up with from SelectDlgProc.  This is only
            * used on the Windows platform.
            */
            TW_IDENTITY *m_pSelectDlgDsId;

            /**
            * The Application ID we're using inside of SelectDlgProc. This
            * is only used on the Windows platform.
            */
            TW_IDENTITY *m_pSelectDlgAppId;
        } pod; /**< Pieces of Data for the DSM class*/
};


#endif // __DSM_H__
