/*
** printed circuit board printer, Copyright (C) Randy Nevin 1989.
**
** you may give this software to anyone, make as many copies as you like, and
** post it on public computer bulletin boards and file servers. you may not
** sell it or charge any fee for distribution (except for media and postage),
** remove this comment or the copyright notice from the code, or claim that
** you wrote this code or anything derived from it. you may modify the code as
** much as you want (please document clearly with comments, and maintain the
** coding style), but programs which are derived from this one are subject to
** the conditions stated here. i am providing this code so that people can
** learn from it, so if you distribute it, please include source code, not
** just executables. contact me to report bugs or suggest enhancements; i do
** not guarantee support, but i will make an effort in good faith to help you,
** and i want to act as a central clearing house for future versions. you
** should contact me before undertaking a significant development effort, to
** avoid reinventing the wheel. if you come up with an enhancement you
** consider particularly useful, i would appreciate being informed so that it
** can be incorporated in future versions. my address is: Randy Nevin,
** 1731 211th PL NE, Redmond, WA 98053. this code is available directly from
** the author; just send a floppy and a self-addressed floppy mailer with
** sufficient postage.
**
** HISTORY
** (name		date		description)
** ----------------------------------------------------
** randy nevin		3/4/89		initial version
** randy nevin		3/4/89		released version 1.00
*/

#include <stdio.h>

#ifndef M_XENIX
#include <stdlib.h>
#endif

#include <string.h>
#include "cell.h"

/*
** usage: PCBPRINT [/P] [/L] [/Rn] [/Zm] infile
**
** /P indicates portrait orientation (paper is longer from top to bottom), and
** /L indicates landscape orientation (paper is longer side-to-side). only one
** of /P and /L can be present. n is the laser printer resolution (75, 100,
** 150, or 300) and m is the zoom factor (0, 1, 2, or 3 ). the defaults are:
** /P /R150 /Z1. this program creates 6 output files (H, T, B, HT, HB, and
** HTB). each of these files can be fed to a hewlett-packard laser printer to
** produce a picture of part or all of the routed printed circuit board. H
** contains commands to print only the holes and routing holes. T and B
** contain commands to print only the top-side or bottom-side traces,
** respectively. HT and HB are the same, but also include the holes, and HTB
** contains everything (holes, top-side traces, and bottom-side traces).
*/

#define MAXZOOM	3	/* maximum zoom number; minimum is 0 */

#define ZOOM0	3	/* 3x3 dots per cell	*/
#define ZOOM1	6	/* 6x6 dots per cell	*/
#define ZOOM2	10	/* 10x10 dots per cell	*/
#define ZOOM3	18	/* 18x18 dots per cell	*/

static int size[MAXZOOM+1] = { ZOOM0, ZOOM1, ZOOM2, ZOOM3 };

#define H	1	/* holes				*/
#define T	2	/* top-side traces			*/
#define B	4	/* bottom-side traces			*/
#define HT	(H+T)	/* holes and top-side traces		*/
#define HB	(H+B)	/* holes and bottom-side traces		*/
#define HTB	(H+T+B)	/* holes, top- and bottom-side traces	*/

static int currnt;	/* current image type (one of the six above)	*/
static int orient;	/* 0=portrait, 1=landscape			*/
static int resol;	/* resolution (one of 75,100,150,300)		*/
static int zoom;	/* 0=3x3, 1=6x6, 2=10x10, 3=18x18		*/
static int nbytes;	/* number of bytes per image row		*/

int justboard = 1; /* only need the board data structure */

/* board dimensions */
extern int Nrows;
extern int Ncols;

extern void InitBoard();
extern long GetCell();
extern void SetCell();

void main();
static void dofile();
static void prolog();
static void epilog();
static void doimage();
static void map();
static void initbit();
static void flushbit();
static void outbit();

void main ( argc, argv ) /* input routed board, output laser printer files */
int argc;
char *argv[];
{
	char *self, *p;
	register int r, c;
	int i1, i2, i3, i4, i, j;
	FILE *fp;
	long x;
	char oset, rset, zset;

	printf( "Copyright (C) Randy Nevin, 1989. Version 1.00\n" );
	printf( "See source code for rights granted.\n\n" );
	orient = 0; /* portrait mode */
	resol = 150; /* 150 dots per inch */
	zoom = 1; /* 6x6 cells */
	oset = rset = zset = 0; /* so far, just defaults */
	self = argv[0];
	/* get rid of initial part of path */
	if ((p = strrchr( self, '\\' )) || (p = strrchr( self, ':' )))
		self = ++p;
	if (argc < 2 || argc > 5) { /* need infile and up to 4 switches */
		fprintf( stderr, "usage: %s [/P] [/L] [/Rn] [/Zm] infile\n",
			self );
		fprintf( stderr, " P = portrait orientation\n" );
		fprintf( stderr, " L = landscape orientation\n" );
		fprintf( stderr, " n = resolution (75,100,150,300)\n" );
		fprintf( stderr, " m = zoom (0,1,2,3)\n" );
		exit( -1 );
		}
	for (i = 1; i < argc-1; i++) { /* process switches */
		if (*argv[i] == '/' && (*(argv[i]+1) == 'P'
			|| *(argv[i]+1) == 'p') && *(argv[i]+2) == 0) {
			if (oset)
				fprintf( stderr, "duplicate orientation %s\n",
					argv[i] );
			else {
				oset = 1;
				orient = 0;
				}
			}
		else if (*argv[i] == '/' && (*(argv[i]+1) == 'L'
			|| *(argv[i]+1) == 'l') && *(argv[i]+2) == 0) {
			if (oset)
				fprintf( stderr, "duplicate orientation %s\n",
					argv[i] );
			else {
				oset = 1;
				orient = 1;
				}
			}
		else if (*argv[i] == '/' && (*(argv[i]+1) == 'R'
			|| *(argv[i]+1) == 'r')) {
			if (rset)
				fprintf( stderr, "duplicate resolution %s\n",
					argv[i] );
			else {
				rset = 1;
				resol = atoi( argv[i]+2 );
				}
			}
		else if (*argv[i] == '/' && (*(argv[i]+1) == 'Z'
			|| *(argv[i]+1) == 'z')) {
			if (zset)
				fprintf( stderr, "duplicate zoom %s\n",
					argv[i] );
			else {
				zset = 1;
				zoom = atoi( argv[i]+2 );
				}
			}
		else
			fprintf( stderr, "unknown option %s\n", argv[i] );
		}
	if (!(fp = fopen( argv[argc-1], "rb" ))) {
		fprintf( stderr, "can't open %s\n", argv[argc-1] );
		exit( -1 );
		}
	/* fetch the board dimensions */
	if ((i = getc( fp )) == EOF || (j = getc( fp )) == EOF) {
		fprintf( stderr, "premature eof\n" );
		exit( -1 );
		}
	Nrows = (i & 0xFF) | ((j << 8) & 0xFF00);
	if ((i = getc( fp )) == EOF || (j = getc( fp )) == EOF) {
		fprintf( stderr, "premature eof\n" );
		exit( -1 );
		}
	Ncols = (i & 0xFF) | ((j << 8) & 0xFF00);
	InitBoard(); /* allocate memory for data structures */
	for (r = 0; r < Nrows; r++) { /* read in the board, row by column */
		for (c = 0; c < Ncols; c++) {
			/* first do top-side */
			if ((i1 = getc( fp )) == EOF
				|| (i2 = getc( fp )) == EOF
				|| (i3 = getc( fp )) == EOF
				|| (i4 = getc( fp )) == EOF) {
				fprintf( stderr, "premature eof\n" );
				exit( -1 );
				}
			x = (long)i1 | (((long)i2) << 8)
				| (((long)i3) << 16) | (((long)i4) << 24);
			SetCell( r, c, TOP, x );
			/* then do bottom-side */
			if ((i1 = getc( fp )) == EOF
				|| (i2 = getc( fp )) == EOF
				|| (i3 = getc( fp )) == EOF
				|| (i4 = getc( fp )) == EOF) {
				fprintf( stderr, "premature eof\n" );
				exit( -1 );
				}
			x = (long)i1 | (((long)i2) << 8)
				| (((long)i3) << 16) | (((long)i4) << 24);
			SetCell( r, c, BOTTOM, x );
			}
		}
	nbytes = (Ncols * size[zoom] + 7) / 8;
	dofile( "H",   H   );
	dofile( "T",   T   );
	dofile( "B",   B   );
	dofile( "HT",  HT  );
	dofile( "HB",  HB  );
	dofile( "HTB", HTB );
	exit( 0 );
	}

static void dofile ( p, i ) /* create a board image file */
	char *p;
	int i;
	{
	FILE *fp;

	printf( "producing %s image file\n", p );
	currnt = i;
	if (!(fp = fopen( p, "wb" ))) {
		fprintf( stderr, "can't open %s\n", p );
		exit( -1 );
		}
	prolog( fp ); /* initial laser printer commands */	
	doimage( fp ); /* create the board image */
	epilog( fp ); /* final laser printer commands */
	if (fclose( fp )) {
		fprintf( stderr, "can't close %s\n", p );
		exit( -1 );
		}
	}

static void prolog ( fp ) /* output initial laser printer commands */
	register FILE *fp;
	{
	putc( 0x1B, fp );		/* <esc> */
	putc( 'E', fp );		/* reset */
	putc( 0x1B, fp );		/* <esc> */
	fprintf( fp, "&l%dO", orient );	/* set image orientation */
	putc( 0x1B, fp );		/* <esc> */
	fprintf( fp, "&a10R" );		/* cursor to row 10 */
	putc( 0x1B, fp );		/* <esc> */
	fprintf( fp, "&a10C" );		/* cursor to column 10 */
	putc( 0x1B, fp );		/* <esc> */
	fprintf( fp, "*t%dR", resol );	/* set resolution in dots per inch */
	putc( 0x1B, fp );		/* <esc> */
	fprintf( fp, "*r1A" );		/* start graphics at cursor */
	}

static void epilog ( fp ) /* output final laser printer commands */
	register FILE *fp;
	{
	putc( 0x1B, fp );	/* <esc> */
	fprintf( fp, "*rB" );	/* end graphics */
	putc( 0x12, fp );	/* formfeed to eject paper */
	putc( 0x1B, fp );	/* <esc> */
	putc( 'E', fp );	/* reset */
	}

static void doimage ( fp ) /* create the board image, row by column */
	FILE *fp;
	{
	register int r, c;
	int ir;
	long x, y;

	for (r = Nrows-1; r >= 0; r--) { /* each row */
		for (ir = size[zoom]-1; ir >= 0; ir--) { /* each scan line */
			putc( 0x1B, fp );	/* <esc> */
			fprintf( fp, "*b%dW", nbytes );
			initbit();
			for (c = 0; c < Ncols; c++) {
				x = GetCell( r, c, TOP );
				y = GetCell( r, c, BOTTOM );
				map( x, y, ir, fp );
				}
			flushbit( fp );
			}
		}
	}

struct x { /* group the bit templates for an object */
	long t;			/* the object type	*/
	char t0[ZOOM0][ZOOM0];	/* tiny zoom template	*/
	char t1[ZOOM1][ZOOM1];	/* small zoom template	*/
	char t2[ZOOM2][ZOOM2];	/* medium zoom template	*/
	char t3[ZOOM3][ZOOM3];	/* large zoom template	*/
	};

extern struct x y1[];  /* hole templates		*/
extern struct x y2[];  /* hole-related templates	*/
extern struct x y3[];  /* non-hole-related templates	*/

extern int z1;  /* number of hole types			*/
extern int z2;  /* number of hole-related types		*/
extern int z3;  /* number of non-hole-related types	*/

#define domap1(v)	{ int i; \
			  for (i = 0; i < z1; i++) { \
				if (v & (y1[i].t)) { \
					if (zoom == 0) \
						bit |= y1[i].t0[ir][ic]; \
					else if (zoom == 1) \
						bit |= y1[i].t1[ir][ic]; \
					else if (zoom == 2) \
						bit |= y1[i].t2[ir][ic]; \
					else if (zoom == 3) \
						bit |= y1[i].t3[ir][ic]; \
					} \
				if (bit) \
					break; \
				} }

#define domap2(v)	{ int i; \
			  for (i = 0; i < z2; i++) { \
				if (v & (y2[i].t)) { \
					if (zoom == 0) \
						bit |= y2[i].t0[ir][ic]; \
					else if (zoom == 1) \
						bit |= y2[i].t1[ir][ic]; \
					else if (zoom == 2) \
						bit |= y2[i].t2[ir][ic]; \
					else if (zoom == 3) \
						bit |= y2[i].t3[ir][ic]; \
					} \
				if (bit) \
					break; \
				} }

#define domap3(v)	{ int i; \
			  for (i = 0; i < z3; i++) { \
				if (v & (y3[i].t)) { \
					if (zoom == 0) \
						bit |= y3[i].t0[ir][ic]; \
					else if (zoom == 1) \
						bit |= y3[i].t1[ir][ic]; \
					else if (zoom == 2) \
						bit |= y3[i].t2[ir][ic]; \
					else if (zoom == 3) \
						bit |= y3[i].t3[ir][ic]; \
					} \
				if (bit) \
					break; \
				} }

static void map ( v0, v1, ir, fp ) /* map a cell to the image */
	long v0, v1;
	int ir;
	FILE *fp;
	{
	register int ic, bit;

	for (ic = 0; ic < size[zoom]; ic++) { /* do each scan column */
		bit = 0;
		if (v0 & HOLE) {
			if (currnt & H) /* plot holes? */
				domap1( v0 );
			if (!bit && (currnt & T)) /* plot top-side? */
				domap2( v0 );
			if (!bit && (currnt & B)) /* plot bottom-side? */
				domap2( v1 );
			}
		else {
			if (v0 && (currnt & T)) /* plot top-side? */
				domap3( v0 );
			if (!bit && v1 && (currnt & B)) /* plot bottom-side? */
				domap3( v1 );
			}
		outbit( bit, fp );
		}
	}

static int shift; /* how far to shift next bit */
static char byte; /* the byte buffer */

static void initbit () /* initialize bit output */
{
	byte = 0;
	shift = 7;
}

static void flushbit ( fp ) /* flush bit output */
FILE *fp;
{
	if (shift != 7) /* buffer empty? */
		putc( byte, fp ); /* no, output partial byte */
}

static void outbit( bit, fp ) /* output a bit using byte buffering */
int bit;
FILE *fp;
{
	byte |= ((char)bit << shift);
	if (!shift) {
		putc( byte, fp );
		byte = 0;
		shift = 7;
		}
	else
		shift--;
}
