/*
   Name: SANED - TWAIN
   Copyright: 
   Author: Igor Farinic
   Date: 03.03.07 18:37
   Description: SANE backend that is used by SANED. This backend loads first TWAIN DataSource on computer where daemon is started.

   The GNU C 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.

   The GNU C 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 the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA. 
*/    

//TODO: Extend to provide to sane client all data sources available on server. For this purpose 
// triplets DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST and DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT can be used.
// Application can itterate through this list. And if sane client chooses to use one of them 
// the application then can e.g. iterate again and open the rigth one

//TODO: Check if application is uses MSG_ENDXFER, if not extend it to use it

//TODO: Add parameters negotiation before transfer - twain image_info structure 
// returned from DS describes the image properties and its transfer
//TODO: Here also check PLANAR parameter, 
// if = FALSE then image is transferred in chunks RRRGGGBBB, 
// if = TRUE then it is tranferred planar RGBRGB
// -- maybe DIB is create in RGBRGB format, check it

//TODO: There seems to be problem with socket timeouts, while waitng for data from twain

#ifdef _AIX
# include "../include/lalloca.h" /* MUST come first for AIX! */
#endif

#include <windows.h>
#include "../twcommon/twain.h"
#include "../twcommon/twndebug.h"

#include "../include/sane/sane.h"
#include "../include/sane/config.h"
#include "../include/sane/saneopts.h"
#include "../include/lalloca.h"

#include "../include/sane/sanei.h"
#include "../include/sane/sanei_config.h"

#define BACKEND_NAME    twain
#include "../include/sane/sanei_backend.h"

#define TWAIN_MAJOR 1
#define TWAIN_MINOR 0
#define TWAIN_PATCH 0

#define TWAIN_CONFIG_FILE "twain.conf"

#define M_COLOR    "Color" 

#define MM_PER_INCH	25.4 
#define default_pixels_x 2800
#define default_pixels_y 3600
/*******************************************************************/
/***** device structure (one for each device discovered)       *****/
/*******************************************************************/

enum TWAIN_Option
{
  OPT_NUM_OPTS = 0,
  
  OPT_MODE_GROUP,
  OPT_MODE,             /* -a,b,c,g */
//  OPT_HALFTONE_PATTERN, /* -H */
  OPT_RESOLUTION,       /* -r */
//  OPT_EXP_RES,
//  OPT_NEGATIVE,         /* -n */
//  OPT_SPEED,            /* -v */
//  OPT_SOURCE,           /* -t */
  OPT_PREVIEW,
//  OPT_CALIB_ONCE,
  
//  OPT_GEOMETRY_GROUP,   /* -f .... */
  OPT_TL_X,             /* top-left x */
  OPT_TL_Y,             /* top-left y */
  OPT_BR_X,             /* bottom-right x */
  OPT_BR_Y,             /* bottom-right y */ 
  
//  OPT_ENHANCEMENT_GROUP,
//  OPT_EXPOSURE,
//  OPT_BRIGHTNESS,       /* -d */
//  OPT_CONTRAST,         /* -k */
//  OPT_HIGHLIGHT,        /* -l */
//  OPT_SHADOW,           /* -s */
//  OPT_MIDTONE,          /* -m */
  
//  OPT_GAMMA_GROUP,
//  OPT_CUSTOM_GAMMA,
//  OPT_ANALOG_GAMMA,
// OPT_ANALOG_GAMMA_R,
//  OPT_ANALOG_GAMMA_G,
//  OPT_ANALOG_GAMMA_B,
  /* "The gamma vectors MUST appear in the order gray, red, green, blue." */
//  OPT_GAMMA_VECTOR,
//  OPT_GAMMA_VECTOR_R,
//  OPT_GAMMA_VECTOR_G,
//  OPT_GAMMA_VECTOR_B,
//  OPT_GAMMA_BIND, 
  
  NUM_OPTIONS, 
  
  OPT_BACKTRACK,        /* -B */
  
  /* must come last: */
  RNUM_OPTIONS
}; 

typedef struct TWAIN_Device {
  struct TWAIN_Device *next; /* next, for linked list     */
  SANE_Device sane;             /* SANE generic device block */ 
  TW_IDENTITY twIdentity;
  TW_IDENTITY dsID;
  BOOL TWDSOpen;    // glue code flag for an Open Source
  BOOL TWDSEnabled; // glue code flag for an Open Source
  TW_USERINTERFACE  twUI;
} TWAIN_Device; 

/*******************************************************************/
/***** scanner structure (one for each device in use)          *****/
/*****       ....all the state needed to define a scan request *****/
/*******************************************************************/

typedef struct TWAIN_Scanner {
  struct TWAIN_Scanner *next;  /* for linked list */
  TWAIN_Device *dev;           /* raw device info */
  BOOL busy;
  BOOL scanning;
  BOOL cancel; 
  SANE_Option_Descriptor sod[RNUM_OPTIONS];  /* option list for session   */ 
  Option_Value val[RNUM_OPTIONS];  /* option values for session */ 
  HANDLE hbm_acq;     // handle to bit map from Source to ret to App
  int undelivered_bytes;
  int processed;
  LONG Width;
  LONG Height;
  DWORD Size;
  DWORD SizeImage;
  WORD BitCount;
  DWORD ClrUsed;    
  SANE_Parameters params;
  int RowLength;
  int RowLengthWithPad;
  int Padding;
  int RowPosition;
  int line_multipl;
  int rgb_order;
  SANE_Range x_range;
  SANE_Range y_range;
  SANE_Range res_range;      /* range desc. for resolution */ 
} TWAIN_Scanner; 


static int num_devices = 0;
static TWAIN_Device *first_dev = NULL;     /* list of known devices */
static TWAIN_Scanner *first_handle = NULL; /* list of open scanners */ 
static const SANE_Device **devlist = NULL;    /* sane_get_devices() */


/********************************************************************/
/* TWAIN STRUCTS AND METHODS                                        */
/********************************************************************/
#define VALID_HANDLE    32      // valid windows handle SB >= 32

static BOOL TWDSMOpen = FALSE;
static HANDLE hDSMDLL = NULL;     // handle to Source Manager for explicit load
//TODO: Ugly hack - hWnd handle is set to some non zero value. This value is passed to DSMEntry() function.
//This hack can make problem if DSM manager will want to show its GUI
static HWND hWnd = (HWND)1;                 // global for window handle
DSMENTRYPROC lpDSM_Entry;         // entry point to the SM
TW_STATUS gGlobalStatus = {0, 0};
TW_IDENTITY appID;

/********************************************************************/
/********************************************************************/
/*** Forward declarations *******************************************/
/********************************************************************/
/********************************************************************/

BOOL set_layout(TWAIN_Scanner *scanner, BOOL max_area);

/********************************************************************/
/********************************************************************/
/*** Utility Functions **********************************************/
/********************************************************************/
/********************************************************************/

/********************************************************************/
/* max_string_size()                                                */
/********************************************************************/
static size_t max_string_size (const SANE_String_Const strings[])
{
  size_t size, max_size = 0;
  int i;

  for (i = 0; strings[i]; ++i) {
    size = strlen(strings[i]) + 1;
    if (size > max_size) max_size = size;
  }
  return max_size;
}

/********************************************************************/
/* CallDSMEntry()                                                   */
/********************************************************************/
TW_UINT16 CallDSMEntry(pTW_IDENTITY pApp, pTW_IDENTITY pSrc,
                       TW_UINT32 DG, TW_UINT16 DAT, TW_UINT16 MSG, TW_MEMREF pData)
{
	TW_UINT16 twRC = (*lpDSM_Entry)(pApp, pSrc, DG, DAT, MSG, pData);

	if((twRC != TWRC_SUCCESS)&&(DAT!=DAT_EVENT))
	{
		VERIFY((*lpDSM_Entry)(pApp, pSrc, DG_CONTROL, DAT_STATUS, MSG_GET, 
					(TW_MEMREF)&gGlobalStatus) == TWRC_SUCCESS);
		DBG(2, "CallDSMEntry: CallDSMEntry function: call failed with RC = %d, CC = %d.\n", 
					twRC, gGlobalStatus.ConditionCode);
	}
	return twRC;
}

/********************************************************************/
/* TWOpenDSM()                                                      */
/********************************************************************/
BOOL TWOpenDSM ()
{
	TW_UINT16     twRC = TWRC_FAILURE;
	OFSTRUCT      OpenFiles;
	#define       WINDIRPATHSIZE 160
	char          WinDir[WINDIRPATHSIZE];
	TW_STR32      DSMName;

	DBG(5, "TWOpenDSM: Initialize appID structure\n");

	//TWAIN initialization
	appID.Id = 0; 				// init to 0, but Source Manager will assign real value
	appID.Version.MajorNum = V_MAJOR;
	appID.Version.MinorNum = V_MINOR;
	appID.Version.Language = TWLG_USA;
	appID.Version.Country  = TWCY_USA;
    lstrcpy (appID.Version.Info,  "SANED - TWAIN connector");
	lstrcpy (appID.ProductName,   "SANED - TWAIN connector 2007");
	appID.ProtocolMajor = 1;//TWON_PROTOCOLMAJOR;
	appID.ProtocolMinor = 7;//TWON_PROTOCOLMINOR;
	appID.SupportedGroups =  DG_IMAGE | DG_CONTROL;
	lstrcpy (appID.Manufacturer,  "Igor Farinic");
	lstrcpy (appID.ProductFamily, "SANED - TWAIN connector");

	DBG(5, "TWOpenDSM: Finished initialization of appID structure\n");

	memset(&OpenFiles, 0, sizeof(OFSTRUCT));
	memset(WinDir, 0, sizeof(char[WINDIRPATHSIZE]));
	memset(DSMName, 0, sizeof(TW_STR32));

    lstrcpy (DSMName, "TWAIN_32.DLL");

	// debug only
	if (TWDSMOpen == TRUE)
	{
		DBG(5, "TWOpenDSM: DSM already open\n");
	}

	// Only open SM if currently closed
	if (TWDSMOpen!=TRUE)
	{

		GetWindowsDirectory (WinDir, WINDIRPATHSIZE);

		if (WinDir[strlen(WinDir)-1] != '\\')
		{
			lstrcat (WinDir, "\\");
		}
		lstrcat (WinDir, DSMName);

		if ((OpenFile(WinDir, &OpenFiles, OF_EXIST) != -1) &&
				(hDSMDLL =     LoadLibrary(DSMName)) != NULL &&
				(hDSMDLL >= (HANDLE)VALID_HANDLE) &&
				(lpDSM_Entry = (DSMENTRYPROC)GetProcAddress(hDSMDLL, MAKEINTRESOURCE (1))) != NULL)
		{
            
            DBG(5, "TWOpenDSM: TWAIN_32.DLL found and opened\n"); 
			// 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
            twRC = CallDSMEntry(&appID,
								NULL,
								DG_CONTROL,
								DAT_PARENT,
								MSG_OPENDSM,
								(TW_MEMREF)&hWnd);

            DBG(5, "TWOpenDSM: Finished call of function DSMEntry()\n"); 

			switch (twRC)
			{
				case TWRC_SUCCESS:
					// Needed for house keeping.  Do single open and do not
					// close SM which is not already open ....
					TWDSMOpen = TRUE;
				    DBG(5, "TWOpenDSM: Source Manager was Opened successfully\n");
					break;

				case TWRC_FAILURE:
					DBG(2, "TWOpenDSM: OpenDSM failure\n");
				default:
					// Trouble opening the SM, inform the user
					TWDSMOpen = FALSE;
					DBG(2, "TWOpenDSM: Trouble opening the SM, inform the user (DG_CONTROL/DAT_PARENT/MSG_OPENDSM)\n");
					break;
			}
		}
		else
		{
    		DBG(2, "TWOpenDSM: Error in Open, LoadLibrary, or GetProcAddress. Twain DLL: TWAIN_32.DLL may not exist.\n");
		}
	}  
	// Let the caller know what happened
	return (TWDSMOpen);
} 


/********************************************************************/
/* twain_open_dsm()                                                 */
/********************************************************************/
SANE_Status twain_open_dsm() 
{
    DBG(5, "twain_open_dsm: Opening source manager\n");
     
    if (!TWDSMOpen)
    {
    	if (TWOpenDSM())
    	{
           DBG(2, "twain_open_dsm: Source manager opened successfully\n");
           return SANE_STATUS_GOOD;
    	}
    }
    else
    {
        DBG(2, "twain_open_dsm: Source manager already open\n");
        return SANE_STATUS_INVAL;
    }
    
    return SANE_STATUS_INVAL;
}

/********************************************************************/
/* TWCloseDSM()                                                     */
/********************************************************************/
BOOL TWCloseDSM (TWAIN_Device *td)
{
	TW_UINT16 twRC = TWRC_FAILURE;
	char buffer[80];

	memset(buffer, 0, sizeof(char[80]));

	if (!TWDSMOpen)
	{
			DBG(4, "TWCloseDSM: Cannot Close Source Manager. Source Manager Not Open\n");
	}
	else
	{
		if (td->TWDSOpen==TRUE)
		{
				DBG(3, "TWCloseDSM: A Source is currently Open\n");
		}
		else
		{
			// 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 !!
    			twRC = CallDSMEntry(&appID,
    								NULL,
    								DG_CONTROL,
    								DAT_PARENT,
    								MSG_CLOSEDSM,
    								&hWnd);

				if (twRC != TWRC_SUCCESS)
				{
					// Trouble closing the SM, inform the user
     				DBG(3, "TWCloseDSM: CloseDSM failure -- twRC = %d\n", twRC);
				}
				else
				{
					TWDSMOpen = FALSE;
					// Explicitly free the SM library
					if (hDSMDLL)
					{        
						FreeLibrary (hDSMDLL);
						hDSMDLL=NULL;
						// the data source id will no longer be valid after
						// twain is killed.  If the id is left around the
						// data source can not be found or opened
						td->dsID.Id = 0;  
					}
						DBG(5, "TWCloseDSM: Source Manager was closed successfully\n");
				}
			}
		}
	}
	// Let the caller know what happened
	return (twRC==TWRC_SUCCESS);
} // TWCloseDSM

/********************************************************************/
/* twain_close_dsm()                                                */
/********************************************************************/
SANE_Status twain_close_dsm(TWAIN_Device *td) 
{
    DBG(5, "twain_close_dsm: Closing source manager\n");
    if (TWCloseDSM(td)){
       DBG(5, "twain_close_dsm: Source manager closed\n");
       return SANE_STATUS_GOOD;
    } 
            
    DBG(5, "twain_close_dsm: Failed to close source manager\n");
    return SANE_STATUS_INVAL;
}

/********************************************************************/
/* Try to attach TWAIN device                                       */
/********************************************************************/
static SANE_Status 
attach_scanner(TWAIN_Device **devp)
{
  TWAIN_Device *dev;
  SANE_Status status;

  DBG(15,"attach_scanner:  %s\n", (char*) (*devp)->dsID.ProductName);
  /* check if device is already known... */
  for (dev = first_dev; dev; dev = dev->next) {
    if (strcmp(dev->sane.name, (*devp)->dsID.ProductName) == 0) {
      if (devp) *devp = dev;
      return SANE_STATUS_GOOD;
    }
  }

  /* open scsi device... */
  DBG(20, "attach_scanner:  opening %s\n", (char*) (*devp)->dsID.ProductName);
  
  //dev=malloc(sizeof(*dev));
  //if (!dev) return SANE_STATUS_NO_MEM;
  //memset(dev, 0, sizeof(*dev));

   dev = *devp; 
   
  /* initialize dev structure */
  dev->sane.name   = (*devp)->dsID.ProductName;
  dev->sane.vendor = (*devp)->dsID.Manufacturer;
  dev->sane.model  = (*devp)->dsID.ProductFamily;
  dev->sane.type   = "flatbed scanner";
  
  dev->twIdentity = (*devp)->dsID;

  /* link into device list... */
  ++num_devices;
  dev->next = first_dev;
  first_dev = dev;
  //if (devp) *devp = dev;

  DBG(15, "attach_scanner:  happy.\n");

  return SANE_STATUS_GOOD;
} 

/********************************************************************/
/* TWOpenDS()                                                       */
/********************************************************************/
BOOL TWOpenDS (TWAIN_Device *td)
{
	TW_UINT16 twRC = TWRC_FAILURE;

	if (TWDSMOpen==FALSE)
	{
		DBG(3, "TWOpenDS: DSM not open - cannot open DS\n");
	}
	else
	{
		//Source Manager is open
		if (td->TWDSOpen==TRUE)
		{
			DBG(4, "TWOpenDS: Source already open\n");
		}
		else
		{
			// This will open the Source.
			twRC = CallDSMEntry(&appID,
								NULL,
     							DG_CONTROL,
    							DAT_IDENTITY,
    							MSG_OPENDS,
    							&(td->dsID));

			switch (twRC)
			{
				case TWRC_SUCCESS:
					DBG(5, "TWOpenDS: OpenDS success\n");
					// do not change flag unless we successfully open
					td->TWDSOpen = TRUE;
					break;

				case TWRC_FAILURE:
					DBG(3, "TWOpenDS: OpenDS failure\n");
					break;

				default:
					DBG(3, "TWOpenDS: Unknown Return Code (DG_CONTROL/DAT_IDENTITY/MSG_OPENDS)");
					break;
			}
		}
	}
	return td->TWDSOpen;
} 

/********************************************************************/
/* twain_open_ds()                                                  */
/********************************************************************/
SANE_Status twain_open_ds(TWAIN_Device *td) 
{
            
    if (!(td->TWDSOpen))
    {
    	if (TWOpenDS(td))             
    	{
           DBG(4, "twain_open_ds: Source successfully opened\n");
           attach_scanner(&td);
           return SANE_STATUS_GOOD;
    	}
    }
    else
    {
        DBG(3, "twain_open_ds: Source already open\n");
    }

    return SANE_STATUS_INVAL;

}

/********************************************************************/
/* TWCloseDS()                                                      */
/********************************************************************/
BOOL TWCloseDS(TWAIN_Device *td) 
{
    TW_UINT16 twRC = TWRC_FAILURE;
    
    if (!(td->TWDSOpen))
    {
  		DBG(4,"TWCloseDS: Cannot Close Source. Source Not Open\n");
    }
    else
    {
    	if (td->TWDSEnabled == TRUE)
    	{
  			DBG(3, "TWCloseDS: Source is Currently Enabled\n");
    	}
    	else
    	{
    		if (td->TWDSOpen==TRUE)
    		{
    			// Close an open Source
    			twRC = CallDSMEntry(&appID,
    							NULL,
    							DG_CONTROL,
    							DAT_IDENTITY,
    							MSG_CLOSEDS,
    							&(td->dsID));
    
    			DBG(5, "TWCloseDS: CloseDS called\n");
    
    			// show error on close
    			if (twRC!= TWRC_SUCCESS) 
    			{
    			   DBG(3, "TWCloseDS: Error closing DS (DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS)\n");
    			} 
    			else 
    			{
    				td->TWDSOpen = FALSE;
    				td->dsID.Id = 0;
    				td->dsID.ProductName[0] = 0;
    				DBG(5, "TWCloseDS: Source was closed successfully\n");
    			}
    
    		}
    	}
    }
    return(twRC==TWRC_SUCCESS);
}

/********************************************************************/
/* twain_close_ds()                                                 */
/********************************************************************/
SANE_Status twain_close_ds(TWAIN_Device *td) 
{
  DBG(5, "twain_close_ds: Closing data source\n");

  if (TWCloseDS(td)) {
     DBG(5, "twain_close_ds: Data source closed\n");
     return SANE_STATUS_GOOD;
  } 
  
  DBG(5, "twain_close_ds: Failed to close data source\n");
  return SANE_STATUS_INVAL;
    
}

/********************************************************************/
/* TWEnableDS()                                                     */
/********************************************************************/
BOOL TWEnableDS(TWAIN_Device *td)
{
	BOOL Result = FALSE;
	TW_UINT16 twRC = TWRC_FAILURE;

	if (td->TWDSOpen==FALSE)
	{
			DBG(4, "TWEnableDS: Cannot Enable Source. No Source Open\n");
	}
	else
	{	
		// only enable open Source's
		if (td->TWDSEnabled==TRUE)	//Source is already enabled
		{
				DBG(4,"TWEnableDS: Cannot Enable Source, already enabled\n");
		}
		else
		{
			// 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)
			memset(&td->twUI, 0, sizeof(TW_USERINTERFACE));
			td->twUI.hParent = hWnd;
			td->twUI.ShowUI  = FALSE;

			twRC = CallDSMEntry(&appID,
						&(td->dsID),
						DG_CONTROL,
						DAT_USERINTERFACE,
						MSG_ENABLEDS,
						(TW_MEMREF)&(td->twUI));

			if (twRC!=TWRC_SUCCESS)
			{
				DBG(3, "TWEnableDS: Failed to enable DS (DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDS)\n");
			}
			else
			{
				Result = TRUE;
				td->TWDSEnabled = TRUE;
				DBG(5, "TWEnableDS: Source Enabled\n");
			}
		}
	}
	return (Result);
} 

/********************************************************************/
/* twain_enable_ds()                                                */
/********************************************************************/
SANE_Status twain_enable_ds(TWAIN_Device *td) 
{
  DBG(5, "twain_enable_ds: Enabling data source\n");

  if (TWEnableDS(td)) {
     DBG(5, "twain_enable_ds: Data source enabled\n");
     return SANE_STATUS_GOOD;
  } 
  
  DBG(5, "twain_enable_ds: Failed to enable data source\n");
  return SANE_STATUS_INVAL;
  
}

/********************************************************************/
/* TWDisableDS()                                                    */
/********************************************************************/
BOOL TWDisableDS(TWAIN_Device *td)
{
	TW_UINT16 twRC = TWRC_FAILURE;
	TW_USERINTERFACE twUI;

	memset(&twUI, 0, sizeof(TW_USERINTERFACE));

	DBG(5, "TWDisableDS: Disable DS\n");

	// only disable enabled Source's
	if (td->TWDSEnabled!=TRUE)
	{
	   DBG(3, "TWDisableDS: Cannot Disable Source. Source Not Enabled\n");
	}
	else
	{

		twUI.hParent = hWnd;
		twUI.ShowUI = TWON_DONTCARE8;

		twRC = CallDSMEntry(&appID,
					&(td->dsID),
					DG_CONTROL,
					DAT_USERINTERFACE,
					MSG_DISABLEDS,
					(TW_MEMREF)&twUI);

		if (twRC == TWRC_SUCCESS)
		{   
			DBG(4, "TWDisableDS: DS Disabled\n");

			td->TWDSEnabled = FALSE;
		}
		else
		{
			DBG(3, "TWDisableDS: Failed to disable DS (DG_CONTROL/DAT_USERINTERFACE/MSG_DISABLEDS)");
		}
	}    	
	return (twRC==TWRC_SUCCESS);
} 

/********************************************************************/
/* twain_disable_ds()                                                */
/********************************************************************/
SANE_Status twain_disable_ds(TWAIN_Device *td) 
{
  DBG(5, "twain_disable_ds: Disabling data source\n");

  if (TWDisableDS(td)) {
     DBG(5, "twain_disable_ds: Data source disabled\n");
     return SANE_STATUS_GOOD;
  } 
  
  DBG(5, "twain_disable_ds: Failed to disable data source\n");
  return SANE_STATUS_INVAL;
  
}

/********************************************************************/
/* set_ranges()                                                     */
/********************************************************************/
BOOL set_ranges(TWAIN_Scanner* ts) 
{

  Option_Value *val = ts->val;
             
  //float base_res_dpmm = ((float)default_dpi / MM_PER_INCH); 
  float base_res_dpmm = ( ((float) SANE_UNFIX(ts->res_range.max)) / MM_PER_INCH); 
  
  ts->x_range.min = SANE_FIX(0);
  DBG(10, "init_options: SANE_FIX of value 0 is %d \n", ts->x_range.min);
  ts->x_range.max = SANE_FIX((float) default_pixels_x / base_res_dpmm);
  DBG(10, "init_options: SANE_FIX of value %f is %d \n", ((float) default_pixels_x / base_res_dpmm), ts->x_range.max);
  ts->x_range.quant = SANE_FIX(0);

  ts->y_range.min = SANE_FIX(0);
  ts->y_range.max = SANE_FIX((float) default_pixels_y / base_res_dpmm);
  ts->y_range.quant = SANE_FIX(0);
  
}

/********************************************************************/
/* Initialize the options registry                                  */
/********************************************************************/
static SANE_Status 
init_options(TWAIN_Scanner *ts)
{
  int i;
  SANE_Option_Descriptor *sod = ts->sod;
  Option_Value *val = ts->val;

  DBG(10, "init_options...\n");

  memset(ts->sod, 0, sizeof(ts->sod));
  memset(ts->val, 0, sizeof(ts->val));
  /* default:  software selectable word options... */
  for (i=0; i<NUM_OPTIONS; i++) {
    sod[i].size = sizeof(SANE_Word);
    sod[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
  }

  DBG(10, "init_options: OPT_NUM_OPTS\n");
  sod[OPT_NUM_OPTS].name =   SANE_NAME_NUM_OPTIONS;
  sod[OPT_NUM_OPTS].title =  SANE_TITLE_NUM_OPTIONS;
  sod[OPT_NUM_OPTS].desc =   SANE_DESC_NUM_OPTIONS;
  sod[OPT_NUM_OPTS].type =   SANE_TYPE_INT;
  sod[OPT_NUM_OPTS].unit =   SANE_UNIT_NONE;
  sod[OPT_NUM_OPTS].size =   sizeof (SANE_Word);
  sod[OPT_NUM_OPTS].cap =    SANE_CAP_SOFT_DETECT;
  sod[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
  
  /* The Scan Mode Group */
  DBG(10, "init_options: OPT_MODE_GROUP\n");
  sod[OPT_MODE_GROUP].name  = "";
  sod[OPT_MODE_GROUP].title = "Scan Mode";
  sod[OPT_MODE_GROUP].desc  = "";
  sod[OPT_MODE_GROUP].type  = SANE_TYPE_GROUP;
  sod[OPT_MODE_GROUP].cap   = 0;
  sod[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; 
  
  DBG(10, "init_options: OPT_MODE\n");
  sod[OPT_MODE].name = SANE_NAME_SCAN_MODE;
  sod[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
  sod[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
  sod[OPT_MODE].type = SANE_TYPE_STRING;
  sod[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
  {
    SANE_String_Const *mode_list;
    mode_list = (SANE_String_Const *) malloc(2 * sizeof(SANE_String_Const));
    if (mode_list == NULL) return SANE_STATUS_NO_MEM;
    i = 0;
    mode_list[i++] = M_COLOR;
    mode_list[i] = NULL;
    sod[OPT_MODE].constraint.string_list = mode_list;
    sod[OPT_MODE].size                   = max_string_size(mode_list);
    val[OPT_MODE].s = strdup(mode_list[0]);
  } 
  
  DBG(10, "init_options: OPT_RESOLUTION\n");
  sod[OPT_RESOLUTION].name  = SANE_NAME_SCAN_RESOLUTION;
  sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
  sod[OPT_RESOLUTION].desc  = SANE_DESC_SCAN_RESOLUTION;
  sod[OPT_RESOLUTION].type  = SANE_TYPE_FIXED;
  sod[OPT_RESOLUTION].unit  = SANE_UNIT_DPI;
  sod[OPT_RESOLUTION].constraint_type  = SANE_CONSTRAINT_RANGE;
  {
    SANE_Int maxres = get_max_resolution(ts);
    ts->res_range.max = SANE_FIX(maxres);
    ts->res_range.min = SANE_FIX( maxres / 100 );
    ts->res_range.quant = SANE_FIX(1);//ts->res_range.min;
    sod[OPT_RESOLUTION].constraint.range = &(ts->res_range);
  }
  val[OPT_RESOLUTION].w     = SANE_FIX(100); 
  set_resolution(ts, SANE_UNFIX(val[OPT_RESOLUTION].w));
  
  set_ranges(ts);
  
  DBG(10, "init_options: OPT_PREVIEW\n");
  sod[OPT_PREVIEW].name  = SANE_NAME_PREVIEW;
  sod[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
  sod[OPT_PREVIEW].desc  = SANE_DESC_PREVIEW;
  sod[OPT_PREVIEW].type  = SANE_TYPE_BOOL; 
  sod[OPT_PREVIEW].unit  = SANE_UNIT_NONE;
  sod[OPT_PREVIEW].size  = sizeof(SANE_Word);
  val[OPT_PREVIEW].w     = SANE_FALSE; 
  
  DBG(10, "init_options: OPT_TL_X\n");
  sod[OPT_TL_X].name  = SANE_NAME_SCAN_TL_X;
  sod[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
  sod[OPT_TL_X].desc  = SANE_DESC_SCAN_TL_X;
  sod[OPT_TL_X].type  = SANE_TYPE_FIXED;
  sod[OPT_TL_X].unit  = SANE_UNIT_MM;
  sod[OPT_TL_X].size  = sizeof(SANE_Word);
  sod[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;

  DBG(10, "init_options: OPT_TL_Y\n");
  sod[OPT_TL_Y].name  = SANE_NAME_SCAN_TL_Y;
  sod[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
  sod[OPT_TL_Y].desc  = SANE_DESC_SCAN_TL_Y;
  sod[OPT_TL_Y].type  = SANE_TYPE_FIXED;
  sod[OPT_TL_Y].unit  = SANE_UNIT_MM;
  sod[OPT_TL_Y].size  = sizeof(SANE_Word);
  sod[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;

  DBG(10, "init_options: OPT_BR_X\n");
  sod[OPT_BR_X].name  = SANE_NAME_SCAN_BR_X;
  sod[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
  sod[OPT_BR_X].desc  = SANE_DESC_SCAN_BR_X;
  sod[OPT_BR_X].type  = SANE_TYPE_FIXED;
  sod[OPT_BR_X].unit  = SANE_UNIT_MM;
  sod[OPT_BR_X].size  = sizeof(SANE_Word);
  sod[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;

  DBG(10, "init_options: OPT_BR_Y\n");
  sod[OPT_BR_Y].name  = SANE_NAME_SCAN_BR_Y;
  sod[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
  sod[OPT_BR_Y].desc  = SANE_DESC_SCAN_BR_Y;
  sod[OPT_BR_Y].type  = SANE_TYPE_FIXED;
  sod[OPT_BR_Y].unit  = SANE_UNIT_MM;
  sod[OPT_BR_Y].size  = sizeof(SANE_Word);
  sod[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
  
  sod[OPT_TL_X].constraint.range = 
    sod[OPT_BR_X].constraint.range = &(ts->x_range);
  sod[OPT_TL_Y].constraint.range = 
    sod[OPT_BR_Y].constraint.range = &(ts->y_range);

  /* set default scan region to be maximum size */
  val[OPT_TL_X].w     = sod[OPT_TL_X].constraint.range->min;
  val[OPT_TL_Y].w     = sod[OPT_TL_Y].constraint.range->min;
  val[OPT_BR_X].w     = sod[OPT_BR_X].constraint.range->max;
  val[OPT_BR_Y].w     = sod[OPT_BR_Y].constraint.range->max; 
  
  if (set_layout(ts, FALSE)) {
        return SANE_STATUS_GOOD; 
  } else {
         return SANE_STATUS_INVAL;
  }

  
  DBG(15, "init_options:  done.\n");
  return SANE_STATUS_GOOD; 
} 

/********************************************************************/
/* DoNativeTransfer()                                               */
/********************************************************************/
static BOOL DoNativeTransfer(TWAIN_Scanner *scanner) 
{
    TWAIN_Device *td = scanner->dev;

	TW_PENDINGXFERS     twPendingXfer;
	TW_UINT16           twRC = TWRC_FAILURE;
	TW_UINT16           twRC2 = TWRC_FAILURE;
	TW_UINT32           hBitMap = NULL;
	char buffer[2048];

	LPBITMAPINFOHEADER lpdib = NULL;

    BOOL status = FALSE;

	memset(&twPendingXfer, 0, sizeof(TW_PENDINGXFERS));
	memset(buffer, 0, sizeof(char[2048]));

	/*
	* Do until there are no more pending transfers
	* explicitly initialize the our flags
	*/
	twPendingXfer.Count = 0;
	do 
	{
		/*
		* Initiate Native Transfer
		*/
		twRC = CallDSMEntry(&appID,
						&td->dsID, 
						DG_IMAGE,
						DAT_IMAGENATIVEXFER, 
						MSG_GET, 
						(TW_MEMREF)&hBitMap);

		switch (twRC)
		{
			case TWRC_XFERDONE:  // Session is in State 7
			    DBG(4, "DoNativeTransfer: TWRC_XFERDONE (DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET)\n");
				scanner->hbm_acq = (HBITMAP)hBitMap;

				/*
				* Acknowledge the end of the transfer 
				* and transition to state 6/5
				*/
				twRC2 = CallDSMEntry(&appID,
									&(td->dsID), 
									DG_CONTROL,
									DAT_PENDINGXFERS, 
									MSG_ENDXFER,
									(TW_MEMREF)&twPendingXfer);

				if (twRC2 != TWRC_SUCCESS)
				{
						DBG(3, "DoNativeTransfer: Failed in transition to state 6/5\n");
						status = FALSE;
				}
				status = TRUE;
				DBG(5, "Pending Xfers = %d\n",twPendingXfer.Count);
				break;

			/*
			* the user canceled or wants to rescan the image
			* something wrong, abort the transfer and delete the image
			* pass a null ptr back to App
			*/
			case TWRC_CANCEL:   // Session is in State 7
                DBG(3, "DoNativeTransfer: TWRC_CANCEL (DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET)\n");                

				/*
				* Source (or User) Canceled Transfer
				* transistion to state 6/5
				*/
				twRC2 = CallDSMEntry(&appID,
								&(td->dsID), 
								DG_CONTROL,
								DAT_PENDINGXFERS, 
								MSG_ENDXFER,
								(TW_MEMREF)&twPendingXfer);

				if (twRC2 != TWRC_SUCCESS)
				{
						DBG(3, "DoNativeTransfer: Failed in canceled transfer transition to state 6/5\n");             
						status = FALSE;
				}
				DBG(5, "DoNativeTransfer: Pending Xfers = %d\n",twPendingXfer.Count);
                break;

			case TWRC_FAILURE:  //Session is in State 6
				/*
				* The transfer failed
				*/
					DBG(3, "DoNativeTransfer: Fail session is in state 6\n");                

				/*
				* Abort the image
				* Enhancement: Check Condition Code and attempt recovery
				*/
				twRC2 = CallDSMEntry(&appID,
								&(td->dsID), 
								DG_CONTROL,
								DAT_PENDINGXFERS, 
								MSG_ENDXFER,
								(TW_MEMREF)&twPendingXfer); 

				if (twRC2 != TWRC_SUCCESS)
				{
					DBG(3, "DoNativeTransfer: Failed in abort the image");             
					status = FALSE;
				}
                DBG(5, "DoNativeTransfer: Pending Xfers = %d\n",twPendingXfer.Count);
				break;

			default:    //Sources should never return any other RC
			    DBG(3, "DoNativeTransfer: Unknown Return Code (DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET)\n");

				/*
				* Abort the image
				* Enhancement: Check Condition Code and attempt recovery instead
				*/
				twRC2 = CallDSMEntry(&appID,
								&(td->dsID), 
								DG_CONTROL,
								DAT_PENDINGXFERS, 
								MSG_ENDXFER,
								(TW_MEMREF)&twPendingXfer);

				if (twRC2 != TWRC_SUCCESS)
				{
				 DBG(3, "DoNativeTransfer: Failed attempt to recovery (DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER)\n");
				 status = FALSE;
				}
                DBG(5, "DoNativeTransfer: Pending Xfers = %d\n",twPendingXfer.Count);
				break;
		}   

	} while (twPendingXfer.Count != 0);

	return status;
}   

/********************************************************************/
/* TWXferMech()                                                     */
/********************************************************************/
BOOL TWXferMech(TWAIN_Device *td)

{
	TW_CAPABILITY   cap;
	pTW_ONEVALUE    pval = NULL;
	TW_INT16        status = TWRC_FAILURE;  

	/*
	*	Initialize all structures
	*/
	memset(&cap, 0, sizeof(TW_CAPABILITY));


	DBG(10, "TWXferMech ...\n");

	cap.Cap = ICAP_XFERMECH;
	cap.ConType = TWON_ONEVALUE;

	/*
	* alloc the container
	*/
	if (cap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE)))
	{
		pval = (pTW_ONEVALUE)GlobalLock(cap.hContainer);
		pval->ItemType = TWTY_UINT16;
		pval->Item = TWSX_NATIVE;
        GlobalUnlock(cap.hContainer);

		status = CallDSMEntry(&appID,
						&(td->dsID),
						DG_CONTROL, 
						DAT_CAPABILITY, 
						MSG_SET,
						(TW_MEMREF)&cap);

		GlobalFree((HANDLE)cap.hContainer);

		if (status != TWRC_SUCCESS)
		{
				DBG(3, "TWXferMech: Transfer mechanism failed set (MSG_SET of ICAP_XFERMECH)\n"); 
		} else {
 	      DBG(3, "TWXferMech: Transfer mechanism successfully set (MSG_SET of ICAP_XFERMECH)\n"); 
        }
	}
	else
	{
		status = TWRC_FAILURE;
		DBG(3, "TWXferMech: Memory allocation for cap.hContainer failed (MSG_SET of ICAP_XFERMECH)\n"); 
	}
	return (status == TWRC_SUCCESS);
}  

/********************************************************************/
/* TWAcquire()                                                      */
/********************************************************************/
BOOL TWAcquire (TWAIN_Device *td)
{
	TW_INT16 result = TRUE;

	DBG(10, "TWAcquire entry...\n");

	if (TWDSMOpen == TRUE)
	{

		if (td->TWDSOpen == TRUE)
		{
			return TWXferMech (td);
        }
    }

	return(FALSE);
}   

/********************************************************************/
/* WaitTillTransferIsReady()                                        */
/********************************************************************/
BOOL WaitTillTransferIsReady(TWAIN_Device *td) {

  TW_UINT16 twRC = TWRC_FAILURE;
  TW_EVENT twEvent;
  
  DBG(5, "WaitTillTransferIsReady: Entering endless wait loop, till Scanner is ready for transfer\n");

  //TODO: add timeout after some time
  do {
      memset(&twEvent, 0, sizeof(TW_EVENT));
      twEvent.TWMessage = MSG_NULL;
      twEvent.pEvent = 0;

      twRC = CallDSMEntry(&appID,
    						&(td->dsID),
    						DG_CONTROL,
    						DAT_EVENT,
    						MSG_PROCESSEVENT,
    						(TW_MEMREF)&twEvent);
    
    		switch (twEvent.TWMessage)
    		{
    			case MSG_XFERREADY:
    				DBG(5, "WaitTillTransferIsReady: Transfer ready notification received\n");
    				return TRUE;
    			
    		// No message from the Source to the App break;
    		// possible new message
    			case MSG_NULL:
    			default:
    				break;
    		}   
   	} while (TRUE);


}

/********************************************************************/
/* FIX32ToFloat()                                                   */
/********************************************************************/
float FIX32ToFloat (TW_FIX32 fix32)
{
	float   floater = 0;

	floater = (float) fix32.Whole + (float) (fix32.Frac / 65536.0);
	return(floater);
}

static void DoubleToFix32(double r, TW_FIX32* pfix);

/********************************************************************/
/* FloatToFIX32()                                                   */
/********************************************************************/
TW_FIX32 FloatToFIX32(double r)
{
	TW_FIX32 fix;
    DoubleToFix32(r, &fix);
	return fix;
} 

/********************************************************************/
/* DoubleToFix32()                                                  */
/********************************************************************/
static void DoubleToFix32(double r, TW_FIX32* pfix)
{
   DBG(128, "Convert double number %f to fix\n", r);    
   // Note 1: This round-away-from-0 is new in TWAIN 1.7
   // Note 2: ANSI C converts float to int by truncating toward 0.
	TW_INT32 val = (TW_INT32)(r * 65536.0 + (r < 0 ? -0.5 : +0.5));
	pfix->Whole = (TW_INT16)(val >> 16);			// most significant 16 bits
	pfix->Frac = (TW_UINT16)(val & 0xffff);		// least
	DBG(128, "Fix value of number %f is %d\n", r, *((long*)pfix));    

} // DoubleToFix32

/********************************************************************/
/* DoubleToFIX32()                                                   */
/********************************************************************/
long DoubleToFIX32(double r)
{
	TW_FIX32 fix;
	//assert(sizeof (TW_FIX32) == sizeof (long));
   DoubleToFix32(r, &fix);
   return *(long*)&fix;
} 

/********************************************************************/
/* GetImageInfo()                                                   */
/********************************************************************/
/*
void GetImageInfo (SANE_Handle handle,
                   SANE_Parameters *params)
{
    TWAIN_Scanner *scanner = handle;

	TW_UINT16       twRC = TWRC_FAILURE;
	TW_UINT16       ccRC = TWRC_FAILURE;
	TW_IMAGEINFO    twImageInfo;
	pTW_FIX32       pTWFix32 = NULL;
	TW_UINT16       index = 0;

	memset(&twImageInfo, 0, sizeof(TW_IMAGEINFO));

	ASSERT(hWndofApp);

	// Check ImageInfo information
	twRC = CallDSMEntry(&appID,
					&(scanner->dev->dsID), 
					DG_IMAGE,
					DAT_IMAGEINFO, 
					MSG_GET, 
					(TW_MEMREF)&twImageInfo);

	//Return Code from Operation
	DBG(5, "%s %d\n", "RETURN CODE:", twRC);

	switch (twRC)
	{
		case TWRC_SUCCESS:
			DBG(5, "%s %.2f\n", "XResolution:", FIX32ToFloat(twImageInfo.XResolution));

			pTWFix32 = &(twImageInfo.YResolution);
			DBG(5, "%s %.2f\n", "YResolution:", FIX32ToFloat(*pTWFix32));

			DBG(5, "%s %ld\n", "ImageWidth:", twImageInfo.ImageWidth);
            params->pixels_per_line = twImageInfo.ImageWidth;
			DBG(5, "%s %ld\n", "ImageLength:", twImageInfo.ImageLength);
            params->lines = twImageInfo.ImageLength;

			DBG(5, "%s %d\n", "SamplesPerPixel:", twImageInfo.SamplesPerPixel);

			DBG(5, "%s %d\n", "BitsPerSample:", twImageInfo.BitsPerSample[0]);
			
			for (index = 1; index < 8; index++)
			{
				DBG(5, ", %d", twImageInfo.BitsPerSample[index]);
			}           
            DBG(5, "\n");
			DBG (5, "%s %d\n", "BitsPerPixel:", twImageInfo.BitsPerPixel);
            params->depth = twImageInfo.BitsPerPixel / 8;
			DBG (5, "%s %d\n", "Planar:", twImageInfo.Planar);

			DBG (5, "%s %d\n", "PixelType:", twImageInfo.PixelType);

			DBG (5, "%s %d\n", "Compression:", twImageInfo.Compression);
			
			//TODO: may be refiment of settings of these parameters will be needed
			params->format = SANE_FRAME_RGB;
			params->bytes_per_line = params->depth * params->pixels_per_line;
			params->last_frame = TRUE;
			
			break;

		case TWRC_FAILURE:
		default:
			// determine details of failure from Source

			if (ccRC == TWRC_SUCCESS)
			{
				DBG(5, "%s %d\n", "Condition Code:", gGlobalStatus.ConditionCode);
			} 
			else 
			{
				DBG(5, "%s\n", "Condition Code Check Failed");
			}
			params->bytes_per_line = 0;
            params->depth = 0;    
            params->format = 0;
            params->last_frame = 0;
            params->lines = 0;
            params->pixels_per_line = 0;
			
			break;
	}

	return;
}
*/

/********************************************************************/
/* SetOneScanParameter()                                            */
/********************************************************************/
BOOL SetOneScanParameter(TWAIN_Device *td, int icap, int cap_type, int itemType, int item)
{

	TW_CAPABILITY   cap;
	pTW_ONEVALUE    pval = NULL;
	TW_INT16        status = TWRC_FAILURE;  

	/*
	*	Initialize all structures
	*/
	memset(&cap, 0, sizeof(TW_CAPABILITY));

	DBG(5, "SetOneScanParameter ...\n");

	cap.Cap = icap;
	cap.ConType = cap_type;

	/*
	* alloc the container
	*/
	if (cap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE)))
	{
		pval = (pTW_ONEVALUE)GlobalLock(cap.hContainer);
		pval->ItemType = itemType;
		pval->Item = item;
        GlobalUnlock(cap.hContainer);

		status = CallDSMEntry(&appID,
						&(td->dsID),
						DG_CONTROL, 
						DAT_CAPABILITY, 
						MSG_SET,
						(TW_MEMREF)&cap);

		GlobalFree((HANDLE)cap.hContainer);

		if (status != TWRC_SUCCESS)
		{
				DBG(3, "SetOneScanParameter: Transfer mechanism failed set (MSG_SET)\n"); 
		} else {
 	      DBG(3, "SetOneScanParameter: Transfer mechanism successfully set (MSG_SET)\n"); 
        }
	}
	else
	{
		status = TWRC_FAILURE;
		DBG(3, "SetOneScanParameter: Memory allocation for cap.hContainer failed (MSG_SET)\n"); 
	}
	return (status == TWRC_SUCCESS);

}

/********************************************************************/
/* SetScanParameters()                                              */
/********************************************************************/
BOOL SetScanParameters(TWAIN_Device *td)
{
  
  BOOL status;
  
  DBG(10, "SetScanParameters: Set ICAP_PIXELTYPE\n");    
  status = SetOneScanParameter(td, ICAP_PIXELTYPE, TWON_ONEVALUE, TWTY_UINT16, TWPT_RGB);
  if (!status)
     return FALSE;
     
  DBG(10, "SetScanParameters: Set ICAP_BITDEPTH\n");    
  status = SetOneScanParameter(td, ICAP_BITDEPTH, TWON_ONEVALUE, TWTY_UINT16, 8);
  if (!status)
     return FALSE;
  
  //DBG(10, "SetScanParameters: Set ICAP_PLANARCHUNKY\n");    
  //status = SetOneScanParameter(td, ICAP_PLANARCHUNKY, TWON_ONEVALUE, TWTY_UINT16, TWPC_PLANAR);
  //if (!status)
  //   return FALSE;
  
  DBG(10, "SetScanParameters: Set ICAP_UNITS\n");    
  //Set to DPI - dots per inch (if dots == pixels then following command will set correct setting for ICAP_UNITS capability)
  status = SetOneScanParameter(td, ICAP_UNITS, TWON_ONEVALUE, TWTY_UINT16, TWUN_INCHES);
  if (!status)
     return FALSE;

  //TODO: may be add check if all parameters were set successfully
  return TRUE;
     
}  

/********************************************************************/
/* get_layout()                                                     */
/********************************************************************/
BOOL get_layout(TWAIN_Scanner *scanner)
{
     
    TW_UINT16       twRC = TWRC_FAILURE;
	TW_IMAGELAYOUT  twImageLayout;

	memset(&twImageLayout, 0, sizeof(TW_IMAGELAYOUT));

	// Check ImageLayout information
	twRC = CallDSMEntry(&appID,
					&scanner->dev->dsID, 
					DG_IMAGE,
					DAT_IMAGELAYOUT, 
					MSG_GET, 
					(TW_MEMREF)&twImageLayout);

	DBG(5, "%s %d\n", "get_layout: Return Code from get ImageLayout Operation:", twRC);

	switch (twRC)
	{
		case TWRC_SUCCESS:
			DBG(5, "%s %.2f\n", "get_layout: Frame.Left:", FIX32ToFloat(twImageLayout.Frame.Left));
			//if (scanner->val[OPT_TL_X].w != SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Left) * MM_PER_INCH )) {
           //   DBG(5, "%s %d %d\n", "get_layout:", scanner->val[OPT_TL_X].w, SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Left) * MM_PER_INCH ));
           //   scanner->val[OPT_TL_X].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Left) * MM_PER_INCH );
			 // return FALSE;
            //}  
			
            DBG(5, "%s %.2f\n", "get_layout: Frame.Top:", FIX32ToFloat(twImageLayout.Frame.Top));
			//if (scanner->val[OPT_TL_Y].w != SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Top) * MM_PER_INCH ))
			//   scanner->val[OPT_TL_Y].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Top) * MM_PER_INCH );
			   //return FALSE;

			DBG(5, "%s %.2f\n", "get_layout: Frame.Right:", FIX32ToFloat(twImageLayout.Frame.Right));
            //if (scanner->val[OPT_BR_X].w != SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Right) * MM_PER_INCH ))
            //   scanner->val[OPT_BR_X].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Right) * MM_PER_INCH );
               //return FALSE;

			DBG(5, "%s %.2f\n", "get_layout: Frame.Bottom:", FIX32ToFloat(twImageLayout.Frame.Bottom));
			//if (scanner->val[OPT_BR_Y].w != SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Bottom) * MM_PER_INCH )) {
			//   DBG(5, "%s %d %d\n", "get_layout:", scanner->val[OPT_BR_Y].w, SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Bottom) * MM_PER_INCH ));
			//   scanner->val[OPT_BR_Y].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Bottom) * MM_PER_INCH );
			   //return FALSE;
            //}			
           
			DBG(5, "%s %d\n", "get_layout: DocumentNumber:", twImageLayout.DocumentNumber);
			DBG(5, "%s %d\n", "get_layout: PageNumber:", twImageLayout.PageNumber);
			DBG(5, "%s %d\n", "get_layout: FrameNumber:", twImageLayout.FrameNumber);

        	return TRUE;

		case TWRC_FAILURE:
		default:
		    DBG(5, "get_layout: Condition Code Check Failed\n");
			break;
	}

	return FALSE;
 
     
}

/********************************************************************/
/* set_layout()                                                     */
/********************************************************************/
BOOL set_layout(TWAIN_Scanner *scanner, BOOL max_area)
{
     
    TW_UINT16       twRC = TWRC_FAILURE;
	TW_IMAGELAYOUT  twImageLayout;

	memset(&twImageLayout, 0, sizeof(TW_IMAGELAYOUT));
	if (!max_area) {
    	//scanner handle holds values of scanning area in unit: mm.
    	DBG(128, "set_layout: SANE_Word value of scanner->val[OPT_TL_X].w is %f\n", SANE_UNFIX(scanner->val[OPT_TL_X].w));
        double d = SANE_UNFIX(scanner->val[OPT_TL_X].w);
        TW_FIX32 fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Left = fix;
       	DBG(128, "set_layout: SANE_Word value of scanner->val[OPT_TL_Y].w is %f\n", SANE_UNFIX(scanner->val[OPT_TL_Y].w));
        d = SANE_UNFIX(scanner->val[OPT_TL_Y].w);
        fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Top =  fix;
        DBG(128, "set_layout: SANE_Word value of scanner->val[OPT_BR_X].w is %f\n", SANE_UNFIX(scanner->val[OPT_BR_X].w));
        d = SANE_UNFIX(scanner->val[OPT_BR_X].w);
        fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Right = fix;
    	DBG(128, "set_layout: SANE_Word value of scanner->val[OPT_BR_Y].w is %f\n", SANE_UNFIX(scanner->val[OPT_BR_Y].w));
        d = SANE_UNFIX(scanner->val[OPT_BR_Y].w);
        fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Bottom = fix;
    } else {
        //if preview mode fails to set, then we will set scanning area manually   
    	DBG(128, "set_layout: SANE_Word value of scanner->x_range.min is %f\n", SANE_UNFIX(scanner->x_range.min));
        double d = SANE_UNFIX(scanner->x_range.min);
        TW_FIX32 fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Left = fix;
       	DBG(128, "set_layout: SANE_Word value of scanner->y_range.min is %f\n", SANE_UNFIX(scanner->y_range.min));
        d = SANE_UNFIX(scanner->y_range.min);
        fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Top =  fix;
        DBG(128, "set_layout: SANE_Word value of scanner->x_range.max is %f\n", SANE_UNFIX(scanner->x_range.max));
        d = SANE_UNFIX(scanner->x_range.max);
        fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Right = fix;
    	DBG(128, "set_layout: SANE_Word value of scanner->y_range.max is %f\n", SANE_UNFIX(scanner->y_range.max));
        d = SANE_UNFIX(scanner->y_range.max);
        fix = FloatToFIX32(d / MM_PER_INCH);
        twImageLayout.Frame.Bottom = fix;
    }
 
	// Check ImageLayout information
	twRC = CallDSMEntry(&appID,
					&scanner->dev->dsID, 
					DG_IMAGE,
					DAT_IMAGELAYOUT, 
					MSG_SET, 
					(TW_MEMREF)&twImageLayout);

	DBG(5, "%s %d\n", "set_layout: Return Code from set ImageLayout Operation:", twRC);

	switch (twRC)
	{
		case TWRC_SUCCESS:
			DBG(5, "%s %.2f\n", "set_layout: Frame.Left:", FIX32ToFloat(twImageLayout.Frame.Left));
			DBG(5, "%s %.2f\n", "set_layout: Frame.Top:", FIX32ToFloat(twImageLayout.Frame.Top));
			DBG(5, "%s %.2f\n", "set_layout: Frame.Right:", FIX32ToFloat(twImageLayout.Frame.Right));
			DBG(5, "%s %.2f\n", "set_layout: Frame.Bottom:", FIX32ToFloat(twImageLayout.Frame.Bottom));
			DBG(5, "%s %d\n", "set_layout: DocumentNumber:", twImageLayout.DocumentNumber);
			DBG(5, "%s %d\n", "set_layout: PageNumber:", twImageLayout.PageNumber);
			DBG(5, "%s %d\n", "set_layout: FrameNumber:", twImageLayout.FrameNumber);

            if (max_area) {
    			scanner->val[OPT_TL_X].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Left) * MM_PER_INCH );
    			scanner->val[OPT_TL_Y].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Top) * MM_PER_INCH );
                scanner->val[OPT_BR_X].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Right) * MM_PER_INCH );
    			scanner->val[OPT_BR_Y].w = SANE_FIX( FIX32ToFloat(twImageLayout.Frame.Bottom) * MM_PER_INCH );
            }
            
        	return TRUE;

		case TWRC_FAILURE:
		default:
			DBG(5, "set_layout: Condition Code Check Failed\n");
			break;
	}

	return FALSE;
 
     
}

/********************************************************************/
/* set_preview_full_mode                                            */
/********************************************************************/
BOOL set_preview_full_mode(TWAIN_Scanner *scanner)
{

 BOOL status = FALSE; 

 DBG(10, "set_preview_full_mode...\n");
 
 //check if preview mode should be enabled or not
 if (scanner->val[OPT_PREVIEW].w) {
 
    DBG(10, "set_preview_full_mode: Set CAP_THUMBNAILSENABLED to TRUE\n");    
    status = SetOneScanParameter(scanner->dev, CAP_THUMBNAILSENABLED, TWON_ONEVALUE, TWTY_BOOL, TRUE);
 } else {
    DBG(10, "set_preview_full_mode: Set CAP_THUMBNAILSENABLED to FALSE\n");    
    status = SetOneScanParameter(scanner->dev, CAP_THUMBNAILSENABLED, TWON_ONEVALUE, TWTY_BOOL, FALSE);
 }

  //if scanner does not support preview mode it will return full image, it is not an error
  if ((!status)&&(gGlobalStatus.ConditionCode == TWCC_BADCAP)) {
    set_layout(scanner, TRUE);
    set_resolution(scanner, 50);
    status = TRUE;
  }

  return status;
 
  DBG(10, "set_preview_full_mode: done\n");

}

int get_max_resolution(TWAIN_Scanner *scanner)
{
  
  //TODO: retrieve maximal resolution from TWAIN ds
  //TODO: default_pixels_x and default_pixels_y are set according value 300 for resolution.   
  return 300;   
                                 
}

BOOL set_resolution(TWAIN_Scanner *scanner, int resolution)
{
     
  BOOL status = FALSE;   

  DBG(10, "set_resolution: Set resolution to value: %d\n", resolution);    
     
  DBG(10, "set_resolution: Set ICAP_XRESOLUTION\n");    
  status = SetOneScanParameter(scanner->dev, ICAP_XRESOLUTION, TWON_ONEVALUE, TWTY_FIX32, DoubleToFIX32(resolution));
  if (!status)
     return FALSE;

  DBG(10, "set_resolution: Set ICAP_YRESOLUTION\n");    
  status = SetOneScanParameter(scanner->dev, ICAP_YRESOLUTION, TWON_ONEVALUE, TWTY_FIX32, DoubleToFIX32(resolution));
  if (!status)
     return FALSE;
  
  return TRUE;
                                 
}


/********************************************************************/
/* SANE INTERFACE METHODS                                           */
/********************************************************************/

/********************************************************************/
/* sane_init()                                                      */
/********************************************************************/
SANE_Status
sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
{
  char dev_name[PATH_MAX];
  size_t len;
  FILE *fp;

  authorize = authorize;
  DBG_INIT();
  DBG(1, "sane_init:  TWAIN says hello! (v%d.%d.%d)\n",
      TWAIN_MAJOR, TWAIN_MINOR, TWAIN_PATCH);
  /* return the SANE version we got compiled under */
  if (version_code)
    *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0);

  /* parse config file */
  fp = sanei_config_open (TWAIN_CONFIG_FILE);
  if (!fp) {
    DBG(1, "sane_init:  missing config file '%s'\n", TWAIN_CONFIG_FILE);
    //TODO: Default configuration
  } else {
      //TODO: Process lines from config file
      while (sanei_config_read(dev_name, sizeof (dev_name), fp)) {
        DBG(23, "sane_init:  config-> %s\n", dev_name);
        if (dev_name[0] == '#') continue;	/* ignore comments */
    
        len = strlen (dev_name);
        if (!len) continue;			/* ignore empty lines */
        }
      fclose (fp);
  }  
  
  SANE_Status status = twain_open_dsm();
  
  if (status != SANE_STATUS_GOOD)
     return status;

  DBG(10, "sane_init: Opening Data Source\n");
  
  TWAIN_Device *td;
  td=malloc(sizeof(*td));
  td->TWDSOpen = FALSE;
  td->TWDSEnabled = FALSE;
  /* function twain_open_ds() will also attach_scanner if DS is successfully opened */
  //TODO: resolve how to does not attach scanner, if DS is successfully opened but scanner is not attached
  //then GUI is displayed but  if GUI does not find any scanner then DS remains successfully opened :(
  status = twain_open_ds(td);
  if (status != SANE_STATUS_GOOD)
     return status;
  if (SetScanParameters(td)) {
     status = SANE_STATUS_GOOD;
  } else {
     status = SANE_STATUS_INVAL;    
  }
    
  return status;
}


/********************************************************************/
/* sane_exit                                                        */
/********************************************************************/
void
sane_exit (void)
{
  TWAIN_Device *next;

  DBG(10, "sane_exit...\n");

  /* close all leftover Scanners */
  /*(beware of how sane_close interacts with linked list) */
  while (first_handle != NULL)
    sane_close(first_handle);
  /* free up device list */
  while (first_dev != NULL) {
    twain_close_ds(first_dev);
    twain_close_dsm(first_dev);
    next = first_dev->next;
    free((void *) first_dev->sane.name);
    free((void *) first_dev->sane.model);
    free(first_dev);
    first_dev = next;
  }
  /* the devlist allocated by sane_get_devices */
  free(devlist);

  DBG(10, "sane_exit:  TWAIN says goodbye.\n");
} 

/********************************************************************/
/* sane_get_devices                                                 */
/********************************************************************/
SANE_Status
sane_get_devices(const SANE_Device ***device_list, 
		 SANE_Bool local_only)
{
  TWAIN_Device *dev;
  int i;

  DBG(10, "sane_get_devices\n");
  /* we keep an internal copy */
  if (devlist)
    free(devlist);  /* hmm, free it if we want a new one, I guess.  YYYYY*/

  devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
  if (!devlist) return SANE_STATUS_NO_MEM;

  for (i=0, dev=first_dev; i < num_devices; dev = dev->next)
    devlist[i++] = &dev->sane;
  devlist[i++] = 0;

  *device_list = devlist;
  return SANE_STATUS_GOOD;
} 

/********************************************************************/
/* sane_open                                                        */
/********************************************************************/
SANE_Status
sane_open(SANE_String_Const devicename,
	  SANE_Handle *handle)
{
  TWAIN_Scanner *scanner;
  TWAIN_Device *dev;
  SANE_Status status;

  DBG(10, "sane_open\n");
  DBG(10, "sane_open:  find device...\n");
  if (devicename[0]) {
    for (dev = first_dev; dev; dev = dev->next) {
      if (strcmp(dev->sane.name, devicename) == 0)
	break;
    }
  } else {  /* no device specified, so use first */
    dev = first_dev;
  }
  if (!dev) return SANE_STATUS_INVAL;

  /* create a scanner... */
  DBG(10, "sane_open:  create scanner...\n");
  scanner = malloc(sizeof(*scanner));
  if (!scanner) return SANE_STATUS_NO_MEM;
  memset(scanner, 0, sizeof(*scanner));

  scanner->busy = FALSE;
  scanner->dev = dev;
//  scanner->send_image_header = TRUE;

  DBG(10, "sane_open:  initialize options  \n");
  if ((status = init_options(scanner)) != SANE_STATUS_GOOD) return status; 

  scanner->next = first_handle;
  first_handle = scanner;
  *handle = scanner;

  return SANE_STATUS_GOOD;
}

/********************************************************************/
/* sane_close                                                       */
/********************************************************************/
void
sane_close (SANE_Handle handle)
{
  TWAIN_Scanner *ts = handle;

  DBG(10, "sane_close...\n");

  /* free malloc'ed stuff (strdup counts too!) */
  free((void *) ts->sod[OPT_MODE].constraint.string_list);
  free(ts->val[OPT_MODE].s);
  free(ts->hbm_acq);

  /* remove Scanner from linked list */
  if (first_handle == ts)
    first_handle = ts->next;
  else {
    TWAIN_Scanner *ts = first_handle;
    while ((ts != NULL) && (ts->next != ts)) ts = ts->next;
    ts->next = ts->next->next; /* == ts->next */
  }
  /* finally, say goodbye to the Scanner */
  
  free(ts);
  
}  

/********************************************************************/
/* sane_start                                                       */
/********************************************************************/
SANE_Status
sane_start (SANE_Handle handle)
{
  TWAIN_Scanner *scanner = handle;
  SANE_Status status;

  DBG(10, "sane_start...\n");

  if (scanner->busy == TRUE) {
    DBG(23, "sane_start:  scanner busy flagged was already set!\n");
    return SANE_STATUS_DEVICE_BUSY;
  }

  scanner->scanning = SANE_TRUE;
  scanner->cancel = SANE_FALSE;
  scanner->processed = 0;
  
/* this will be set in method sane_control_option
  if (!set_preview_full_mode(scanner)) {
     return SANE_STATUS_INVAL;
  }
*/
  
  DBG(10, "sane_start: Enabling Data Source\n");
  status = twain_enable_ds(scanner->dev);

  if (status != SANE_STATUS_GOOD) {
    DBG(10, "sane_start: Failed to enable Data Source\n");
    return status;
  }

  if (!(TWAcquire(scanner->dev)))
     return SANE_STATUS_INVAL;


//TODO: Resolve how to wait for XFERREADY message from DS
/*
  if (!(WaitTillTransferIsReady(scanner->dev))){
     return SANE_STATUS_INVAL;
  }
*/   
  if (!(DoNativeTransfer(scanner))){
     return SANE_STATUS_INVAL;
  }
  //now if transfer was successfull then s->hbm_acq contains handle to image data
  
  if (scanner->hbm_acq) {
    LPBITMAPINFO pdib;
    BYTE* pDib;
	pDib = (BYTE*)GlobalLock(scanner->hbm_acq);
	pdib = (LPBITMAPINFO)pDib;
	scanner->Width = pdib->bmiHeader.biWidth;
	scanner->Height = pdib->bmiHeader.biHeight;
	scanner->Size = pdib->bmiHeader.biSize;
	scanner->SizeImage = pdib->bmiHeader.biSizeImage;
	scanner->BitCount = pdib->bmiHeader.biBitCount;
	scanner->ClrUsed = pdib->bmiHeader.biClrUsed;
	scanner->undelivered_bytes = scanner->SizeImage;
    GlobalUnlock(scanner->hbm_acq);    


    scanner->params.depth = 8;    
    scanner->params.bytes_per_line = scanner->Width * 3;
    scanner->params.format = SANE_FRAME_RGB;
    scanner->params.last_frame = SANE_TRUE;
    scanner->params.lines = scanner->Height;
    scanner->params.pixels_per_line = scanner->Width;
    scanner->RowLength = scanner->params.bytes_per_line;
    scanner->Padding = ((scanner->SizeImage - (scanner->Width * 3 * scanner->Height) ) / scanner->Height);
    scanner->RowLengthWithPad = scanner->RowLength + scanner->Padding;

    scanner->RowPosition = 0;

    scanner->line_multipl = 1;
    //2, 0, -2
    scanner->rgb_order = 2;
    
    DBG(5, "!!!!!!!!!!! Size: %d\n", scanner->Size);
    DBG(5, "!!!!!!!!!!! SizeImage: %d\n", scanner->SizeImage);
    DBG(5, "!!!!!!!!!!! Width: %d\n", scanner->Width);
    DBG(5, "!!!!!!!!!!! Height: %d\n", scanner->Height);
    DBG(5, "!!!!!!!!!!! Width * Height: %d\n", scanner->Width * scanner->Height);
    DBG(5, "!!!!!!!!!!! BitCount: %d\n", scanner->BitCount);
    
  }

  return status;
} 

/********************************************************************/
/* sane_cancel                                                      */
/********************************************************************/
void
sane_cancel (SANE_Handle handle)
{
  TWAIN_Scanner *ts = handle;
  DBG(10, "sane_cancel...\n");
  
  
  ts->cancel = TRUE;
  DBG(10, "sane_close: Disabling Data Source\n");
  twain_disable_ds(ts->dev);
  ts->busy = FALSE;
  ts->scanning = SANE_FALSE;



} 

/********************************************************************/
/* sane_get_option_descriptor                                       */
/********************************************************************/
const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle,
			    SANE_Int option)
{
  TWAIN_Scanner *scanner = handle;

  DBG(10, "sane_get_option_descriptor (%d)...\n", option);
  if (option >= NUM_OPTIONS) return NULL;
  return &(scanner->sod[option]);
}

/********************************************************************/
/* sane_control_option                                              */
/********************************************************************/
SANE_Status 
sane_control_option (SANE_Handle handle,
		     SANE_Int option,
		     SANE_Action action,
		     void *value,
		     SANE_Int *info)
{
  TWAIN_Scanner *scanner = handle;
  SANE_Option_Descriptor *sod;
  Option_Value  *val;
  SANE_Status status;

  DBG(10, "sane_control_option (opt=%d,act=%d,val=%p,info=%p)\n",
      option, action, value, info);

  sod = scanner->sod;
  val = scanner->val;

  /* no changes while in mid-pass! */
  if (scanner->scanning) return SANE_STATUS_DEVICE_BUSY;

  if ( ((option >= NUM_OPTIONS) || (option < 0)) ||
       (!SANE_OPTION_IS_ACTIVE(scanner->sod[option].cap)) )
    return SANE_STATUS_INVAL;

  if (info) *info = 0;

  /* choose by action */
  switch (action) {

  case SANE_ACTION_GET_VALUE:
    switch (option) {
      /* word options... */
    case OPT_RESOLUTION: 
    case OPT_BACKTRACK:
    case OPT_PREVIEW:
         *(SANE_Word *)value = val[option].w;
         return SANE_STATUS_GOOD;
    case OPT_MODE:
      strcpy(value, val[option].s);
      return SANE_STATUS_GOOD;       
    case OPT_TL_X:
    case OPT_TL_Y:
    case OPT_BR_X:
    case OPT_BR_Y: 
      status = get_layout(scanner);   
      *(SANE_Word *)value = val[option].w;
      if (status)
         return SANE_STATUS_GOOD;
      else 
         return SANE_STATUS_INVAL;    
    case OPT_NUM_OPTS:
      *(SANE_Word *) value = NUM_OPTIONS;
      return SANE_STATUS_GOOD;
    default:
      return SANE_STATUS_INVAL;
    }
    break; 
        
  case SANE_ACTION_SET_VALUE: {
    status = sanei_constrain_value(sod + option, value, info);
    if (status != SANE_STATUS_GOOD)
      return status;
    
    switch (option) {
      /* set word options... */
    case OPT_PREVIEW:
      val[option].w = *(SANE_Word *)value;
      BOOL status = set_preview_full_mode(scanner);
      if (status)
         return SANE_STATUS_GOOD;
      else 
         return SANE_STATUS_INVAL;
    case OPT_TL_X:
    case OPT_TL_Y:
    case OPT_BR_X:
    case OPT_BR_Y:    
      if (info)
	     *info |= SANE_INFO_RELOAD_PARAMS;   

      val[option].w = *(SANE_Word*)value;
      if (set_layout(scanner, FALSE)) {
            return SANE_STATUS_GOOD; 
      } else {
             return SANE_STATUS_INVAL;
      }
    case OPT_RESOLUTION:
      if (info)
	     *info |= SANE_INFO_RELOAD_PARAMS; 
      val[option].w = *(SANE_Word *)value;
      if (set_resolution(scanner, SANE_UNFIX(val[option].w))) {
         return SANE_STATUS_GOOD; 
      } else {
         return SANE_STATUS_INVAL;
      }
    case OPT_MODE: 
        //Contains always value: Color
        return SANE_STATUS_GOOD; 
        
    default:
      return SANE_STATUS_INVAL;
    }
  }
  break; 
  
  }
  return SANE_STATUS_GOOD;
}

/********************************************************************/
/* sane_set_io_mode                                                 */
/********************************************************************/
SANE_Status
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
{
  DBG(10, "sane_set_io_mode...\n");
  handle = handle;
  if (non_blocking)
    return SANE_STATUS_UNSUPPORTED;
  else
    return SANE_STATUS_GOOD;
}  

/********************************************************************/
/* sane_get_select_fd                                               */
/********************************************************************/
SANE_Status
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
{
  DBG(10, "sane_get_select_fd...\n");
  handle = handle, fd = fd;
  return SANE_STATUS_UNSUPPORTED;
} 

/********************************************************************/
/* sane_read                                                        */
/********************************************************************/
SANE_Status 
sane_read (SANE_Handle handle, SANE_Byte *dest_buffer,
		SANE_Int dest_length, SANE_Int *ret_length)
{
  TWAIN_Scanner *s = handle;
  SANE_Status status;

  DBG(10, "sane_read...\n");
  
  *ret_length = 0; /* default: no data */
  
  /* we have been cancelled... */
  //TODO: bind flags cancel and scanning with DoNativeTransferMethod
  if (s->cancel) return SANE_STATUS_GOOD;
  /* we're not really scanning!... */
  if (!(s->scanning)) return SANE_STATUS_INVAL;
  /* we are done scanning... */
  if (s->undelivered_bytes <= 0) return SANE_STATUS_EOF; 

  DBG(10, "sane_read: start reading part of TWAIN bitmap\n");
  if (s->hbm_acq) {    
    if (s->undelivered_bytes >= dest_length) {
        *ret_length = dest_length;
    } else {
        *ret_length = s->undelivered_bytes; 
    }
    DBG(10, "sane_read: Try to deliver %d bytes \n", *ret_length);
    LPBITMAPINFO pDib = (LPBITMAPINFO)GlobalLock(s->hbm_acq);
    BYTE* ptr;
    int i;
    for (i = 0; i < *ret_length; i++ ) {
        ptr = (unsigned char TW_HUGE *) pDib;
		ptr += sizeof(BITMAPINFOHEADER);
		ptr += pDib->bmiHeader.biClrUsed * sizeof(RGBQUAD);

        if (s->undelivered_bytes == 0) {
           DBG(4, "sane_read: All image data was delivered. No more image data are available. Length of delivered data: %d\n", s->processed);
           s->busy = FALSE;
           s->scanning = SANE_FALSE;
           return SANE_STATUS_EOF;
        }

        //(*(dest_buffer + i)) = ( *(pDib + (s->SizeImage /*- s->processed - i*/ + 1) - (s->line_multipl * s->RowLength) + s->RowPosition +s->rgb_order +38) );
        if (s->RowPosition < s->RowLength) {
                    //(*(dest_buffer + i)) = ( *(ptr + (s->SizeImage + 1) - (s->line_multipl * s->RowLengthWithPad) + s->RowPosition + s->rgb_order ) + s->Padding );
                    (*(dest_buffer + i)) = ( *(ptr + (s->SizeImage) - (s->line_multipl * s->RowLengthWithPad) + s->RowPosition + s->rgb_order ) );
                    if (s->rgb_order == -2) s->rgb_order = 2;
                    else if (s->rgb_order == 0) s->rgb_order = -2;
                    else if (s->rgb_order == 2) s->rgb_order = 0;
                    
                    s->processed++;

        } else {
               //padding is not added to result, we are going to skip it
               i--;
        }
        
        (s->RowPosition)++;
        
        
        if (s->RowPosition >= s->RowLengthWithPad) {
           s->RowPosition = 0;
           s->line_multipl++;
        }
        
        s->undelivered_bytes--;
    }
    
    GlobalUnlock(s->hbm_acq);
  } else {
    //if handle to bitmap read by TWAIN is not valid     
    DBG(10, "sane_read: Error TWAIN bitmap is NULL\n");
    return SANE_STATUS_EOF;
  }

  return SANE_STATUS_GOOD;
}

/********************************************************************/
/* sane_get_parameters                                              */
/********************************************************************/
SANE_Status
sane_get_parameters (SANE_Handle handle,
		     SANE_Parameters *params)
{
  TWAIN_Scanner *s = handle;

  DBG(10, "sane_get_parameters...\n");

  if (s->dev->TWDSEnabled) {
    DBG(5, "Parameters are set after image was loaded from scanner\n");
    
    params->bytes_per_line = s->params.bytes_per_line;
    params->depth = s->params.depth;
    params->format = s->params.format;
    params->last_frame = s->params.last_frame;
    params->lines = s->params.lines;
    params->pixels_per_line = s->params.pixels_per_line;

  } else {
    DBG(5, "Parameters are set before image is loaded from scanner - default parameters set\n");
    /*TWEnableDS(s->dev);
    GetImageInfo(handle, params);
    TWDisableDS(s->dev);
    */
    params->pixels_per_line = default_pixels_x;
    params->lines = default_pixels_y;
    params->bytes_per_line = 3 * params->pixels_per_line;
    params->depth = 8;
    params->format = SANE_FRAME_RGB;
    params->last_frame = TRUE;
  }

  DBG(5, "!!!!!!!!!!! params->bytes_per_line: %d\n", params->bytes_per_line);
  DBG(5, "!!!!!!!!!!! params->depth: %d\n", params->depth);
  DBG(5, "!!!!!!!!!!! params->format: %d\n", params->format);
  DBG(5, "!!!!!!!!!!! params->last_frame: %d\n", params->last_frame);
  DBG(5, "!!!!!!!!!!! params->lines: %d\n", params->lines);
  DBG(5, "!!!!!!!!!!! params->pixels_per_line: %d\n", params->pixels_per_line);

  return SANE_STATUS_GOOD;
}
 
