/*->c.link */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"
#include "h.swis"

#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.xext"

#include "h.code"

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

#include "h.link"




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


static int sopoffset;        /* offset of start of page */
static int dataoffset;       /* offset of data          */



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


static os_error * xxstart(int fo)
{
 os_error * err;

 npages++;

         err=fs_tell(fo,&sopoffset);
 if(!err)err=faxifd(fo,maxwidth,lines,xres,yres,
                                          0,TIFFCCITT4,npages,npages+1);
 if(!err)err=fs_tell(fo,&dataoffset);

 return(err);
}


static os_error * xend(int fo)
{
 os_error * err;

 int        nextoffset;

          err=fs_tell(fo,&nextoffset);
 if(!err) err=fs_seek(fo,sopoffset);
 if(!err) err=faxifd(fo,maxwidth,lines,xres,yres,nextoffset-dataoffset,
                                                 TIFFCCITT4,npages,npages+1);
 if(!err) err=fs_seek(fo,nextoffset);

 if(lines>maxlines) maxlines=lines;

 return(err);
}


static os_error * xcopy(int fi,int fo)
{
 os_error * err;
 int        len;
 int        c;
 char     * buffer;
 int        size;
 int        transfer;
 int        chunk;


 err=fs_getc(fi,&c);
 len=c;
 err=fs_getc(fi,&c);
 len+=c<<8;

 len-=4;

 transfer=len;
 size=transfer;

 do
 {
  err=flex_alloce((flex_ptr)&buffer,size);
  if(!err) break;
  size/=2;
 } while(size>128);


 if(!err)
 {
  while(transfer>0)
  {
   chunk=MIN(size,transfer);

   fs_read(fi,buffer,chunk);
   fs_write(fo,buffer,chunk);

   transfer-=chunk;
  }
  flex_free((flex_ptr)&buffer);
 }

 return(err);
}



static os_error * xwidth(int fi)
{
 os_error * err;
 int        c;

 err=fs_getc(fi,&c);
 maxwidth=c;
 err=fs_getc(fi,&c);
 maxwidth+=c<<8;

 return(err);
}


static os_error * xlength(int fi)
{
 os_error * err;
 int        c;

 err=fs_getc(fi,&c);
 lines=c;
 err=fs_getc(fi,&c);
 lines+=c<<8;

 return(err);
}


static os_error * xxres(int fi)
{
 os_error * err;
 int        c;

 err=fs_getc(fi,&c);
 xres=c;
 err=fs_getc(fi,&c);
 xres+=c<<8;

 return(err);
}


static os_error * xyres(int fi)
{
 os_error * err;
 int        c;

 err=fs_getc(fi,&c);
 yres=c;
 err=fs_getc(fi,&c);
 yres+=c<<8;

 return(err);
}


static os_error * xvarstring(int n,int fi)
{
 os_error * err;
 char       string[256];
 char     * p;
 int        c;

 err=NULL;

 n-='0';

 p=string+sprintf(string,"Set %s ",commandname[n]);

 do
 {
  err=fs_getc(fi,&c);
  if(err) break;
  *p++=c;
 } while(c);

/* dprintf(2,"p=%s",string); */

 if(!err) oscli(string);

 return(err);
}


static os_error * xenddoc(int fo)
{
 os_error * err;
 int        nextoffset;

         err=fs_tell(fo,&nextoffset);
 if(!err) err=fs_seek(fo,sopoffset);
 if(!err) err=faxifd(fo,maxwidth,lines,xres,yres,nextoffset-dataoffset,
                                     TIFFCCITT4,npages,npages);
 if(!err) err=fs_seek(fo,nextoffset);

 return(err);
}


static os_error * processxgraphicsfile(int fi,int fo)
{
 os_error * err;
 int        c;
 int        escape;


 xres=XRES;
 yres=YRESHI;
 maxwidth=1728;
 npages=0;
 maxlines=0;

 xxstart(fo);

 escape=0;

 while(1)
 {
  err=fs_getc(fi,&c);
  if(c==-1) break;
  if(err)   break;

  if(escape)
  {
   switch(c)
   {
    case 's':         /* start of page */
             err=xxstart(fo);
             break;

    case 'E':         /* end of page */
             err=xend(fo);
             break;

    case 'B':         /* buffer of stuff */
             err=xcopy(fi,fo);
             break;

    case 'W':         /* width */
             err=xwidth(fi);
             break;

    case 'L':         /* length */
             err=xlength(fi);
             break;

    case 'X':
             err=xxres(fi);
             break;

    case 'Y':
             err=xyres(fi);
             break;

     default:
             if(c>='0' && c<='9') err=xvarstring(c,fi);
             break;
   }
   escape=0;
  }
  else
  if(c==27) escape=1;
 }

 xenddoc(fo);

 return(err);
}

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

static codestr cx;


#define P1NONE      0
#define P1LINESTART 1
#define P1LINEEND   2
#define P1LINELEN1  3
#define P1PAGE      4




static os_error * startpage(buffer * dest)
{
 os_error * ep;

 lines=0;
 npages++;

 cx.bf=dest;
 cx.width=maxwidth;
 cx.bitrev=0;
 cx.height=-1;
 cx.line=0;
 if(datacompression) cx.type=TIFFCCITT4;  /* FAXCCITT4; */  /* TIFFCCITT4; */
 else                cx.type=FAXCCITT3;

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

        ep=bf_dump(dest);
 if(!ep)ep=fs_tell(dest->fh,&sopoffset);
 if(!ep)ep=faxifd(dest->fh,maxwidth,lines,xres,yres,0,cx.type,npages,npages+1);
 if(!ep)ep=fs_tell(dest->fh,&dataoffset);

 if(!ep)ep=codeinit(&cx);


 return(ep);
}



static os_error * endpage(buffer * dest)
{
 os_error * ep;
 int        nextoffset;

 ep=cx.puteop(&cx);

 codefinit(&cx);

 if(!ep) ep=bf_dump(dest);
 if(!ep) ep=fs_tell(dest->fh,&nextoffset);
 if(!ep) ep=fs_seek(dest->fh,sopoffset);
 if(!ep) ep=faxifd(dest->fh,maxwidth,lines,xres,yres,nextoffset-dataoffset,
                                                      cx.type,npages,npages+1);
 if(!ep) ep=fs_seek(dest->fh,nextoffset);

 if(lines>maxlines) maxlines=lines;


 return(ep);
}


os_error * newline(buffer * dest,int len)
{
 os_error * ep;

 dest=dest;
 len=len;

 ep=NULL;

 lines++;

 return(ep);
}



os_error * endline(buffer * dest)
{
 os_error * ep;


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


 return(ep);

 USE(dest);
}


static os_error * startdoc(buffer * dest)
{
 os_error * ep;
 int        pagewidth;

 ep=NULL;
 dest=dest;

 xres=XRES;
 yres=YRESLO;

 pagewidth=printareawidth();
 if(pagewidth)
 {
  pagewidth*=xres;
  pagewidth/=180;

  if(pagewidth<=1728) maxwidth=1728;
  else
  if(pagewidth<=2048) maxwidth=2048;
  else                maxwidth=2432;

  /* dprintf(0,"maxw=%d",maxwidth); */
 }
 else maxwidth=1728;

 npages=0;
 maxlines=0;

 return(ep);
}




static os_error * enddoc(buffer * dest)
{
 os_error * ep;
 int        nextoffset;

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

 return(ep);
}



static os_error * processtextfile(buffer * dest,buffer * src,int hires)
{
 os_error  * ep;
 Draw_diag   diag;
 int         c;
 int         escape;
 int         bold;
 int         italic;
 int         subs;
 int         sups;
 int         uline;
 int         linespp;
 int         pageopen;
 int         column;
 int         pendstyle;

 escape=0;
 bold=0;
 italic=0;
 subs=0;
 sups=0;
 uline=0;
 linespp=65;
 diag.length=0;
 diag.data=NULL;
 pageopen=0;
 column=0;
 pendstyle=0;

 faxfixmachine(-1);

 ep=startdoc(dest);

/* xres=204; preset by start doc. */
 yres=hires?YRESHI:YRESLO;


 while(!ep)
 {
  ep=bf_get(src,&c);
  if(c==-1) break;
  if(ep)    break;

  if(escape)
  {
   switch(c)
   {
    case '@': /* end of job */
             break;

    case 'T': /* start of NLQ job */
             break;

    case 't': /* start of draft job */
             break;

    case 'C': /* lines per page */
             ep=bf_get(src,&linespp);
             break;

    case 'P': /* Pica */
             break;

    case 'Q': /* Elite */
             break;

    case 'W': /* Condensed */
             break;

    case 'Y': /* Expanded */
             break;

    case 'B': /* Bold on */
             bold=1;
             pendstyle=1;
             break;

    case 'b': /* Bold off */
             bold=0;
             pendstyle=1;
             break;

    case 'I': /* Italic on */
             italic=1;
             pendstyle=1;
             break;

    case 'i': /* Italic off */
             italic=0;
             pendstyle=1;
             break;

    case 'G': /* Light  on */
             break;

    case 'g': /* Light  off */
             break;

    case 'R': /* Superscript  on */
             sups=1;
             break;

    case 'r': /* Superscript  off */
             sups=0;
             break;

    case 'V': /* Subsscript  on */
             subs=1;
             break;

    case 'v': /* Subsscript  off */
             subs=0;
             break;

    case 'U': /* Underline on */
             uline=1;
             pendstyle=1;
             break;

    case 'u': /* Underline off */
             uline=0;
             pendstyle=1;
             break;
   }
   escape=0;
  }
  else
  {
   if(c!=27 && !pageopen)
   {
    ep=starttextpage(&diag,textdumpmode,linespp);
    pageopen=1;
   }

   switch(c)
   {

    case 13:        /* start of line */
            break;



    case 10:        /* new line */
            faxfixmachine(0);
            writetotextcolumn(&diag,"\\\n");
            break;



    case  9:        /* tab */
            break;



    case  8:        /* BS */
            break; 



    case 27:
            escape=1;
            break;



    case 12:        /* form feed == end of page */
            faxfixmachine(0);
            writetotextcolumnc(&diag,'\n');
            endtextcolumn(&diag);
            if(textdumpmode || column==1)
            {
             column=0;
             pageopen=0;

             startpage(dest);
             ep=rasterize(xres,yres,maxwidth,textdumpmode,dest,&diag,&cx);
             endpage(dest);

             cleardiag(&diag);
            }
            else
            {
             startcolumn2(&diag,linespp);
             column++;
            }

            break;


    default:
            if(c>31)
            {
             if(pendstyle)
             {
              if(bold && italic) writetotextcolumn(&diag,"\\3/");
              else
              if(italic)         writetotextcolumn(&diag,"\\2/");
              else
              if(bold)           writetotextcolumn(&diag,"\\1/");
              else               writetotextcolumn(&diag,"\\0/");

              if(!uline)         writetotextcolumn(&diag,"\\U 0 0\n");
              else               writetotextcolumn(&diag,"\\U -8 6\n");

              pendstyle=0;
             }
             if(c=='\\') writetotextcolumnc(&diag,'\\');
             writetotextcolumnc(&diag,c);
             faxfixmachine(c);
             if(c=='\\') writetotextcolumnc(&diag,'/');
            }
            break;

   }
  }
 }

 if(!ep && pageopen)
 {
  ep=writetotextcolumnc(&diag,'\n');
  faxfixmachine(0);
  if(!ep)
  {
   ep=endtextcolumn(&diag);
   if(!ep)
   {
    ep=startpage(dest);
    if(!ep)
    {
     ep=rasterize(xres,yres,maxwidth,textdumpmode,dest,&diag,&cx);
     if(!ep)endpage(dest);
    }
   }
  }
 }

 if(diag.data) cleardiag(&diag);

 if(!ep) ep=enddoc(dest);

 return(ep);
}



os_error * writeblanks(int * blanklines,int maxwidth,buffer * dest,
                                                              codestr * cx)
{
 os_error * err;
 int        i;
 int        n;

 err=NULL;

 n=*blanklines;
 if(n)
 {
  for(i=0;i<n;i++)
  {
   newline(dest,maxwidth);

   cx->runs[cx->run++]=maxwidth;

   endline(dest);
  }

  *blanklines=0;
 }
 return(err);
}






static os_error * processgraphicsfile(buffer * dest,buffer * src)
{
 os_error * ep;
 int        state;
 int        c;
 int        linelen;
 int        escape;
 int        i;
 int        j;
 int        mask;
 int        run;
 int        black;
 int        width;
 char    *  buff;
 int        blanklines;


 ep=startdoc(dest);

 escape=0;
 state=P1NONE;
 blanklines=0;
 linelen=0;  /* compiler */

 while(1)
 {
  ep=bf_get(src,&c);
  if(c==-1) break;
  if(ep)    break;

  if(escape)
  {
   switch(c)
   {
    case 'S':                  /* graphics page prologue */
             break;

    case 'E':                  /* graphics page epilogue */
             break;

    case 'X':                  /* end of line */
    case 'M':                  /* skip line   */
             if(state==P1NONE)
             {
              startpage(dest);
              state=P1PAGE;
             }

             if(state==P1PAGE)
             {
              blanklines+=8;
             }
             state=P1PAGE;
             break;


    case 'L':      /* lo res line */
    case 'H':      /* hi res line */

             if(state==P1NONE)
             {
              startpage(dest);
             }

             if(c=='H')
             {
              xres=XRES;
              yres=YRESHI;
             }
             state=P1LINESTART;
             break;

     default:
             state=P1PAGE;
             break;
   }
   escape=0;
  }
  else
  if(state==P1PAGE || state==P1LINEEND || state==P1NONE)
  {
   switch(c)
   {
    case 27:
            escape=1;
            break;

    case 12:         /* form feed == end of page */
            if(blanklines>8) blanklines=8;
            writeblanks(&blanklines,maxwidth,dest,&cx);

            if(state==P1PAGE || state==P1LINEEND) endpage(dest);
            state=P1NONE;
            break;
   }
  }
  else
  if(state==P1LINESTART)
  {
   linelen=c;
   state=P1LINELEN1;
  }
  else
  if(state==P1LINELEN1)
  {
   writeblanks(&blanklines,maxwidth,dest,&cx);

   linelen+=(c<<8);

   state=P1LINEEND;
/*   dprintf(0,"lineln=%d",linelen); */

   ep=bf_run(src,linelen);
   buff=src->data+src->p;

   for(j=0;j<8;j++)
   {
    newline(dest,linelen);
    mask=1<<(7-j);
    run=0;
    black=0;
    width=0;

    for(i=0;i<linelen;i++)
    {
     if(buff[i] & mask)
     {
      if(black) run++;
      else
      {
       cx.runs[cx.run++]=run;

       width+=run;
       run=1;
       black=1;
      }
     }
     else
     {
      if(!black) run++;
      else
      {
       cx.runs[cx.run++]=run;

       width+=run;
       run=1;
       black=0;
      }
     }
    }
    if(black)
    {
     cx.runs[cx.run++]=run;

     width+=run;
     run=0;
    }

    cx.runs[cx.run++]=maxwidth-width;

    endline(dest);
   }
   src->p+=linelen;
  }
 }

 if(!ep) ep=enddoc(dest);

 return(ep);
}



#define DESTSIZE 0x4000
#define SRCSIZE  0x4000


static os_error * processprinter(char * outfile,char * infile)
{
 os_error * ep;
 int        fo;
 int        fi;
 buffer     src;
 buffer     dest;
 fstat      f;
 int        c;

 stat(infile,&f);

 ep=fs_open(infile,'r',&fi);
 if(!ep)
 {
  ep=fs_open(outfile,'w',&fo);
  if(!ep)
  {
   fs_getc(fi,&c); /* this had better be escape */
   fs_getc(fi,&c); /* this had better be S==graphics T==NLQ or t==draft */

   tiff_hdr(fo);

   if(c=='s')
   {
    arcfaxfixset(FAXFIXCLRV);
    ep=processxgraphicsfile(fi,fo);
   }
   else
   {
    bf_alloc(&dest,DESTSIZE,fo,0);
    bf_alloc(&src,SRCSIZE,fi,f.length-2);

    if(c=='S')
    {
     ep=processgraphicsfile(&dest,&src);
     arcfaxfixset(FAXFIXWRITE);
    }
    else
    {
     arcfaxfixset(FAXFIXCLRV);
     if(c=='T') ep=processtextfile(&dest,&src,1);
     else
     if(c=='t') ep=processtextfile(&dest,&src,0);
    }


    bf_dump(&dest);
    bf_dealloc(&src);
    bf_dealloc(&dest);
   }

   ep=fs_close(fo,ep);

   setftype(outfile,TIFF);

   if(ep) fs_delete(outfile);
  }
  ep=fs_close(fi,ep);
 }
 return(ep);
}


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

int * pollword;


static int linkdelay;
static int linktime;


void linkpoll(int fp)
{
 linkdelay=stack[fp];

 if(pollword)
 {
  if(linkdelay) addzeroevent(LINKDEMON);
  else          remzeroevent(LINKDEMON);
 }
}



void linkzero(void)
{
 os_error * ep;
 char       string[256];
 fstat      f;
 int        bn;
 char       stringf[256];
 char       stringfp[256];

 /* use statx to do the job of fexists() and stat() in one go */

/* dprintf(0,"pw=%d address=%x",*pollword,pollword); */

 if(zerotime>linktime || !linkdelay)
 {
  linktime=zerotime+linkdelay;

  sprintf(stringf,"%s.Fax",path(AFXTMP));
  sprintf(stringfp,"%s.FaxP",path(AFXTMP));

  ep=statx(stringf,&f); 
  if(!ep)
  {
   if(f.object==1 && f.length)
   {
    if(!fs_rename(stringf,stringfp))
    {
     genbatchname(TXBFILE,string,TIFF);
     hourglasson();
     ep=processprinter(string,stringfp);
     hourglassoff();
     fs_delete(stringfp);
     if(!ep)
     {
      bn=addtotxbatch(TIFF,string,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=maxlines;
      vtable[TXBFILE][bn].tsum.npages=npages;
      newfax(bn);    /* bn can change - rename */
      saveconfig();
     }
     else report(ep);
    }
   }
   else arcfaxfixset(FAXFIXRESET);
  }
 }
}




void linkmessage(void)
{
/* dprintf(0,"link message"); */

 linkzero();

 *pollword=0;
}


void linkboot(void)
{
 os_regset  rx;
 os_error * err;
 int      * p;
 char       string[256];

 sprintf(string,"%s.FaxP",path(AFXTMP));

 fs_delete(string);

 pollword=NULL;

 if(os3())
 {
  rx.r[0]=18;
  rx.r[1]=(int)"ArcFaxFix";

  err=os_swix(OS_Module,&rx);
  if(!err)
  {
   /* r3->module code */

   p=(int*)rx.r[3];
   pollword=(int*)((char*)p+(*(p+17)));

   *pollword=1;   /* fiddle one pass */

   return;
  }
 }

 addzeroevent(LINKDEMON);
}

