#define PROGNAME "dot"
#include <stdio.h>

#define EOS '\0'
#define  TABLEWIDTH  256  /* conversion map */
short conv[TABLEWIDTH];
char mapfilename[31];
#define  MAPFILE  "map.dat"
char mflag=0; /* set if mapfile is to be consulted */

readmap()
{
  FILE *fp=NULL;
  register short *p,i;

  if(!mflag)  {
    linmap();
    return(0);
  }
  if((fp = fopen(mapfilename,"r")) == NULL)
     error("can't read map file");

  fprintf(stderr,"reading map file");

  for (i=0,p=conv; i<TABLEWIDTH; i++)
    *p++ = getc(fp)&0377;
  fclose(fp);

  fprintf(stderr,"\n");

}

linmap()
{
  register short i;
  register short *convp;
  
  for (i=0,convp=conv; i<TABLEWIDTH; i++)
    *convp++ = i;
}

showmap()
{
  register short i;
  register short *convp;

  for (i=0,convp=conv; i<TABLEWIDTH; i++)
    fprintf(stderr,"conv[%3d] = %3d\n",i,*convp++);
}

invertmap()
{
  register short i;
  register short *convp;

  for (i=0,convp=conv; i<TABLEWIDTH; i++,convp++)
      *convp = 256 - *convp;
}

#define MAXBUF 2000
short buf[MAXBUF];
short bufsize;
short scale=1;       /* blow up scale */


#define MAXD 16
#define MAXDSQR 256
char dither[MAXDSQR];
static char hex[16+1] = "0123456789ABCDEF";


/* The two char arrays below are combined to form the dither matrix.  */
/* 16 submatrices similar to "dithfont" are combined as a 4 by 4 set */
/* To produce a 16 by 16 matrix. Each submatrix in the matrix has a */
/* positional correspondence with an element in the matrix "random". */
/* For each submatrix every element it contains is incremented by */
/* the value held in the "corresponding" element of the matrix "random" */

char random[4][4] = {   /* From Foley & Van Dam */
  { 0, 8, 2,10},
  {12, 4,14, 6},
  { 3,11, 1, 9},
  {15, 7,13, 5}
};

char dithfont[4][4] = {  /* designed to clump dots */
  { 0, 1, 4, 9},
  { 2, 3, 5,10},
  { 6, 7, 8,11},
  {12,13,14,15}
};

/* DITHER MATRIX PRODUCTION */
make_dith()             /* writen for clarity rather than speed */
{
char r,c,row,col;
  for(r=0;r<4;r++)
    for(c=0;c<4;c++)
      for(row=0;row<4;row++)
        for(col=0;col<4;col++)
          dither[4*(c+r*MAXD)+col+row*MAXD] = 16*dithfont[row][col]+random[r][c];
}

#define NEWLINE putchar('\n')

show_dither()
{
short i,j;
char *dp;
  
  for(j=0,dp=dither;j<MAXD;j++){
    for(i=0;i<MAXD;i++,dp++)
        fprintf(stderr,"%3d ",(*dp)&0377);
    NEWLINE ;
  }
}


#define COUNTFILENAME "pub:count"
char outfilename[31];

char dflag=0;  /* display dither matrix option */
char lflag=0;  /* direct output to laserprinter dir */
char screen_flag = 0; /* output meant for doc:lg1 program display */
short header=1;/* set if pic file contains a header char */
short xsize;   /* pic file size                          */
FILE *fp = NULL; /* pointer to pic file */
FILE *fq = NULL; /* pointer to output file */


main(argc,argv)
char **argv;
{

  argv++;

  while((argc>0)&&(*argv[0] == '-'))
  {
    switch((*argv)[1]) {
    case 'x':
      sscanf(*argv+2,"%hd",&xsize);
      header=(!header);
      break;
    case 's':
      sscanf(*argv+2,"%hd",&scale);
      if (scale<1) error("negative scaling factor");
      break;
    case 'd':     /*  d for debug or display */
      dflag=1;
      break;
    case 'm':
      sscanf(*argv+2,"%s",mapfilename);
      if(mapfilename[0]==EOS) {
         strcpy(mapfilename,MAPFILE);
         fprintf(stderr,"using %s as default\n",MAPFILE);
      }
      mflag=1;
      break;
    case 'i':             /* i for inverse */
      screen_flag=1;
      break;
    case 'l':
      lflag=1;
      break;
    default:
      error("bad option");
      break;
    }
    argc--;
    argv++;
  }


  if (!((2<=argc)&&(argc<=3)))
    error("arg count");

  readmap();      /* placed here to avoid using fp twice */
  if(!screen_flag) invertmap();
  if(dflag)  showmap();

  if((fp=fopen(*argv,"r"))==NULL)
    error("can't open picture file");

  argc--;
  argv++;
  if(argc==2) {
    if(lflag) error("no output filename if sending direct to laser printer");
    strcpy(outfilename,*argv);
  }else
    sprintf(outfilename,"%sF%02dP%02d",
           (lflag?("lp1:"):("")),file_count(),getpid());

  if(lflag&&screen_flag)
    fprintf(stderr,
       "warning : you are sending screen inverted output to the printer\n");
  make_dith();
  if(dflag) show_dither();
  display();
  report();
  fclose(fp);
  fclose(fq);
  putchar(7);       /* BEEP */
}
  
getbuf(bufp)
short *bufp;
{
short i,s,c;
    for(i=0,bufp=buf;i<xsize;i++) {
      c = conv[getc(fp)&0377];
      for(s=0;s<scale;s++)
        *bufp++ = c;
    }
}

error(s)
char *s;
{
  fprintf(stderr,"%s: %s\n",PROGNAME,s);
  exit(0);
}

#define PATTERNSIZE 8

dot_line(dp,bufp)
char *dp;
short *bufp;
{
  char *d1p;
  register short i,k,d_count;
  register short dot_pattern;

  d1p=dp;
  for (i=0,d_count=0; i<bufsize; ) {
    for (k=0,dot_pattern=0;k<PATTERNSIZE; k++) {
      dot_pattern<<=1;
      if(i<bufsize){ i++;
                   dot_pattern |= ((*bufp++) >= ((*dp)&0377));
                   if(d_count<MAXD-1) {d_count++;dp++;}
                   else{d_count=0;dp=d1p;}
      }
    }
    putc(hex[(dot_pattern&017)],fq);
    putc(hex[(dot_pattern>>4)],fq);
  }
  putc(' ',fq);
}

display()
{
register short j,d_count,s;
register char *dp;
  if (header)
    xsize=(getc(fp))&0377; /*good for xsize upto 256 only*/

  if (xsize<1) error("negative picture size");
  bufsize=scale*xsize;
  if (bufsize>MAXBUF)
    error("picture too wide at this scale");

  if(bufsize>256)  /* APM extent problem solved  */
    sprintf(outfilename,"%s,%-x",outfilename, ((bufsize*bufsize)>>11));

  if((fq=fopen(outfilename,"w"))==NULL)
     error("can't open output file");

  send_header();
  for (j=0,dp=dither,d_count=0; j<xsize; j++) {
    getbuf(buf);
    for(s=0;s<scale;s++) {
      dot_line(dp,buf);
      if(d_count<MAXD-1) {d_count++;dp+=MAXD;}
      else{d_count=0;dp=dither;}
    }
  }
  putc('\n',fq); /* end of last line */

}


send_header() /* GRAPHICAL BITMAP REPRESENTATION */
{
  fprintf(fq,"\033[ 0;%d;0;%d;0G\n",bufsize,bufsize);
}

file_count()
{
FILE *fq = NULL;
short count=0;
    if((fq=fopen(COUNTFILENAME,"r"))==NULL) {
        fprintf(stderr,"%s created.\n",COUNTFILENAME);
        fprintf(stderr,"Count initialised in %s.\n",COUNTFILENAME);
    }
    else {
        fscanf(fq,"%hd",&count);
        fclose(fq); fq=NULL;
        if (count==0){
          fprintf(stderr,"%s has been corrupted.\n",COUNTFILENAME);
          fprintf(stderr,"Count initialised in %s.\n",COUNTFILENAME);
        }
    }
    if((fq=fopen(COUNTFILENAME,"w"))==NULL)
       error("can't write count file");
    fprintf(fq,"%d",count+1);
    fclose(fq);
  return(count);
}

report()
{
  fprintf(stderr,"Your options were : \n");
  if(screen_flag)    fprintf(stderr,
                 "inversed dot pattern for screen viewing.\n");
  if(lflag) fprintf(stderr,"output sent to laser printer directory\n");
  if(mflag) fprintf(stderr,"%s used to scale grey levels\n",mapfilename);
  fprintf(stderr,"  picture file  size = %d.\n",xsize);
  fprintf(stderr,"  scaling factor = %d.\n",scale);
  fprintf(stderr,"Output file is called %s.\n",outfilename);
}
