/*->c.main */

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


#include "kernel.h"
#include "swis.h"


#include "h.os"

#include "h.RMInfo"

#include "h.etc"

#include "h.buffer"
#include "h.code"
#include "h.g4"
#include "h.fsx"

#include "h.main"


#define ESC 27



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

static _kernel_swi_regs rx;

os_error * fs_write(int fh,void * b,int n)
{
 os_error * err;

 if(!n) return(NULL);

 rx.r[0]=2;
 rx.r[1]=fh;
 rx.r[2]=(int)b;
 rx.r[3]=n;

 err=(os_error*)_kernel_swi(OS_GBPB,&rx,&rx);

 return(err);
}

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

#define MAXCOMMANDS 7


#define FAXFIXCLRV  0
#define FAXFIXWRITE 1
#define FAXFIXRESET 2



static char * varray[MAXCOMMANDS]=
{
 "ArcFax$FaxTo",
 "ArcFax$FaxAfter",
 "ArcFax$FaxName",
 "ArcFax$FaxBand",
 "ArcFax$FaxPrefix",
 "ArcFax$FaxPriority",
 "ArcFax$FaxMerge",
};


static void arcfaxfixclr(void)
{
 int               i;
 _kernel_swi_regs  rx;

 for(i=0;i<MAXCOMMANDS;i++)
 {
  rx.r[0]=(int)varray[i];
  rx.r[1]=rx.r[0];
  rx.r[2]=-1;
  rx.r[3]=0;
  rx.r[4]=0;

  _kernel_swi(OS_SetVarVal,&rx,&rx);
 }
}


static void arcfaxfixset(int set)
{
 switch(set)
 {
  case FAXFIXCLRV:
         arcfaxfixclr();
         break;

  case FAXFIXWRITE:
         arcfaxfixclr();
         _kernel_oscli("ArcFaxFix_Write");
         break;

  case FAXFIXRESET:
         _kernel_oscli("ArcFaxFix_Clear");
         break;
 }
}


static void writefixstrings(int fh)
{
 char * p;
 int    c;
 int    esc; 
 int    i;

 c='0';
 esc=ESC;

 arcfaxfixset(FAXFIXWRITE);

 for(i=0;i<MAXCOMMANDS;i++)
 {
  if((p=getenv(varray[i]))!=NULL)
  {
   fs_write(fh,&esc,1);
   fs_write(fh,&c,1);
   do
   {
    fs_write(fh,p,1);
   } while(*p++);
  }
  c++;
 }

 arcfaxfixset(FAXFIXCLRV);
}


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

static codestr cx;
static buffer  bf;

static int     blanklines;
static int     xres;
static int     yres;
static int     leftmargin;


static os_error * writeblanks(void)
{
 os_error * err;

 err=NULL;

 if(blanklines)
 {
  while(blanklines--)
  {
   err=codeblank(&cx);
   if(err) break;
  }
 }

 blanklines=0;

 return(err);
}


static int isblankline(char * p,int width,int bpp)
{
 int   words;
 int   bytes;
 int   bits;
 int * q;
 int   c;

 bits=width*bpp;
 words=bits/32;
 bytes=bits/8;
 bits-=bytes*8;
 bytes-=words*4;

 q=(int*)p;

 while(words--)
 {
  if(*q++) return(0);
 }

 p=(char*)q;

 while(bytes--)
 {
  if(*p++) return(0);
 }

 c=*p;

 while(bits--)
 {
  if(c & 0x1) return(0);
  c=c>>1;
 }

 return(1);
}



static os_error * link_line(char * p,int width,int bpp)
{
 os_error * err;
 int        word;
 int        i;
 int        shift;
 int        mask;
 int        sbyte;


 err=NULL;

 if(isblankline(p,width,bpp)) blanklines++;
 else
 {
  if(blanklines) writeblanks();

  codebitstart(&cx,leftmargin);

  word=0;   /* compiler */
  shift=32;

  if(bpp==1) mask=0x1;
  else
  if(bpp==2) mask=0x3;
  else
  if(bpp==4) mask=0xF;
  else       mask=0xFF;


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

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

    codebit(&cx,sbyte);
   }
  }
  else   /* bpp==8 */
  {
   for(i=0;i<width;i++)
   {
    if(shift==32)
    {
     word=*((int*)p);
     p+=sizeof(int);
     shift=0;
    }

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

    codebit(&cx,sbyte>128);
   }
  }


  codebitend(&cx);

  err=cx.putline(&cx);
 }

 return(err);
}



/* coding index goes several pixels past max line width */


#define XWIDTH   (1728+8)       
#define ZWIDTH   (1728)



#define BUFFSIZE 0x4000


#define XSIZE (sizeof(short)*2*XWIDTH+BUFFSIZE)


#define MYTAG 0x707

static char * mychunk;     /* XSIZE */


/* free memory */

static os_error * link_dealloc(int anchor)
{
 os_error * err;

 rx.r[0]=anchor;
 rx.r[2]=MYTAG;

 err=(os_error*)_kernel_swi(PDumper_Find,&rx,&rx);

 rx.r[0]=anchor;

 err=(os_error*)_kernel_swi(PDumper_Free,&rx,&rx);

 return(err);
}


static os_error * link_setup(int anchor)
{
 os_error * err;

 rx.r[0]=anchor;
 rx.r[2]=MYTAG;

 err=(os_error*)_kernel_swi(PDumper_Find,&rx,&rx);

 mychunk=(char*)rx.r[2];

 bf.data=mychunk;

 even_runs=(short*)(bf.data+BUFFSIZE);
 odd_runs=(short*)(((char*)even_runs)+sizeof(short)*XWIDTH);

 g4j_linecheck();

 return(err);
}


/* claim the memory */

/* in general width can be less than XWIDTH (1728) */

static os_error * link_alloc(int width,int anchor)
{
 os_error * err;

 rx.r[0]=anchor;
 rx.r[3]=XSIZE;
 rx.r[4]=MYTAG;

 err=(os_error*)_kernel_swi(PDumper_Claim,&rx,&rx);
 if(!err) err=link_setup(anchor);

 bf_alloc(&bf,BUFFSIZE,0,0);

 return(err);
 width=width;
}


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


#define DUMPER 8


#define Service_PDumperStarting 0x66
#define Service_PDumperDying    0x67


#define PDriver_MiscOp 0x8015A



static char palette[256];



static _kernel_oserror * setdriver(_kernel_swi_regs *r)
{
 /* r3->palette name */

 strncpy(palette,(char*)r->r[3],255);

 USE(r);
 return(NULL);
}



static _kernel_oserror * dumpstrip(_kernel_swi_regs *r)
{
 _kernel_swi_regs  rx;
 char            * p;
 int               i;

 link_setup(r->r[8]);


 if(r->r[2] & 0x1)  /* GS 8bpp */
 {
  rx.r[0]=r->r[8];  /* anchor word */
  rx.r[1]=r->r[0];  /* data */
  
  if((r->r[6] & 0xFF)>1) rx.r[2]=0;        /* copied from LJ */
  else                   rx.r[2]=1;        /* output format  */

  rx.r[3]=r->r[3];  /* width in pixels */
  rx.r[4]=r->r[4];  /* height */
  rx.r[5]=r->r[5];  /* width in bytes  */
  rx.r[6]=r->r[6];  /* halftone info */

  _kernel_swi(PDumper_PrepareStrip,&rx,&rx);

  p=(char*)r->r[0];

  for(i=0;i<r->r[4];i++)
  {
   link_line(p,r->r[3],8);

   p+=r->r[5];
  }
 }
 else
 {                   /* 1bpp */
  p=(char*)r->r[0];

  for(i=0;i<r->r[4];i++)
  {
   link_line(p,r->r[3],1);

   p+=r->r[5];
  }
 }

 return(NULL);
}



static _kernel_oserror * matchcolour(_kernel_swi_regs * r)
{
 _kernel_swi_regs  rx;
 _kernel_oserror * err;


 rx.r[0]=r->r[3];
 rx.r[1]=r->r[0];
 rx.r[2]=r->r[2];
 rx.r[4]=r->r[4];


 err=_kernel_swi(PDumper_SetColour,&rx,&rx);

 r->r[3]=rx.r[3];

 return(err);
}



static char escstart[2]={ESC,'s'};


static _kernel_oserror * startpage(_kernel_swi_regs *r)
{

 link_setup(r->r[4]);

 fs_write(r->r[1],escstart,2);

 blanklines=0;

 cx.bf=&bf;
 cx.width=ZWIDTH;
 cx.height=-1;
 cx.line=0;

 cx.putbyte=bf_put;

 bf.fh=r->r[1];

 codeinit(&cx);

 xres=(r->r[7] & 0xFFFF);
 yres=(r->r[7]>>16) & 0xFFFF;
 leftmargin=r->r[6];

 arcfaxfixset(FAXFIXRESET);

 return(NULL);
}



static char escend[2]  ={ESC,'E'};
static char esclines[4]={ESC,'L',0,0};
static char escwidth[4]={ESC,'W',0,0};
static char escxres[4] ={ESC,'X',0,0};
static char escyres[4] ={ESC,'Y',0,0};



static _kernel_oserror * endpage(_kernel_swi_regs *r)
{

 link_setup(r->r[3]);

 bf.fh=r->r[1];

 if(blanklines>8) blanklines=8;
 writeblanks();

 cx.puteop(&cx);
 codefinit(&cx);
 bf_dump(&bf);

 esclines[2]=(cx.line & 0xFF);
 esclines[3]=(cx.line>>8);
 fs_write(r->r[1],esclines,4);

 escwidth[2]=(cx.width & 0xFF);
 escwidth[3]=(cx.width>>8);
 fs_write(r->r[1],escwidth,4);

 escxres[2]=(xres & 0xFF);
 escxres[3]=(xres >> 8);
 fs_write(r->r[1],escxres,4);

 escyres[2]=(yres & 0xFF);
 escyres[3]=(yres >> 8);
 fs_write(r->r[1],escyres,4);

 writefixstrings(r->r[1]);

 fs_write(r->r[1],escend,2);

 return(NULL);
 USE(r);
}


static _kernel_oserror * startjob(_kernel_swi_regs *r)
{
 _kernel_oserror * err;

 rx.r[0]=r->r[0]; /* (int)&anchor; */
 rx.r[1]=0;
 rx.r[2]=(int)palette;

 err=_kernel_swi(PDumper_StartJob,&rx,&rx);
 if(!err) err=(_kernel_oserror *)link_alloc(ZWIDTH,r->r[0]);

 return(err);
}


static _kernel_oserror * abortjob(_kernel_swi_regs *r)
{
 _kernel_swi_regs  rx;
 _kernel_oserror * err;

 err=NULL;

 if(r->r[3])  /* r3!=0 means end of doc */
 {
  link_dealloc(r->r[0]);

  rx.r[0]=r->r[0];
  rx.r[1]=0;
  rx.r[2]=r->r[3];

  err=_kernel_swi(PDumper_TidyJob,&rx,&rx);
 }

 return(err);
}




static _kernel_oserror * dumpinit(void)
{



 return(NULL);
}



static _kernel_oserror * dumpfinit(void)
{
 return(NULL);
}




_kernel_oserror * dumper_entry(_kernel_swi_regs *r)
{
 _kernel_oserror * err;

 err=NULL;

 switch(r->r[11])
 {
  case 0:
         err=setdriver(r);
         break;

  case 1:
         err=dumpstrip(r);
         break;

  case 2:
         err=matchcolour(r);
         break;

  case 3:
         err=startpage(r);
         break;

  case 4:
         err=endpage(r);
         break;

  case 5:
         err=startjob(r);
         break;

  case 6:
         err=abortjob(r);
         break;
 }

 return(err);
}


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

 /*
   Close your filing system down here and free up all your memory NOW.
   If you do not then all your filing system's junk is going to be left
   cluttering up the RMA as this module is just about to be killed.
 */


static void Dumper_finalise(void)
{
 _kernel_swi_regs r;

 dumpfinit();

 r.r[0]=0x80000001;
 r.r[1]=DUMPER;

 _kernel_swi(XOS_Bit|PDriver_MiscOp,&r,&r); 
}




static _kernel_oserror * declare_Dumper(void *private_word)
{
 _kernel_swi_regs r;

 r.r[0]=0x80000000;
 r.r[1]=DUMPER;
 r.r[2]=300;
 r.r[3]=(int)private_word;
 r.r[4]=(int)veneer_dumper_entry;
 r.r[5]=0xFF;  /* reason codes 0..7 */
 r.r[6]=0x7;   /* strip types 0..2 */

 return(_kernel_swi(XOS_Bit|PDriver_MiscOp,&r,&r));
}



/* initialisation code */

_kernel_oserror *PDumperAF_initialise(char *cmd_tail,int podule_base,
                                                   void *private_word)
{
 _kernel_oserror *err;

 /* These keep the compiler quiet. */

 cmd_tail=cmd_tail;
 podule_base=podule_base;

 /*
   Place your filing system's initialisation here. If an error occurs then
   you can skip the 'declaring yourself to FileSwitch' bit.
  */

 err=dumpinit();
 if(!err)
 {
  declare_Dumper(private_word);   /* may give an error */

  if(!err)
  {
   atexit(Dumper_finalise);
  }
 }

 return(err);
}


/* service call handler */

void PDumperAF_service(int service_number,_kernel_swi_regs *r,
                                                  void *private_word)
{
 r=r;

 switch(service_number)
 {

  case Service_PDumperStarting:
                               declare_Dumper(private_word);
                               break;


  case    Service_PDumperDying:    /* now deregisted */

                               break;

 }


 USE(private_word);
}



