/*
** printed circuit board displayer, 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
**
** HISTORY
** (name		date		description)
** ----------------------------------------------------
** randy nevin		2/1/89		initial version
** randy nevin		2/11/89		released version 1.00
*/

#include <stdio.h>

/*
#ifndef M_XENIX
#include <stdlib.h>
#include <io.h>
#include <conio.h>
#include <dos.h>
#endif
*/

#include "pcbview.h"

struct Screen *screen;
struct Window *window;
struct RastPort *rastp;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct IntuiMessage *IM;
ULONG class;
USHORT code;

extern struct Screen *OpenScreen();
extern struct Window *OpenWindow();
extern struct Library *OpenLibrary();
extern void CloseLibrary(),CloseScreen(),CloseWindow();
extern struct Message *GetMsg();

#define MENUCODE(m,i,s) (SHIFTMENU(m)|SHIFTITEM(i)|SHIFTSUB(s))

/*
#include <string.h>
*/

extern char *strrchr();

/* #include <signal.h> enable this to catch ^C */
#include "cell.h"

/*
** ^C handling is disabled because on my system enabling it seems to interfere
** with int 10h processing, which is needed to plot pixels. you should enable
** it and see what happens on your system.
*/

/* WARNING: the code below assumes 640x350 16-color ega */

/* 0=black	1=blue		2=green		3=light blue		*/
/* 4=red	5=purple	6=brown		7=grey			*/
/* 8=black	9=bright blue	A=bright green	B=bright light blue	*/
/* C=scarlet	D=purple	E=yellow	F=white			*/

/*
** the colors below work fine for me, but may not for your particular ega and
** monitor. if bright blue and light blue look the same to you, or some traces
** appear to be missing, you may want to change these constants.
**
** on some egas, there appear to be gaps in the traces; i don't know why. on
** other egas, the traces look fine. this happened to me on the maximum zoom,
** but not on any other zoom level. apparently some problem with the int 10h
** interface.
*/

/* colors of objects */
#define HOLC	0x2	/* hole color; scarlet			*/
#define FRSD	0x5	/* frontside trace color; bright blue	*/
#define BKSD	0x4	/* backside trace color; light blue	*/
#define EDGE	0x7	/* edge color; yellow			*/

/* screen limits */
#define MINHORZ	0	/* left-most pixel	*/
#define MAXHORZ	639	/* right-most pixel	*/
#define MINVERT	15	/* top-most pixel	*/
#define MAXVERT	511	/* bottom-most pixel	*/

static int mode; /* for saving original screen mode */
static int sides = 3; /* 0=holes only, 1=front only, 2=back only, 3=all */

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

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

static int zoom = 1; /* 0=3x3, 1=6x6, 2=10x10, 3=18x18 */
static int zoomsize[MAXZOOM+1] = { ZOOM0, ZOOM1, ZOOM2, ZOOM3 };

/* current lower-left position */
static int Xrow = 0;
static int Xcol = 0;

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();
extern int GetMode();
extern void SetMode();
extern void Dot();

void main();
/* static void control_C( void ); enable this to catch ^C */
static void doedges();
static void doboard();
static void map();
static void plot0();
static void plot1();
static void plot2();
static void plot3();

void main ( argc, argv ) /* input routed board, display it on the screen */
int argc;
char *argv[];
{
	char *self, *p;
	int r, c, rp, cp, i1, i2, i3, i4;
	FILE *fp;
	long x;
/*
#ifndef M_XENIX
	union REGS regs;
#else
	int chh;
#endif
*/

	printf( "Copyright (C) Randy Nevin, 1989. Version 1.00\n" );
	printf( "See source code for rights granted.\n\n" );
	printf( "Amiga Port by Udi Finkelstein\n" );
	self = argv[0];
	/* get rid of initial part of path */
	if ((p = strrchr( self, '/' )) || (p = strrchr( self, ':' )))
		self = ++p;
	if (argc != 2) { /* need infile */
		fprintf( stderr, "usage: %s infile\n", self );
		exit( -1 );
		}
	if (!(fp = fopen( argv[1], "r" ))) {
		fprintf( stderr, "can't open %s\n", argv[1] );
		exit( -1 );
		}
	/* fetch the board dimensions */
	if ((rp = getc( fp )) == EOF || (cp = getc( fp )) == EOF) {
		fprintf( stderr, "premature eof\n" );
		exit( -1 );
		}
	Nrows = (rp & 0xFF) | ((cp << 8) & 0xFF00);
	if ((rp = getc( fp )) == EOF || (cp = getc( fp )) == EOF) {
		fprintf( stderr, "premature eof\n" );
		exit( -1 );
		}
	Ncols = (rp & 0xFF) | ((cp << 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 frontside */
			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 backside */
			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 );
			}
		}
	/* tell user what commands are available */
/*
	printf( "\t0   = holes only\n" );
	printf( "\t1   = holes and top traces\n" );
	printf( "\t2   = holes and bottom traces\n" );
	printf( "\t3   = holes and all traces\n" );
	printf( "\tz/Z = zoom one level / maximum zoom\n" );
	printf( "\ts/S = shrink one level / minimum shrink\n" );
	printf( "\tl/L = move left by one / move left by ten\n" );
	printf( "\tr/R = move right by one / move right by ten\n" );
	printf( "\tu/U = move up by one / move up by ten\n" );
	printf( "\td/D = move down by one / move down by ten\n" );
	printf( "\tany other key exits the program\n" );
	printf( "\nPress ENTER to continue, or ^C to exit " );
*/

#ifdef MSDOS_CODE

#ifndef M_XENIX
	regs.h.ah = 0x8; /* character input without echo */
	intdos( &regs, &regs ); /* call dos to get a keystroke */
#else
	(void)getchar();
#endif
	mode = GetMode(); /* save mode so can restore later */
	/* signal( SIGINT, control_C ); enable this to catch ^C */
	SetMode( 0x10 ); /* 640x350 16-color mode */

#endif

	if ((GfxBase = (struct GfxBase *)OpenLibrary(
		"graphics.library",33L)) == NULL) {
			printf("Can't open gfx!\n");
			exit(10);
	}

	if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
		"intuition.library",33L)) == NULL) {
			printf("Can't open intuition!\n");
			CloseLibrary(GfxBase);
			exit(10);
	}

	if ((screen = OpenScreen(&NewScreenStructure)) == NULL) {
			printf("Can't open screen!\n");
			CloseLibrary(IntuitionBase);
			CloseLibrary(GfxBase);
			exit(10);
	}

	NewWindowStructure1.Screen = screen;

	if ((window = OpenWindow(&NewWindowStructure1)) == NULL) {
			printf("Can't open window!\n");
			CloseScreen(screen);
			CloseLibrary(IntuitionBase);
			CloseLibrary(GfxBase);
			exit(10);
	}

	rastp = window->RPort;
	SetMenuStrip(window,&MenuList1);
	LoadRGB4(&screen->ViewPort,Palette,(long)PaletteColorCount);

	doedges(); /* display board edges */
	doboard(); /* display the board */
	for (;;) { /* process until unrecognized keystroke */

#ifdef MSDOS_CODE

#ifndef M_XENIX
		regs.h.ah = 0x8; /* character input without echo */
		intdos( &regs, &regs ); /* call dos to get a keystroke */
		switch (regs.h.al) { /* determine what it is */
#else
		switch (chh = getchar()) { /* determine what it is */
#endif
#endif

		Wait (1L << (window->UserPort->mp_SigBit));
		while((IM = (struct IntuiMessage *)
				GetMsg(window->UserPort)) != NULL) {
			code = IM->Code;
			class = IM->Class;
			ReplyMsg(IM);
			switch (class) {
				case CLOSEWINDOW:
					CloseWindow(window);
					CloseScreen(screen);
					CloseLibrary(IntuitionBase);
					CloseLibrary(GfxBase);
					exit(10);
				case MENUPICK:
					switch (code) {
						case MENUCODE(0,0,NOSUB): /* just show holes */
							if (sides == 0)
								continue;
							sides = 0;
							break;
						case MENUCODE(0,1,NOSUB): /* show holes and top-side traces */
							if (sides == 1)
								continue;
							sides = 1;
							break;
						case MENUCODE(0,2,NOSUB): /* show holes and bottom-side traces */
							if (sides == 2)
								continue;
							sides = 2;
							break;
						case MENUCODE(0,3,NOSUB): /* show holes and all traces */
							if (sides == 3)
								continue;
							sides = 3;
							break;
						case MENUCODE(2,1,NOSUB): /* zoom to the limit */
							if (zoom == MAXZOOM)
								continue;
							zoom = MAXZOOM;
							break;
						case MENUCODE(2,0,NOSUB): /* zoom by one */
							if (zoom == MAXZOOM)
								continue;
							zoom++;
							break;
						case MENUCODE(2,3,NOSUB): /* shrink to the limit */
							if (zoom == 0)
								continue;
							zoom = 0;
							break;
						case MENUCODE(2,2,NOSUB): /* shrink by one */
							if (zoom == 0)
								continue;
							zoom--;
							break;
						case MENUCODE(1,1,NOSUB): /* left by 10 */
							if (Xcol == 0)
								continue;
							if (Xcol <= 10)
								Xcol = 0;
							else
								Xcol -= 10;
							break;
						case MENUCODE(1,0,NOSUB): /* left by one */
							if (Xcol == 0)
								continue;
							Xcol--;
							break;
						case MENUCODE(1,3,NOSUB): /* right by 10 */
							if (Xcol == Ncols-1)
								continue;
							if (Xcol >= Ncols-11)
								Xcol = Ncols-1;
							else
								Xcol += 10;
							break;
						case MENUCODE(1,2,NOSUB): /* right by one */
							if (Xcol == Ncols-1)
								continue;
							Xcol++;
							break;
						case MENUCODE(1,5,NOSUB): /* up by 10 */
							if (Xrow == Nrows-1)
								continue;
							if (Xrow >= Nrows-11)
								Xrow = Nrows-1;
							else
								Xrow += 10;
							break;
						case MENUCODE(1,4,NOSUB): /* up by one */
							if (Xrow == Nrows-1)
								continue;
							Xrow++;
							break;
						case MENUCODE(1,7,NOSUB): /* down by 10 */
							if (Xrow == 0)
								continue;
							if (Xrow <= 10)
								Xrow = 0;
							else
								Xrow -= 10;
							break;
						case MENUCODE(1,6,NOSUB): /* down by one */
							if (Xrow == 0)
								continue;
							Xrow--;
							break;
					}
			}
		}
		ClearPCB();
		doedges(); /* display board edges */
		doboard(); /* display the board */
	}
}

/*
** enable this to catch ^C
**
**static void control_C () { / * handle ^C * /
**	SetMode( mode ); / * restore original screen mode * /
**	exit( 0 );
**	}
*/

/*
 * display the board edges
 */
static void doedges ()
{
	int r1, c1, r2, c2, i, z;
	long xr1, xc1, xr2, xc2;

	z = zoomsize[zoom];
	/* first, calculate their virtual screen positions */
	r1 = MAXVERT+(Xrow*z); /* bottom edge */
	c1 = MINHORZ-(Xcol*z); /* left edge */
	r2 = MAXVERT-1-((Nrows-Xrow)*z); /* top edge */
	c2 = MINHORZ+1+((Ncols-Xcol)*z); /* right edge */

	SetAPen(rastp,(long)EDGE);

	xr1 = r1; xr2 = r2;
	if (r1 < MINVERT) xr1 = MINVERT;
	if (r1 > MAXVERT) xr1 = MAXVERT;
	if (r2 < MINVERT) xr2 = MINVERT;
	if (r2 > MAXVERT) xr2 = MAXVERT;

	xc1 = c1; xc2 = c2;
	if (c1 < MINHORZ) xc1 = MINHORZ;
	if (c1 > MAXHORZ) xc1 = MAXHORZ;
	if (c2 < MINHORZ) xc2 = MINHORZ;
	if (c2 > MAXHORZ) xc2 = MAXHORZ;

	if (r1 >= MINVERT && r1 <= MAXVERT) { /* draw bottom edge */
		Move(rastp,xc1,(long)r1);
		Draw(rastp,xc2,(long)r1);
	}

	if (c1 >= MINHORZ && c1 <= MAXHORZ) { /* draw left edge */
		Move(rastp,(long)c1,xr1);
		Draw(rastp,(long)c1,xr2);
	}

	if (r2 >= MINVERT && r2 <= MAXVERT) { /* draw top edge */
		Move(rastp,xc1,(long)r2);
		Draw(rastp,xc2,(long)r2);
	}

	if (c2 >= MINHORZ && c2 <= MAXHORZ) { /* draw right edge */
		Move(rastp,(long)c2,xr1);
		Draw(rastp,(long)c2,xr2);
	}
}

static void doboard () { /* display the board on the screen, row by column */
	int r, c, rp, cp, rpd, cpd, z;
	long x, y;

	z = zoomsize[zoom];
	rpd = MINVERT+z; /* top-most plottable row */
	cpd = MAXHORZ-z; /* right-most plottable column */
	for (r = Xrow, rp = MAXVERT-1; r < Nrows && rp >= rpd; r++, rp -= z) {
		for (c = Xcol, cp = MINHORZ+1; c < Ncols && cp <= cpd;
				c++, cp += z) {
			x = GetCell( r, c, TOP );
			y = GetCell( r, c, BOTTOM );
			if (x || y) /* only map if something is there */
				map( rp, cp, x, y );
			}
		}
	}

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,c)	{ for (i = 0; i < z1; i++) { \
				if (v & (y1[i].t)) { \
					if (zoom == 0) \
						plot0( rp, cp, y1[i].t0, c );\
					else if (zoom == 1) \
						plot1( rp, cp, y1[i].t1, c );\
					else if (zoom == 2) \
						plot2( rp, cp, y1[i].t2, c );\
					else if (zoom == 3) \
						plot3( rp, cp, y1[i].t3, c );\
					} \
				} } \

#define domap2(v,c)	{ for (i = 0; i < z2; i++) { \
				if (v & (y2[i].t)) { \
					if (zoom == 0) \
						plot0( rp, cp, y2[i].t0, c );\
					else if (zoom == 1) \
						plot1( rp, cp, y2[i].t1, c );\
					else if (zoom == 2) \
						plot2( rp, cp, y2[i].t2, c );\
					else if (zoom == 3) \
						plot3( rp, cp, y2[i].t3, c );\
					} \
				} } \

#define domap3(v,c)	{ for (i = 0; i < z3; i++) { \
				if (v & (y3[i].t)) { \
					if (zoom == 0) \
						plot0( rp, cp, y3[i].t0, c );\
					else if (zoom == 1) \
						plot1( rp, cp, y3[i].t1, c );\
					else if (zoom == 2) \
						plot2( rp, cp, y3[i].t2, c );\
					else if (zoom == 3) \
						plot3( rp, cp, y3[i].t3, c );\
					} \
				} } \

static void map ( rp, cp, v0, v1 ) /* map a cell */
	int rp, cp;
	long v0, v1;
	{
	int i;

	if (v0 & HOLE) {
		domap1( v0, HOLC ); /* plot the hole */
		if (v1 && (sides & 2)) /* plot backside? */
			domap2( v1, BKSD );
		if (v0 && (sides & 1)) /* plot frontside? */
			domap2( v0, FRSD );
		}
	else {
		if (v1 && (sides & 2)) /* plot backside? */
			domap3( v1, BKSD );
		if (v0 && (sides & 1)) /* plot frontside? */
			domap3( v0, FRSD );
		}
	}

static void plot0 ( rp, cp, obj, color ) /* plot a 3x3 template */
	int rp, cp;
	char obj[ZOOM0][ZOOM0];
	int color;
	{
	int r, c;

	for (r = 0; r < ZOOM0; r++)
		for (c = 0; c < ZOOM0; c++)
			if (obj[r][c])
				Dot( color, rp-r, cp+c );
	}

static void plot1 ( rp, cp, obj, color ) /* plot a 6x6 template */
	int rp, cp;
	char obj[ZOOM1][ZOOM1];
	int color;
	{
	int r, c;

	for (r = 0; r < ZOOM1; r++)
		for (c = 0; c < ZOOM1; c++)
			if (obj[r][c])
				Dot( color, rp-r, cp+c );
	}

static void plot2 ( rp, cp, obj, color ) /* plot a 10x10 template */
	int rp, cp;
	char obj[ZOOM2][ZOOM2];
	int color;
	{
	int r, c;

	for (r = 0; r < ZOOM2; r++)
		for (c = 0; c < ZOOM2; c++)
			if (obj[r][c])
				Dot( color, rp-r, cp+c );
	}

static void plot3 ( rp, cp, obj, color ) /* plot an 18x18 template */
	int rp, cp;
	char obj[ZOOM3][ZOOM3];
	int color;
	{
	int r, c;

	for (r = 0; r < ZOOM3; r++)
		for (c = 0; c < ZOOM3; c++)
			if (obj[r][c])
				Dot( color, rp-r, cp+c );
	}
