/*->c.tw */

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

#include "h.os"
#include "h.wimp"
#include "h.wimpt"
#include "h.werr"
#include "h.flex"


#include "h.Drawlevel0"


#include "h.def"
#include "h.wos"
#include "h.main"
#include "h.mym"
#include "h.serial"
#include "h.ram"
#include "h.file"
#include "h.pr"
#include "h.strdef"
#include "h.key"
#include "h.script"
#include "h.dir"
#include "h.batch"
#include "h.fsx"
#include "h.timex"
#include "h.fax"
#include "h.view"
#include "h.tiff"
#include "h.buffer"
#include "h.etc"

#include "h.code"

#include "h.text"
#include "h.config"
#include "h.newfax"
#include "h.faxfix"


#include "h.TWAIN"
#include "h.glue"
#include "h.acq"


#include "h.tw"



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

/* entry for intercepting wimp events */

int twainpoll(wimp_eventstr * wimpevent)
{
 if(TWProcessMessage(wimpevent)) return(1);
 else                            return(0);
}


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


static void twerror(os_error * err)
{
 report(err);
}


/* called when a source is selected */

static os_error * selectend(int ok)
{
 remzeroevent(TWAINZERO);
 TWCloseDSM();
 return(NULL);
 USE(ok);
}

/* open select source dialogue */

os_error * twainselect(void)
{
 os_error * err;

 if((err=TWOpenDSM())==NULL)
 {
  TWSelectDS(selectend);
  addzeroevent(TWAINZERO);
 }

 return(err);
}

/****************************************************************************/
/* code for acquiring images */

static TWAcquireblockstr acqblock;

static int acqxpix;
static int acqypix;
static int acqbpp;
static int acqxres;
static int acqyres;
static int acqcolours;

static char acqpal[256];

static int npages;
static int maxwidth;
static int xres;
static int yres;
static int maxlines;
static int lines;

static int sopoffset;
static int dataoffset;

static codestr cx;

static buffer  xbuff;

static char faxname[256];


#define DESTSIZE 0x4000


static int iswhite(int word)
{
 int r;
 int g;
 int b;

 r=(word>>8)&0xFF;
 g=(word>>16)&0xFF;
 b=(word>>24)&0xFF;

 return((r*r+b*b+g*g)>(128*128));
}


static os_error * twstartscan(void)
{
 os_error * err;
 int        fo;

 genbatchname(TXBFILE,faxname,TIFF);

 err=fs_open(faxname,'w',&fo);
 if(!err)
 {

  bf_alloc(&xbuff,DESTSIZE,fo,0);
  tiff_hdr(fo);

  maxwidth=1728;
  maxlines=acqypix;
  npages=1;

  xres=XRES;
  if((acqyres*4)>(acqxres*3)) yres=YRESHI;
  else                        yres=YRESLO;

  lines=0;

  cx.bf=&xbuff;
  cx.width=maxwidth;
  cx.height=maxlines;
  cx.line=0;
  cx.bitrev=0;

  if(datacompression) cx.type=TIFFCCITT4;
  else                cx.type=FAXCCITT3;

  cx.putbyte=bf_put;
  cx.newbuffer=1;

  if(!err) err=fs_tell(xbuff.fh,&sopoffset);
  if(!err) err=faxifd(xbuff.fh,maxwidth,maxlines,xres,yres,0,
                                           cx.type,npages,npages+1);
  if(!err) err=fs_tell(xbuff.fh,&dataoffset);

  if(!err) err=codeinit(&cx);
 }
 return(err);
}


static os_error * endscan(void)
{
 os_error * err;
 int        nextoffset;
 int        bn;

 err=cx.puteop(&cx);

 codefinit(&cx);

 if(!err) err=bf_dump(&xbuff);
 if(!err) err=fs_tell(xbuff.fh,&nextoffset);
 if(!err) err=fs_seek(xbuff.fh,sopoffset);
 if(!err) err=faxifd(xbuff.fh,maxwidth,lines,xres,yres,
                            nextoffset-dataoffset,cx.type,npages,npages);

 err=fs_close(xbuff.fh,err);
 setftype(faxname,TIFF);
 err=bf_dealloc(&xbuff);

 if(!err)
 {
  bn=addtotxbatch(TIFF,faxname,1,0)-1;
  vtable[TXBFILE][bn].tsum.xres=xres;
  vtable[TXBFILE][bn].tsum.yres=yres;
  vtable[TXBFILE][bn].tsum.maxwidth=maxwidth;
  vtable[TXBFILE][bn].tsum.maxlines=lines;
  vtable[TXBFILE][bn].tsum.npages=npages;
  newfax(bn); /* bn can change - rename */
  saveconfig();
 }

 return(err);
}


/* process one line, using acqpal table of black/white info */

static os_error * processline(char * p)
{
 int width;
 int word;
 int i;
 int shift;
 int mask;
 int sstep;
 int sbyte;

 width=MIN(maxwidth,acqxpix);

 codebitstart(&cx);

 if(acqbpp>8)
 {
  for(i=0;i<width;i++)
  {
   word=(*p++)<<8;
   word|=(*p++)<<16;
   word|=(*p++)<<24;

   codebit(&cx,iswhite(word));
  }
 }
 else
 {
  word=0;   /* compiler */
  shift=32;

  if(acqbpp==1) {mask=0x1;sstep=1;}
  else
  if(acqbpp==2) {mask=0x3;sstep=2;}
  else
  if(acqbpp==4) {mask=0xF;sstep=4;}
  else          {mask=0xFF;sstep=8;}


  for(i=0;i<width;i++)
  {
   if(shift==32)
   {
    word=*((int*)p);
    p+=sizeof(int);
    shift=0;
   }

   sbyte=(word>>shift)&mask;
   shift+=sstep;

   codebit(&cx,acqpal[sbyte]);
  }
 }

 codebitend(&cx);

 cx.putline(&cx,0,1);

 lines++;
 return(NULL);
}


/****************************************************************************/
/* code for acquiring sprites directly */

static os_error * getmempal(int * palw,int * ncolours,
                                       int bpp,sprite_header * sph)
{
 os_error * err;
 int        palentries;
 int        paltab[257];
 int        i;
 int        c1;
 int      * pal;

 err=NULL;

 pal=(int*)((char*)sph+sizeof(sprite_header));

 palentries=(sph->image-sizeof(sprite_header))/8;
 if((palentries > 256) || (palentries < 0)) palentries = 0;
 if(palentries==0)
 {
  /* if there is no palette, we'd like it to look like wimp cols */

  if(bpp==8)
  {
   palentries=16;
   paltab[0]=0x0;
   paltab[1]=0x10101000;
   paltab[2]=0x20202000;
   paltab[3]=0x30303000;
   paltab[4]=0x4000;
   paltab[5]=0x10105000;
   paltab[6]=0x20206000;
   paltab[7]=0x30307000;
   paltab[8]=0x40000000;
   paltab[9]=0x50101000;
   paltab[10]=0x60202000;
   paltab[11]=0x70303000;
   paltab[12]=0x40004000;
   paltab[13]=0x50105000;
   paltab[14]=0x60206000;
   paltab[15]=0x70307000;
   palentries=16;
  }
  else
  if(bpp==4)
  {
   paltab[0]=0xffffff00;
   paltab[1]=0xdddddd00;
   paltab[2]=0xbbbbbb00;
   paltab[3]=0x99999900;
   paltab[4]=0x77777700;
   paltab[5]=0x55555500;
   paltab[6]=0x33333300;
   paltab[7]=0x00000000;
   paltab[8]=0x99440000;
   paltab[9]=0x00eeee00;
   paltab[10]=0x00cc0000;
   paltab[11]=0x0000dd00;
   paltab[12]=0xbbeeee00;
   paltab[13]=0x00885500;
   paltab[14]=0x00bbff00;
   paltab[15]=0xffbb0000;
   palentries=16;
  }
  else
  if(bpp==2)
  {
   paltab[0]=0xffffff00;
   paltab[1]=0xdddddd00;
   paltab[2]=0x99999900;
   paltab[3]=0x00000000;
   palentries=4;
  }
  else
  if(bpp==1)
  {
   paltab[0]=0xffffff00;
   paltab[1]=0x00000000;
   palentries=2;
  }
 }
 else
 {
  for(i=0;i<palentries;i++)
  {
   *(paltab+i)=*pal;
   pal+=2;
  }

  if(bpp==8 && (palentries==64 || palentries==16))
  {
   if(palentries==64)
   {
    for(i=0;i<16;i++) paltab[i]=paltab[i+48];
   }

   for(i=255;i>=0;i--)
   {
    c1=paltab[(i & 0xF)];

    if(i & 0x10) c1|=0x8000;
    else         c1&=~0x8000;

    if(i & 0x20) c1|=0x400000;
    else         c1&=~0x400000;

    if(i & 0x40) c1|=0x800000;
    else         c1&=~0x800000;

    if(i & 0x80) c1|=0x80000000;
    else         c1&=~0x80000000;

    c1&=0xF0F0F000;
    c1|=(c1>>4)&0xF0F0F00;

    paltab[i]=c1;
   }
   palentries=256;
  }
 }

 *ncolours=palentries;
 for(i=0;i<palentries;i++) palw[i]=paltab[i];

 return(err);
}


static os_error * acqspritesub(void)
{
 os_error      * err;
 int             spdx;
 int             spdy;
 int             spbpp;
 int             spbitmask;
 int             bitwidth;
 sprite_header * sph;
 int             pal[256];
 int             ncolours;
 int             i;

 int             y;

 char          * spdata;

  err=NULL;

 /* just received a sprite in a sprite area... */

  if(!err)
  {
   sph=(sprite_header *)((char*)acqblock.memory.native.sp+ 
                                       acqblock.memory.native.sp->sproff);

   getdeltas(sph->mode,&spdx,&spdy);
   getbpp(sph->mode,&spbpp,&spbitmask);
   spbpp=(1<<spbpp);

   bitwidth=32*(sph->width+1)-sph->lbit-(31-sph->rbit);

   acqbpp=spbpp;
   acqxpix=bitwidth/spbpp;
   acqypix=sph->height+1;
   acqxres=180/spdx;
   acqyres=180/spdy;
  
   if(!err)
   {
    getmempal(pal,&ncolours,spbpp,sph);
    for(i=0;i<ncolours;i++) acqpal[i]=iswhite(pal[i]);

    spdata=(char*)sph+sph->image;


    err=twstartscan();

    for(y=0;y<acqypix;y++)
    {
     processline(spdata);
     spdata+=(sph->width+1)*sizeof(int);
    }
   }
   err=endscan();
  }

 return(err);
}



static os_error * acqsprite(int last)
{
 os_error * err;

 err=acqspritesub();

 flex_free((flex_ptr)&acqblock.memory.native.sp);
 
 if(!err)
 {

 }

 return(err);
}


/****************************************************************************/
/* code for direct memxfers */

static int        memline;


static os_error * acqmemxferpalette(int * paltab,int nc)
{
 int i;
 for(i=0;i<nc;i++) acqpal[i]=iswhite(paltab[i]); 
 acqcolours=nc;

 return(NULL);
}


static os_error * acqsetmemxfer(int xpix,int ypix,int bpp,int xres,int yres)
{
 os_error * err;

 err=NULL;

 memline=0;

 acqxpix=xpix;
 acqypix=ypix;
 acqbpp=bpp;
 acqxres=xres;
 acqyres=yres;

 return(err);
}


/* just received a chunk */

static os_error * acqmemxfer(int size,int last,int bpr,int rows)
{
 os_error * err;
 int        i;

 err=NULL;

 if(!memline) twstartscan();

 for(i=0;i<rows;i++)
 {
  processline(((char*)acqblock.memory.memxfer.buffer)+bpr*i); 
  memline++;
 }

 if(last)
 {
  endscan();
 }

 return(err);
}

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

static os_error * acqend(void)
{
 if(acqblock.transfertype==TWSX_NATIVE)
 {
  if(acqblock.memory.native.sp)
                        flex_free((flex_ptr)&acqblock.memory.native.sp);
 }
 else
 if(acqblock.transfertype==TWSX_MEMORY)
 {
  if(acqblock.memory.memxfer.buffer)
                        flex_free((flex_ptr)&acqblock.memory.memxfer.buffer);

 }

 remzeroevent(TWAINZERO);
 return(NULL);
}


static os_error * acqinfo(int xpix,int ypix,int bpp,int xres,int yres)
{
 os_error * err;


 /* dprintf(1,"acqinfo"); */

 err=NULL;

 if(acqblock.transfertype==TWSX_MEMORY)
 {
  err=acqsetmemxfer(xpix,ypix,bpp,xres,yres);
 }

/* if(err) dprintf(1,"acqinfo error"); */


 return(err);
}

static os_error * acqpalette(int * paltab,int nc)
{
 os_error * err;

 err=NULL;

/* dprintf(1,"acqpal"); */

 if(acqblock.transfertype==TWSX_MEMORY)
 {
  err=acqmemxferpalette(paltab,nc);
 }

/* if(err) dprintf(1,"acqpal  error"); */

 return(err);
}



static os_error * acqtransfer(int size,int last,int bpr,int rows)
{
 os_error * err;

 err=NULL;

 if(acqblock.transfertype==TWSX_NATIVE) err=acqsprite(last);
 else
 if(acqblock.transfertype==TWSX_MEMORY) err=acqmemxfer(size,last,bpr,rows);

 return(err);
}


#define SPARE 0x1000

static os_error * acqalloc(int size)
{
 os_error * err;

 err=NULL;

 if(acqblock.transfertype==TWSX_NATIVE)
 {
  size+=SPARE;
  err=flex_alloce((flex_ptr)&acqblock.memory.native.sp,size);
  sprite_area_initialise(acqblock.memory.native.sp,size);
 }
 else
 if(acqblock.transfertype==TWSX_MEMORY)
 {
  err=flex_alloce((flex_ptr)&acqblock.memory.memxfer.buffer,size);

 }

 return(err);
}


static int pxcount;
static int pxtime;


static void acqpoll(void)
{
 poll(FAXPOLL);
}



os_error * twainacquire(void)
{
 addzeroevent(TWAINZERO);

 TWAcquireDefault(&acqblock);


/* if(selection)  
 {
  acqblock.setarea=1;
  acqblock.area.x0=sbox.x;
  acqblock.area.y1=sbox.y;
  acqblock.area.x1=sbox.x+sbox.w;
  acqblock.area.y0=sbox.y+sbox.h;
 } */


 acqblock.transfertype=TWSX_MEMORY;
 acqblock.settransfertype=1;

 acqblock.TWend=acqend;
 acqblock.TWalloc=acqalloc;
 acqblock.TWtrans=acqtransfer;
 acqblock.TWinfo=acqinfo;
 acqblock.TWpoll=acqpoll;
 acqblock.TWpal=acqpalette;
 acqblock.TWerror=twerror;

 TWAcquire(&acqblock);

 return(NULL);
}


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

void twainfinit(void)
{
 TWSelectTerminate(0);
 TWTerminate();
}

os_error * twaininit(void)
{
 TW_IDENTITY AppIdentity;

 AppIdentity.Id=0;/* init to 0, but Source Manager will assign real value */

 AppIdentity.Version.MajorNum=1;
 AppIdentity.Version.MinorNum=0;
 AppIdentity.Version.Language=TWLG_ENG;
 AppIdentity.Version.Country=TWCY_UNITEDKINGDOM;
 strcpy(AppIdentity.Version.Info,wimpt_programname());

 AppIdentity.ProtocolMajor=TWON_PROTOCOLMAJOR;
 AppIdentity.ProtocolMinor=TWON_PROTOCOLMINOR;
 AppIdentity.SupportedGroups=DG_IMAGE | DG_CONTROL;

 strcpy(AppIdentity.Manufacturer,"David Pilling");
 strcpy(AppIdentity.ProductFamily,wimpt_programname());
 strcpy(AppIdentity.ProductName,wimpt_programname());

 /* pass app particulars to glue code */
 
 TWInitialize(&AppIdentity,taskhandle);

 return(NULL);
}

