/*->c.acq */

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


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


#include "TWAIN.h"

#include "h.con"

#include "h.arc"
#include "glue.h"
#include "acq.h"



static TWAcquireblockstr * TWAcquireblockp;


/******************************************************************************/
static void icapunits(TW_FIX32 * icaps,int ourunits)
{
 icaps->Whole=ourunits/72000;
 icaps->Frac=((ourunits %72000)*(0x10000/64))/(72000/64);
}

static int ourunits(TW_FIX32 * icaps)
{
 return(icaps->Whole*72000+(icaps->Frac*(72000/64))/(0x10000/64));
}

static void acqsetarea(void)
{
 TW_IMAGELAYOUT ImageLayout;

 icapunits(&ImageLayout.Frame.Top,TWAcquireblockp->area.y1);
 icapunits(&ImageLayout.Frame.Bottom,TWAcquireblockp->area.y0);
 icapunits(&ImageLayout.Frame.Left,TWAcquireblockp->area.x0);
 icapunits(&ImageLayout.Frame.Right,TWAcquireblockp->area.x1);

 ImageLayout.DocumentNumber=1;
 ImageLayout.PageNumber=1;
 ImageLayout.FrameNumber=1;

 DSM_Entry(&appID,&dsID,DG_IMAGE,DAT_IMAGELAYOUT,MSG_SET,
                                                    (TW_MEMREF)&ImageLayout);
}


static void acqgetarea(void)
{
 TW_IMAGELAYOUT ImageLayout;

 if(DSM_Entry(&appID,&dsID,DG_IMAGE,DAT_IMAGELAYOUT,MSG_GET,
                                    (TW_MEMREF)&ImageLayout)==TWRC_SUCCESS)
 {
  TWAcquireblockp->area.y1=ourunits(&ImageLayout.Frame.Top);
  TWAcquireblockp->area.y0=ourunits(&ImageLayout.Frame.Bottom);
  TWAcquireblockp->area.x0=ourunits(&ImageLayout.Frame.Left);

  TWAcquireblockp->area.x1=ourunits(&ImageLayout.Frame.Right);
 }
}


static os_error * acqgetpalette(void)
{
 TW_PALETTE8 Palette;
 int         i;
 int         nc;
 int         paltab[256];
 os_error *  err;

 err=NULL;

 if(DSM_Entry(&appID,&dsID,DG_IMAGE,DAT_PALETTE8,MSG_GET,
                                    (TW_MEMREF)&Palette)==TWRC_SUCCESS)
 {
  nc=Palette.NumColors;
  for(i=0;i<nc;i++)
  {
   paltab[i]=(Palette.Colors[i].Channel1<<8)|
             (Palette.Colors[i].Channel2<<16)|
             (Palette.Colors[i].Channel3<<24);
  }

  if(TWAcquireblockp->TWpal) err=TWAcquireblockp->TWpal(paltab,nc);
 }

 return(err);
}

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

static TW_UINT32 TWGetXferType(void)
{
 TW_CAPABILITY   dcCap;
 TW_UINT16       dcRC=TWRC_FAILURE;
 TW_INT32        back=0;
 TW_INT32        current;

 dcCap.Cap=ICAP_XFERMECH;

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

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

  case TWON_ENUMERATION:
                        current=GetEnumerationCurrentIndex(&dcCap);
                        ExtractEnumerationValue(&dcCap,&back,(int)current);
    /*         dprintf(0,"current=%d back=%d",current,back); */

                        break;

                default:
                        break;
  }
 }


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

 return(back);
}



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

 dcCap.Cap=ICAP_XFERMECH;
 dcCap.ConType=TWON_ONEVALUE;

 BuildUpOneValue(&dcCap,ItemType,ItemValue);

 dcRC=DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_CAPABILITY,MSG_SET,
                                                       (TW_MEMREF)&dcCap);
 if(dcCap.hContainer) GlobalFree(dcCap.hContainer);
 return(dcRC);
}


static void acqsetxfermech(void)
{
/* dprintf(0,"set xfer %d %d",TWAcquireblockp->settransfertype,
                            TWAcquireblockp->transfertype);   */

 if(TWAcquireblockp->settransfertype)
   TWSetXferType(TWTY_UINT16,TWAcquireblockp->transfertype);

 TWAcquireblockp->transfertype=(int)TWGetXferType();


/* dprintf(0,"get xfer %d",TWAcquireblockp->transfertype); */
}


/*************************************************************************
 * FUNCTION: TWAcquire
 *
 * ARGS:    None
 * RETURNS: None
 *
 * NOTES:   - opendsm, open the Source Manager
 *          - opends, open the Source
 *          - enable Source
 *          - wait for a message from Source (usually XFERREADY)
 */

os_error * TWAcquire(TWAcquireblockstr * TWAcquireblock)
{
 os_error * err;

 err=NULL;

 if(!TWIsDSOpen())
 {
  TWAcquireblockp=TWAcquireblock;
  if((err=TWOpenDSM())==NULL)
  {
 /* Please note that another Source may change the system default while
    you are not looking and simply getting the default will not guarentee
    you get what you want.  Suggest saving the dsID structure privately
    after you open it to assure subsequent connections are to your intended
    Source -- ed
    Also note the the DSM will maintain a static list of all Sources in
    the system at the moment it is opened.  Changes in available Sources
    while the DSM is open may cause unpredictable results. -- ed
    force the opends to give me the system default
  */

   if(TWOpenDS()==TRUE)
   {
   /* tell source what we want */

    if(TWAcquireblock->setarea) acqsetarea();

    /* An EnableDS error should close the DS */
    if(TWEnableDS(TWAcquireblockp->nouserinterface)==FALSE)
    {
     TWCloseDS();
    }
   }
  }
 }

 return(err);
}



void TWAcquireDefault(TWAcquireblockstr * TWAcquireblock)
{
 memset(TWAcquireblock,0,sizeof(TWAcquireblockstr));
}



/*************************************************************************
 * FUNCTION: TWProcessMessage
 *
 * ARGS:    lpMsg  Pointer to Windows msg retrieved by GetMessage
 *          hWnd   Application's main window handle
 *
 * RETURNS: TRUE  if application should process message as usual
 *          FALSE if application should skip processing of this message
 *
 * NOTES:   1). be sure both Source Manager and Source are open
 *          2). two way message traffic:
 *              - relay windows messages down to Source's modeless dialog
 *              - retrieve TWAIN messages from the Source
 *
 * COMMENT: ProcessSourceMessage is designed for applications that can only
 * have one Source open at a time.  If you wish your application to have more
 * than one Source open at a time please consult the TWAIN ToolKit for
 * event handling instructions.
 */

BOOL TWProcessMessage(wimp_eventstr * wevent)
{
 TW_UINT16  dcRC=TWRC_NOTDSEVENT;
 TW_EVENT   dcEvent;

 /* Only ask Source Manager to process event if there is a Source connected. */

 if((TWIsDSMOpen()) && (TWIsDSOpen()))
 {
        /* A Source provides a modeless dialog box as its user interface.
         * The following call relays Windows messages down to the Source's
         * UI that were intended for its dialog box.  It also retrieves TWAIN
         * messages sent from the Source to our  Application.
         */

  dcEvent.pEvent=(TW_MEMREF)wevent;
  dcRC=DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_EVENT,MSG_PROCESSEVENT,
                                                        (TW_MEMREF)&dcEvent);


  switch(dcEvent.TWMessage)
  {
   case MSG_XFERREADY:
                      TWTransferImage();
                      break;

  case MSG_CLOSEDSREQ:
                      TWTerminate();
                      break;

        case MSG_NULL:
                      /* No message from the Source to the App */ 
                      break;

              default:
                      break;
  }
 }
 else
 if(TWIsTWSelectOpen())
 {
  dcEvent.pEvent=(TW_MEMREF)wevent;
  dcRC=DSM_Entry(&appID,NULL,DG_CONTROL,DAT_EVENT,MSG_PROCESSEVENT,
                                                        (TW_MEMREF)&dcEvent);

  switch(dcEvent.TWMessage)
  {
       case MSG_SELECT_OK:
                          TWSelectTerminate(1);
                          break;

   case MSG_SELECT_CANCEL:
                          TWSelectTerminate(0);
                          break;

            case MSG_NULL:
                          /* No message from the Source to the App */ 
                          break;

                  default:
                          break;
  }
 }

 return(dcRC==TWRC_DSEVENT);
}


/*************************************************************************
 *
 * FUNCTION: TWTransferImage
 *
 * ARGS:    hWnd
 *
 * RETURNS: none
 *
 * NOTES: 1). delete any bit maps laying around
 *        2). mention those who do not want Native need CAP nego. ICAP_XFERMECH
 *        3). get a little information about image, for form, I do not use it
 *        4). set up a for form loop to pull image(s) from the Source
 *        5). call for GetCompleteImage from Source
 *        6). be sure to send a MSG_ENDXFER as a seperator between images
 *        7). after the images are transfered I like to shut down the Source
 *            TWTerminate
 *
 * COMMENTS: Setup for a transfer in the routine called as a response to
 * XFERREADY.  Then has a nested loop do/while on the routine which
 * actually pulls in the image or GetCompleteImage.  The GetCompleteImage
 * routine also deals with the cancel, xferdone, success messages from
 * Source.
 *
 */


void TWTransferImage(void)
{
 os_error        * err;
 TW_IMAGEINFO      dcImageInfo;
 TW_PENDINGXFERS   dcPendingXfer;
 TW_SETUPMEMXFER   dcSetupMemXfer;

 err=NULL;

 /* NOTE: you must do the clean up of old images yourself */

 /* explicitly initialize the our flags */

 dcPendingXfer.Count=0;

 /* Tell the Source what type of transfer you want.  I use the default, */
 /* NATIVE so no set of ICAP_XFERMECH is required */

 acqsetxfermech();

 /* Native also negates the need to ask for a pixel type ICAP_PIXELTYPE
    TWSetPixelType(TWTY_UINT16, (TW_UINT32)TWPT_RGB);
    pixeltype=TWGetPixelType(); */


 /* Get the image information, nice to know a little about the image the Source
    will be sending */


 if(DSM_Entry(&appID,&dsID,DG_IMAGE,DAT_IMAGEINFO,MSG_GET,
                        (TW_MEMREF)&dcImageInfo)==TWRC_SUCCESS)
 {
  int size;

  size=(int)dcImageInfo.ImageWidth*(int)dcImageInfo.ImageLength*
       (int)dcImageInfo.BitsPerPixel /* *(int)dcImageInfo.SamplesPerPixel */;

  size/=8;

  acqgetarea();

  if(TWAcquireblockp->TWinfo)
  {
   err=TWAcquireblockp->TWinfo((int)dcImageInfo.ImageWidth,
                             (int)dcImageInfo.ImageLength,
     (int)dcImageInfo.BitsPerPixel /* *(int)dcImageInfo.SamplesPerPixel */,
                             (int)dcImageInfo.XResolution.Whole,
                             (int)dcImageInfo.YResolution.Whole,
                             (int)dcImageInfo.Planar);

  }

  if(!err) err=acqgetpalette(); 
                         /* at least give a chance for image to be set up */

  if(!err)
  {
   if(TWAcquireblockp->transfertype==TWSX_NATIVE)
   {
    err=TWAcquireblockp->TWalloc(size);
   }
   else
   if(TWAcquireblockp->transfertype==TWSX_MEMORY)
   {
    DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_SETUPMEMXFER,MSG_GET,
                                                (TW_MEMREF)&dcSetupMemXfer);

    err=TWAcquireblockp->TWalloc((int)dcSetupMemXfer.Preferred);
   TWAcquireblockp->memory.memxfer.size=(int)dcSetupMemXfer.Preferred;
   }
  }

  /* Ask Source if more images are coming. I only support one image per 
     transfer in this example but do/while loop is included to help? 
     those who wish to support more than one.
   */

  if(!err)
  {
   do 
   {
    TWGetCompleteImage();
    /* Required for proper 6<->7 state transitions */
    if(DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_PENDINGXFERS,MSG_ENDXFER,
                               (TW_MEMREF)&dcPendingXfer) != TWRC_SUCCESS)
    {
     dcPendingXfer.Count=0;  /* trash remaining images */
    }
   } while(dcPendingXfer.Count!=0);
  }
  else   /* got an error before getting image, reset the source */
  {
   dcPendingXfer.Count=0;

   DSM_Entry(&appID,&dsID,DG_CONTROL,DAT_PENDINGXFERS,MSG_RESET,
                               (TW_MEMREF)&dcPendingXfer);

   if(TWAcquireblockp->TWerror) TWAcquireblockp->TWerror(err);
  }
  /* shut down the Source if there are no more images pending */
  TWTerminate();
 }
}


/*************************************************************************
 * FUNCTION: GetCompleteImage
 *
 * ARGS:    hWnd    handle to window
 *
 * RETURNS: none
 *
 * NOTES:   1). ask the Source to start sending the pending image.  We
 *              were told about this by receipt of a XFERREADY message
 *              from the Source.
 *
 * COMMENTS: In a Native transfer I expect ONLY a handle to a bit map
 * to be returned so the WHILE loop is NOT needed here.  But for memory
 * transfers, slice/tile by slice/tile the loop would be required as
 * well as code to support the additional listed TWRC_XXX values.  The
 * additional TWRC_XXX values are TWRC_CANCEL, user cancelled an in
 * progress image; TWRC_FAILURE, transfer crashed; TWRC_SUCCESS got a
 * slice/tile continue.
 */


static void GetCompleteImageNative(void)
{
 TW_UINT16   dcRC;

 dcRC=TWRC_SUCCESS;

 while(dcRC!=TWRC_XFERDONE)
 {
  dcRC=DSM_Entry(&appID,&dsID,DG_IMAGE,DAT_IMAGENATIVEXFER,MSG_GET,
                               (TW_MEMREF)&TWAcquireblockp->memory.native.sp);

  switch(dcRC)
  {
   case TWRC_XFERDONE:
                      TWAcquireblockp->TWtrans(0,1,0,0);
                      break;

     case TWRC_CANCEL:
                      /* the user canceled or wants to rescan the image */
    case TWRC_FAILURE:
              default:
               /* something wrong, abort the transfer and delete the image */
                      TWAcquireblockp->TWtrans(0,-1,0,0);
                      break;
  }
 }
}


static void GetCompleteImageMemXfer(void)
{
 os_error      * err;
 TW_UINT16       dcRC;
 TW_IMAGEMEMXFER dcIMAGEMEMXFER;
 int             i;
 int             failed;

 memset(&dcIMAGEMEMXFER,0,sizeof(dcIMAGEMEMXFER));

 dcIMAGEMEMXFER.Memory.Flags=TWMF_APPOWNS;
 dcIMAGEMEMXFER.Memory.Length=TWAcquireblockp->memory.memxfer.size;
 dcIMAGEMEMXFER.Memory.TheMem=TWAcquireblockp->memory.memxfer.buffer;

 dcRC=TWRC_SUCCESS;
 failed=0;
 err=NULL;

 while((dcRC!=TWRC_XFERDONE) && !failed && !err)
 {
  dcRC=DSM_Entry(&appID,&dsID,DG_IMAGE,DAT_IMAGEMEMXFER,MSG_GET,
                               (TW_MEMREF)&dcIMAGEMEMXFER);

  switch(dcRC)
  {
    case TWRC_SUCCESS:    /* got another block */
                     err=
                     TWAcquireblockp->TWtrans((int)dcIMAGEMEMXFER.BytesWritten,
                                               0, 
                                               (int)dcIMAGEMEMXFER.BytesPerRow,
                                               (int)dcIMAGEMEMXFER.Rows);
                      break;


   case TWRC_XFERDONE:
                     err=
                     TWAcquireblockp->TWtrans((int)dcIMAGEMEMXFER.BytesWritten,
                                               1, 
                                               (int)dcIMAGEMEMXFER.BytesPerRow,
                                               (int)dcIMAGEMEMXFER.Rows);
                      break;

     case TWRC_CANCEL:
                      /* the user canceled or wants to rescan the image */
    case TWRC_FAILURE:
              default:
               /* something wrong, abort the transfer and delete the image */
               /* pass a null ptr back to App */
                      TWAcquireblockp->TWtrans(0,-1,0,0);
                      failed=1;
                      break;
  }

  if(TWAcquireblockp->TWpoll)
  {
   for(i=0;i<5;i++) TWAcquireblockp->TWpoll();
  }
 }
}
                                                                               

void TWGetCompleteImage(void)
{
 switch(TWAcquireblockp->transfertype)
 {
  case TWSX_NATIVE:
                   GetCompleteImageNative();
                   break;

  case TWSX_MEMORY:
                   GetCompleteImageMemXfer();
                   break;
 }
}


/***********************************************************************
 * FUNCTION:    TWTerminate
 * 
 * ARGS:    none
 * 
 * RETURNS: none
 *
 * NOTES:    To properly back out of a connection to a Source:
 *                TWDisableDS      disable the Source
 *                TWCloseDS        close the Source
 *                TWCloseDSM       close the Source Manager
 *
 * The simple parameterless calls do not support multiple connections.
 */

void TWTerminate(void)
{
 TWDisableDS();     /* this is required by 'the rules' */
 TWCloseDS();
 TWCloseDSM();

 if(TWAcquireblockp && TWAcquireblockp->TWend) TWAcquireblockp->TWend();
}

