/*->c.glue */

/* #define DEBUG */      /* Show errors that result from application */
                         /* calls to Source Manager */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "swis.h"
#include "h.os"
#include "h.bbc"
#include "h.wimpt"
#include "h.werr"
#include "h.kernel"



#include "h.TWAIN"  /* for TW data type defines */
#include "h.glue"   /* for function prototypes */
#include "h.arc"
#include "h.con"    /* common lib of container routines */



/* Globals to this module ONLY */

static BOOL TWDSMOpen = FALSE;  /*glue code flag for an Open Source Manager*/
static BOOL TWSelectOpen = FALSE;
static int  apptaskhandle;      /* global for window handle */

/* Globals */
static BOOL TWDSOpen = FALSE;    /* glue code flag for an Open Source */
       TW_IDENTITY appID;
       TW_IDENTITY dsID;         /* storage for App and DS (Source) states */




#ifdef DEBUG

static void Explain_Error(char *state);



/* Defines for the current_state table used in Explain_Error */
#define E_CLOSEDSM          (char *)"Error Closing DSM."
#define E_CLOSEDS           (char *)"Error Closing DS."
#define E_USERSELECT        (char *)"Error Accessing DS."
#define E_SETUPMEMXFER      (char *)"Error Setting up memory transfer."
#define E_DISABLEDS         (char *)"Error Disabling DS."
#define E_ENABLEDS          (char *)"Error Enabling DS."
#define E_GETFIRST          (char *)"Error Getting first Data Source."
#define E_GETNEXT           (char *)"Error Getting Next Data Source."
#define E_CAPPIXELGET       (char *)"Error Getting Cap Pixel Type."
#define E_CAPPIXELSET       (char *)"Error Setting Cap Pixel Type."


#endif

/***********************************************************************
 * FUNCTION: TWInitialize
 * 
 * ARGS:    pIdentity information about the App
 *          hMainWnd  handle to App main window
 * 
 * RETURNS: none
 *
 * NOTES:   This simple copy to a static structure, appID, does not support
 *          multiple connections. TODO, upgrade.
 *
 */

void TWInitialize(pTW_IDENTITY pIdentity,int taskhandle)
{
 appID=*pIdentity;               /* get copy of app info */
 apptaskhandle=taskhandle;      /* get copy of app window handle */
} 



/***********************************************************************
 * FUNCTION: TWOpenDSM
 * 
 * ARGS:    none
 * 
 * RETURNS: current state of the Source Manager
 *
 * NOTES:     1). be sure SM is not already open
 *            2). explicitly load the .DLL for the Source Manager
 *            3). call Source Manager to:
 *                - opens/loads the SM
 *                - pass the handle to the app's window to the SM
 *                - get the SM assigned appID.id field
 *
 */


os_error * TWOpenDSM (void)
{
 TW_UINT16     dcRC;
 _kernel_oserror  *  err;
 _kernel_swi_regs    rx;


 err=NULL;

 /* Only open SM if currently closed */                                          if(TWDSMOpen!=TRUE)
 {
  /* This call performs four important functions:
     - opens/loads the SM
     - passes the handle to the app's window to the SM
     - returns the SM assigned appID.id field
     - be sure to test the return code for SUCCESSful open of SM
  */

  rx.r[0]=0x12;
  rx.r[1]=(int)"TWAIN";

  if((err=_kernel_swi(OS_Module,&rx,&rx))!=NULL)
  {
   rx.r[0]=0x1;
   rx.r[1]=(int)"<TWAIN$Path>TWAIN";
   _kernel_swi(OS_Module,&rx,&rx);


   rx.r[0]=0x12;
   rx.r[1]=(int)"TWAIN";
   err=_kernel_swi(OS_Module,&rx,&rx);
  }

  if(!err)
  {
   dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_PARENT,MSG_OPENDSM,
                                                 (TW_MEMREF)&apptaskhandle);


   switch(dcRC)
   {
    case TWRC_SUCCESS:
                     /* Needed for house keeping.  Do single open and do not */
                     /* close SM which is not already open .... */
                      TWDSMOpen = TRUE;
                      break;

    case TWRC_FAILURE:
              default:
                     /* Trouble opening the SM, inform the user */
                      TWDSMOpen = FALSE;
                      #ifdef DEBUG
                      Explain_Error(NULL);
                      #endif
                      break;
   }
  }
  #ifdef DEBUG
  else
  Explain_Error(NULL);
  #endif
 }

 /* Let the caller know what happened */
 return((os_error *)err);
}



/***********************************************************************
 * FUNCTION: TWCloseDSM
 * 
 * ARGS:    none
 * 
 * RETURNS: current state of Source Manager
 *
 * NOTES:    1). be sure SM is already open
 *           2). call Source Manager to:
 *               - request closure of the Source identified by appID info
 *
 */

BOOL TWCloseDSM(void)
{
 TW_UINT16 dcRC;

 /* Only close something which is already open */
 if(TWDSMOpen==TRUE)
 {
  /* This call performs one important function:
     - tells the SM which application, appID.id, is requesting SM to close
     - be sure to test return code, failure indicates SM did not close !!
  */

  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_PARENT,MSG_CLOSEDSM,
                                                             &apptaskhandle);

  if(dcRC!=TWRC_SUCCESS)
  {
   /* Trouble closing the SM, inform the user */
   #ifdef DEBUG
   Explain_Error(E_CLOSEDSM);
   #endif
  }
  else
  {
   TWDSMOpen=FALSE;        
            /* Explicitly free the SM library */
  }
 }
           /* Let the caller know what happened */
 return (TWDSMOpen);
}


/***********************************************************************
 * FUNCTION: TWIsDSMOpen
 * 
 * ARGS:    none
 * 
 * RETURNS: current state of Source Manager (open/closed)
 *
 * NOTES:   Just a way to reduce the number of global vars and keep the
 *          state of SM information local to this module.  Let the caller,
 *          app, know current state of the SM.
 */

BOOL TWIsDSMOpen(void)
{
 return(TWDSMOpen);
}



/***********************************************************************
 * FUNCTION: TWOpenDS
 *
 * ARGS:    none
 *
 * RETURNS: current state of select Source
 *
 * NOTES:    1). only attempt to open a source if it is currently closed
 *           2). call Source Manager to:
 *                - open the Source indicated by info in dsID
 *                - SM will fill in the unique .Id field
 */

BOOL TWOpenDS(void)
{
 TW_UINT16  dcRC=TWRC_FAILURE;

 if(TWDSOpen==FALSE)
 {
  /* This will open the Source. */

  dsID.Id=0;
  dsID.ProductName[0]=NULL;
  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_IDENTITY,MSG_OPENDS,&dsID);

  if(dcRC==TWRC_SUCCESS)
  {
   /* do not change flag unless we successfully open */
   TWDSOpen = TRUE;
  }

  #ifdef DEBUG
  else
  {
   /* Trouble opening the Source */
   Explain_Error(NULL);
  }
  #endif
 }
 return(TWDSOpen);
}



/***********************************************************************
 * FUNCTION: TWCloseDS
 *
 * ARGS:    none
 *
 * RETURNS: none
 *
 * NOTES:    1). only attempt to close an open Source
 *           2). call Source Manager to:
 *                - ask that Source identified by info in dsID close itself
 */


void TWCloseDS(void)
{
 TW_UINT16         dcRC=TWRC_FAILURE;

 if(TWDSOpen==TRUE)
 {
  /* Close an open Source */
  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_IDENTITY,MSG_CLOSEDS,&dsID);

  /* show error on close */

  if(dcRC==TWRC_SUCCESS) TWDSOpen=FALSE;
  #ifdef DEBUG
  else
  Explain_Error(E_CLOSEDS);
  #endif
 }
 return;
}



/***********************************************************************
 * FUNCTION: TWEnableDS
 *
 * ARGS:    none
 *
 * RETURNS: BOOL for TRUE=open; FALSE=not open/fail
 *
 * NOTES:    1). only enable an open Source
 *           2). call the Source Manager to:
 *                - bring up the Source's User Interface
 */


BOOL TWEnableDS(int nouserinterface)
{
 BOOL              Result=FALSE;
 TW_UINT16         dcRC=TWRC_FAILURE;
 TW_USERINTERFACE  dcUI;

 /* only enable open Source's */

 if(TWDSOpen==TRUE)
 {
  /*This will display the Source User Interface. The Source should only display
    a user interface that is compatible with the group defined
    by appID.SupportedGroups (in our case DG_IMAGE | DG_CONTROL) */

  dcUI.hParent=&apptaskhandle;
  dcUI.ShowUI = nouserinterface?FALSE:TRUE;
  dcRC=DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_USERINTERFACE,MSG_ENABLEDS,
                                                           (TW_MEMREF)&dcUI);

  if(dcRC==TWRC_SUCCESS) Result=TRUE; 
  #ifdef DEBUG
  else
  Explain_Error(E_ENABLEDS);
  #endif
 }
 return(Result);
}



/*********************************************************************
 * FUNCTION: TWDisableDS
 *
 * ARGS:    none
 *
 * RETURNS: none
 *
 * NOTES:    1). only disable an open Source
 *           2). call Source Manager to:
 *                - ask Source to hide it's User Interface
 */

void TWDisableDS(void)
{
 TW_UINT16         dcRC=TWRC_FAILURE;
 TW_USERINTERFACE  dcUI;

 /* only disable open Source's */
 if(TWDSOpen==TRUE)
 {
  /* Hide the Source UI */
  dcUI.hParent=&apptaskhandle;
  dcUI.ShowUI=FALSE;

  dcRC=DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_USERINTERFACE,MSG_DISABLEDS,
                                                        (TW_MEMREF)&dcUI);

  #ifdef DEBUG
  if(dcRC!=TWRC_SUCCESS) Explain_Error (E_DISABLEDS);
  #endif
 }
 return;
}



/***********************************************************************
 * FUNCTION: TWIsDSOpen
 *
 * ARGS:    none
 *
 * RETURNS: current state of the Source
 *
 * NOTES:    Just a way to reduce the number of global vars and keep the
 *           state of Source information local to this module.  Let the caller,
 *           app, know current state of the Source.
 */

BOOL TWIsDSOpen(void)
{
 return(TWDSOpen);
}



/***********************************************************************
 * FUNCTION: TWGetPixelType
 *
 * ARGS:    none
 *
 * RETURNS: the current pixel type
 *
 * NOTES:   1). selects the CAP type, ICAP_PIXELTYPE
 *          2). calls the Source Manager to:
 *              - have Source build a container who's contents will be the
 *                current pixel type of the selected Source
 *          3). use common lib routine to extract value from the container
 *              type built, Source's choice, by the Source
 */


TW_UINT32 TWGetPixelType(void)
{
 TW_CAPABILITY   dcCap;
 TW_UINT16       dcRC=TWRC_FAILURE;
 TW_INT32        back=0L;

 /* App ONLY fills in the CAP_ of interest */
 dcCap.Cap = ICAP_PIXELTYPE;

 /* Have Source build a container and fill it with the CAP_ of interest */

 dcRC=DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_CAPABILITY,MSG_GET,
                                                          (TW_MEMREF)&dcCap);

 #ifdef DEBUG
 if(dcRC!=TWRC_SUCCESS) Explain_Error(E_CAPPIXELGET);
 #endif

 /* Add code to do a switch on contype.  Then call the appropriate extract
    routine from the common container code routines. */

 switch(dcCap.ConType)
 {
     case TWON_ONEVALUE:
                        ExtractOneValue(&dcCap,&back);
                        break;

  case TWON_ENUMERATION:
                        /* get a single value from the enumeration container */
                        ExtractEnumerationValue (&dcCap, &back, 1);
                        break;

                default:
                        break;
 }

 /* App is ALWAYS responsible for cleaning up the container */

 if(dcCap.hContainer) GlobalFree(dcCap.hContainer);

 return(back);
}



/***********************************************************************
 * FUNCTION: TWSetPixelType
 *
 * ARGS:    ItemType    TWTY_xxx, type of the ItemValue
 *          ItemValue   the none TWPT_xxx constant for pixel type, see TW.H
 *
 * RETURNS: dcRC TWAIN status return code
 *
 * NOTES:   1). build up a container of type OneValue filled with ItemType
 *              and ItemValue
 *          2). call Source Manager to:
 *              - give the Source access to the pixel type you wish to set
 *                the Source to, assumes you have asked the Source previously
 *                if it can handle the pixel type you are now setting
 */


TW_UINT16 TWSetPixelType(TW_UINT16 ItemType,TW_INT32 ItemValue)
{
 TW_CAPABILITY     dcCap;
 TW_UINT16         dcRC = TWRC_FAILURE;

 dcCap.Cap        = ICAP_PIXELTYPE;   /* id of cap you want */
 dcCap.ConType    = TWON_ONEVALUE;    /* container type */

 /* App must build the container & pass the container handle to the Source */


 BuildUpOneValue(&dcCap, ItemType, ItemValue);

 /* It is assumed that the Source will read the container NOW */

 dcRC=DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_CAPABILITY,MSG_SET,
                                                       (TW_MEMREF)&dcCap);

 #ifdef DEBUG
 if(dcRC!=TWRC_SUCCESS) Explain_Error(E_CAPPIXELSET);
 #endif

  /* NOTE: the App ALWAYS is required to Free the container */
 if(dcCap.hContainer) GlobalFree(dcCap.hContainer);

 return(dcRC);
}



/***********************************************************************
 * FUNCTION: TWSelectDS
 *
 * ARGS:    none
 *
 * RETURNS: dcRC TWAIN status return code
 *
 * NOTES:   1). call the Source Manager to:
 *              - have the SM put up a list of the available Sources
 *              - get information about the user selected Source from
 *                NewDSIdentity, filled by Source
 */


static TWselectfn TWSelect;

BOOL TWSelectDS(TWselectfn TWSelectx)
{
 TW_UINT16 dcRC;
 TW_IDENTITY NewDSIdentity;

 dcRC=TWRC_SUCCESS;

 if(TWSelectOpen!=TRUE)
 {
  TWSelect=TWSelectx;

  /* I will settle for the system default.  Shouldn't I get a highlight
     on system default without this call? */

  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_IDENTITY,MSG_GETDEFAULT,
                                               (TW_MEMREF)&NewDSIdentity);


    /* This call performs one important function:
    - should cause SM to put up dialog box of available Source's
    - tells the SM which application, appID.id, is requesting, REQUIRED
    - returns the SM assigned NewDSIdentity.id field, you check if changed
      (needed to talk to a particular Data Source)
    - be sure to test return code, failure indicates SM did not close !!
    */


  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_IDENTITY,MSG_USEROPEN,
                                               (TW_MEMREF)&NewDSIdentity);

    /* Check if the user changed the Source and react as apporpriate.
    - TWRC_SUCCESS, log in new Source
    - TWRC_CANCEL,  keep the current Source
    - default,      check down the codes in a status message, display result
    */


  switch(dcRC)
  {
    case TWRC_SUCCESS:
                      dsID=NewDSIdentity;
                      TWSelectOpen=TRUE;
                      break;
      
     case TWRC_CANCEL:
                      break;

              default:
                      #ifdef DEBUG
                      Explain_Error(E_USERSELECT);
                      #endif       
                      break;
  }

  /* Let the caller know what happened */
 }
 return(dcRC);
}



void TWSelectTerminate(int ok)
{
 TW_UINT16   dcRC;
 TW_IDENTITY NewDSIdentity;

 if(TWSelectOpen)
 {
  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_IDENTITY,MSG_USERCLOSE,
                                               (TW_MEMREF)&NewDSIdentity);

  if(dcRC==TWRC_SUCCESS && ok) dsID=NewDSIdentity;

  TWSelectOpen=FALSE;
  if(TWSelect) TWSelect(ok);
 }
}


int TWIsTWSelectOpen(void)
{
 return(TWSelectOpen);
}


/*************************************************************************/

/* Should Select Source be greyed out? */

int TW_GreySelect(void)
{
 if(!TWDSMOpen) return(1);

 return(0);
}


/* Should Acquire be greyed out? */
int TW_GreyAcquire(void)
{
 if(!TWDSOpen && !TWDSMOpen) return(1);

 return(0);
}


/*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*/
/* These routines would be better termed extras or interface routines vs.
   the label glue for the above routines.
*/
#ifdef DEBUG
/***********************************************************************
 * FUNCTION: Explain_Error
 *
 * ARGS:    hWnd    handle to a display window
 *          *state  ptr to a character string from dca_glue.h which defines
 *                  these error strings.  Iff state is NULL then no extra 
 *                  text, probably using the standard App error messages,
 *                  (See dcastrg.rc, stringtable)
 *
 * RETURNS: none
 *
 */


static char * IDSstring[]=
{
  "!TWAIN",
  "TWAIN Sample App",
  "Sorry, could not find the TWAIN Source Manager (!TWAIN).  Please open a window on !TWAIN",
  "Sorry, no TWAIN Source found in the !TWAIN directory.  Refer to your TWAIN Source installation instructions.",
  "Sorry, there is not enough memory to Acquire.",
  "Sorry the TWAIN Source %s is already in use.",
  "Bummer, there is a TWAIN error!",
  "Sorry, capability error has occurred.",
  "Sorry, protocol error has occurred.",
  "Sorry, bad value.",
  "",
  "Sorry, DG DAT MSG is out of expected sequence.",
  "Sorry, detailed error status returns success.",
  "Sorry, could not get detailed Error Code.",
  "Sorry, Unknown error code returned by DG_CONTROL, DAT_STATUS, MSG_GET."

};


typedef enum
{
  IDS_DSMNAME,
  IDS_WINDOWTITLE,
  IDS_NODSM,
  IDS_NODS,
  IDS_LOWMEMORY, 
  IDS_MAXCONNECTIONS,
  IDS_BUMMER,
  IDS_BADCAP,
  IDS_BADPROTOCOL,
  IDS_BADVALUE,
  IDS_OPERATIONERROR,
  IDS_SEQERROR,
  IDS_SUCCESS,
  IDS_NODETAIL,
  IDS_DEFAULT

} IDScode;




static void Explain_Error(char *state)
{
 TW_STATUS         dcStatus;
 TW_UINT16         dcRC;
 char              Details[255];
 IDScode           code;

 /* Put in detail of current operation */

 if(state) strcpy(Details,state);
 else      Details[0]=0;


 /* determine details of failure from SM */
 dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_STATUS,MSG_GET,(TW_MEMREF)&dcStatus);

 if(dcRC==TWRC_SUCCESS)
 {
  switch(dcStatus.ConditionCode)
  {
                case TWCC_BADCAP:
                code = IDS_BADCAP;
                break;

                case TWCC_BADPROTOCOL:
                code = IDS_BADPROTOCOL;
                break;

                case TWCC_BADVALUE:
                code = IDS_BADVALUE;                
                break;

                case TWCC_BUMMER:   
                code = IDS_BUMMER;
                        break;

                        case TWCC_OPERATIONERROR:
  /* Error in the DS or DSM which was already reported to the user.  The
     App should not be redundant.  The operation failed. */
                 goto no_error;
                 break;

                        case TWCC_LOWMEMORY:
                code = IDS_LOWMEMORY;                
                        break;

                        case TWCC_MAXCONNECTIONS:
                code = IDS_MAXCONNECTIONS;                  
                break;

                        case TWCC_NODS:
                code = IDS_NODS;
                        break;

                        case TWCC_SEQERROR:
                code = IDS_SEQERROR;
                        break;

                        case TWCC_SUCCESS:
                code = IDS_SUCCESS;
                        break;

                        default:
                code = IDS_DEFAULT;
                        break;
  }
 }
 else
 {
  code=IDS_NODETAIL;
 }
    

 werr(0,"TWAIN Error:%s %s",Details,IDSstring[code]);

 /* don't report error */
 no_error:
          return;

}  
#endif


