/****************************************************************************
**	Skid.c
**
**	This is a program that will output a perspective drawn 3d grid,
**	or dots, or other shit, of what, I'm not so sure.
**
**	To compile this trippy, just type, heh heh: (this is what I do)
**		gcc -o skid skid.c -lm -lXmu -lX11 -L/usr/X386/lib
**
**	Jed Reynolds, August '93
****************************************************************************/

#include <stdio.h>
#include <math.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#define MINWIDTH 300
#define MINHEIGHT 300
#define MINEXITWINH 10
#define MINEXITWINW 10
#ifdef Debug
#define TITLE "KickButt"
#else
#define TITLE "SkidMarks"
#endif
#define TITLEFONT "-adobe-courier-*-r-*-*-24-*"

/* declares for the maximum width, depth and height for the 
* "ground", these will be used in set_ground and other places */

#define MAXX 31
#define MAXY 31
#define MAXZ 10

/***************************************************************************
**	Typedef's and Global variables
***************************************************************************/
/* typedef for the ground */
	
typedef int Bedrock[MAXX][MAXY];

/* globals for X windows */
Display *theDisplay;
int theScreen;
static char *progname;

/***************************************************************************
**	main
**
**	There will be no other function nessisary, I bet.
****************************************************************************/
#define ARROWBUFF 1
#define JUMP 10
main(argc, argv)
int argc;
char **argv;
{
	unsigned int displayWidth, displayHeight;

	Window skidmark;
	unsigned int width, height;
	unsigned int skidmarkBorderWidth = 4;
	int window_size = 0;
	char *window_name = "Skid";
	Bedrock *ground;	/* this is for the points to plot */
	int lookx = 30, looky = 30, lookz = 30; /* viewpoints */
	int i,j;
	int buffer[ARROWBUFF];
	int bufsize = ARROWBUFF;
	KeySym keysym;
	XComposeStatus compose;
	int count;
	char string[ARROWBUFF];

	Window goodexit;
	unsigned int exitWidth, exitHeight;
	unsigned int exitBorderWidth = 4;

	int x =0, y = 0;
	unsigned int iconWidth, iconHeight;

	char *icon_name = "Skidmarx";
	Pixmap iconPM;

	XSizeHints sizeHints;
	XIconSize *sizeList;
	XEvent report;
	GC gc;
	XFontStruct *fontInfo;
	char *theDisplayName = NULL;

	progname = argv[0];

	/* connect to X server */
	if ((theDisplay = XOpenDisplay(theDisplayName)) == NULL )
	{
		(void) fprintf(stderr, "%s:cannot connect to X server %s\n",
				progname, XDisplayName(theDisplayName));
		exit(-1);
	}

	/* Get screen size from the display structure macro */
	theScreen = DefaultScreen(theDisplay);
	displayWidth = DisplayWidth(theDisplay, theScreen);
	displayHeight = DisplayHeight(theDisplay, theScreen);
	
	x = y = 0;	/* NW corner of window */

	/* Size the window with the proper amount of room */
	width = MINWIDTH;
	height = MINHEIGHT;

	/* create unmapped window */
	skidmark = XCreateSimpleWindow(theDisplay,
			RootWindow(theDisplay, theScreen),
			x, y, width, height, skidmarkBorderWidth,
			BlackPixel(theDisplay, theScreen),
			WhitePixel(theDisplay, theScreen));

	exitWidth = MINEXITWINW;
	exitHeight = MINEXITWINH;

	goodexit = XCreateSimpleWindow(theDisplay,
			skidmark,
			x + 5, y + 5, exitWidth, exitHeight, exitBorderWidth,
			BlackPixel(theDisplay, theScreen),
			WhitePixel(theDisplay, theScreen));

	/* create input masks for window */
	XSelectInput(theDisplay, skidmark, ExposureMask | ButtonPressMask
			| KeyPressMask | StructureNotifyMask);

	XSelectInput(theDisplay, goodexit, ExposureMask | ButtonPressMask
			| KeyPressMask );

	/* Load the font */
	load_font(&fontInfo);

	/* create GC */
	getGC(skidmark, &gc, fontInfo);

	/* This is where to deal with the Icon merde */

	/* BEFORE I map the window, I need window HINTZ */
	/* I won't deal with R3 declarations here, fukkit */
	sizeHints.flags = PPosition | PSize | PMinSize;
	sizeHints.min_width = MINWIDTH;
	sizeHints.min_height = MINHEIGHT;
	{
		XWMHints  wmHints;
                XClassHint classHints;
                XTextProperty windowName, iconName;

                /* store window name, icon name into XTextProperty, other fields */
                if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
                {
                        (void) fprintf(stderr, "%s: structure allocation for \
                                        windowName filaed.\n", progname);
                        exit(-1);
                }
		
		if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0) 
		{
			(void) fprintf(stderr, "%s: structure allocation\
					for iconName failed.\n",
					progname);
			exit(-1);
		}

		wmHints.initial_state = NormalState;
		wmHints.input = True;
		wmHints.icon_pixmap = iconPM;
		wmHints.flags = StateHint | IconPixmapHint | InputHint;

		classHints.res_name = progname;
		classHints.res_class = "Spin";

		XSetWMProperties(theDisplay, skidmark, &windowName, &iconName,
				argv, argc, &sizeHints, &wmHints, &classHints);
	}
	
	/* make room for the ground database, eh */
	#ifdef Debug
	printf("I'm making the ground...\n");
	#endif
	ground = malloc (sizeof *ground);
	set_ground(ground);

	/* map some windowage, eh? */
	XMapWindow(theDisplay, skidmark);
	XMapWindow(theDisplay, goodexit);

	/* Event Loop, baby! */
	while (1) {
		XNextEvent(theDisplay, &report);
		switch (report.type) {
		case Expose:
			#ifdef Debug
			printf("Expose\n");
			#endif 
			if (report.xexpose.count != 0)
				break;
			put_title(skidmark, gc, fontInfo, width, height);
			#ifdef Debug
			printf("	width, height = %d, %d\n", 
					width, height);
			#endif
			oh_god(skidmark, gc, ground, &width, &height, &lookx,
					&looky, &lookz);
			break;
		case ConfigureNotify:
			#ifdef Debug
			printf("Configure\n");
			#endif
			/* resize */
			
			width = report.xconfigure.width;
			height = report.xconfigure.height;
			
			/* this would be the place to see if 
			* 	window too small,
			*	reconfigure subwindows and re-map them */
			break;
		case ButtonPress:
			#ifdef Debug
			printf("ButtonPress\n");
			#endif
			if (report.xbutton.window == goodexit)
			{	/* exit, dood */
				XUnloadFont(theDisplay, fontInfo->fid);
				XFreeGC(theDisplay, gc);
				XCloseDisplay(theDisplay);
				exit(1);
			}
			break;
                case KeyPress:
			#ifdef Debug
                        printf("KeyPress\n");
			#endif
			/* get arrow keys to move around the viewpoint */
			count = XLookupString(&report, buffer, bufsize,
					&keysym, &compose);
			/* interperet the keyhitz */
			switch (keysym) {
			case XK_Left:
				lookx += JUMP;
				break;
			case XK_Right:
				lookx -= JUMP;
				break;
			case XK_Up:
				lookz += JUMP;
				break;
			case XK_Down:
				lookz -= JUMP;
				break;
			case XK_KP_Add:
				looky += JUMP;
				break;
			case XK_KP_Subtract:
				looky -= JUMP;
				break;
			default:
				break;
			} /* end key switch */
			XClearWindow(theDisplay, skidmark);
                        oh_god(skidmark, gc, ground, &width, &height, &lookx,
                                        &looky, &lookz);

			buffer[0] = NULL;
			break;
		default:
			#ifdef Debug
			printf("Default\n");
			#endif
			break;
		} /* end of switch */
	} /* end of while */

} /* end of main */

/******************************************************************************
**	load_font
**
**	load the font, eh...
******************************************************************************/
load_font(fontInfo)
XFontStruct **fontInfo;
{
	char *fontName =TITLEFONT;

	if ((*fontInfo = XLoadQueryFont(theDisplay, fontName)) == NULL)
	{
		(void) fprintf(stderr,"%s: Cannot open %s font.\n", progname,
				TITLEFONT);
		exit(-1);
	}
} /* end of load_font */

/*****************************************************************************
**	put_title
**
**	This function places the title, eh?
******************************************************************************/
put_title(window, gc, fontInfo, width, height)
Window window;
GC gc;
XFontStruct *fontInfo;
unsigned int width, height;
{
	/* this is the string that will be the title */
	char *title = TITLE;
	int titleLen, titleWidth, fontHeight;

	titleLen = strlen(title);
	titleWidth = XTextWidth(fontInfo, title, titleLen);
	fontHeight = fontInfo->ascent + fontInfo->descent;

	/*
	XDrawString(theDisplay, window, gc,
			(width - titleWidth)/2,
			(height/2) + fontInfo->descent,
			title, titleLen);
	*/
} /* end of put_title */

/*****************************************************************************
**	getGC
**
**	This function loads a GraphicsContext
*****************************************************************************/
getGC(window, gc, fontInfo)
Window window;
GC *gc;
XFontStruct *fontInfo;
{
	unsigned long valuemask = 0;
	XGCValues values;

	values.fill_style = FillSolid;
	values.foreground = BlackPixel(theDisplay, theScreen);
	values.background = WhitePixel(theDisplay, theScreen);
	/* create it... */
	*gc = XCreateGC(theDisplay, window, valuemask, &values);
	/* specify the font */
	XSetFont(theDisplay, *gc, fontInfo->fid);
	XSetForeground(theDisplay, *gc, BlackPixel(theDisplay, theScreen));
} /* end of getGC */

/*****************************************************************************
**	set_ground
**
**	This function is passed a pointer to an array of integers, it fills 
**	it with neato integers, and that's it.
*****************************************************************************/
set_ground(data)
Bedrock data;
{
	int i,j;

	#ifdef Debug
	printf("I'm in set_ground\n");
	#endif

	/* fill it with random numbers for now */
	for (i=0; i<MAXX; i++)
	{
		for (j=0; j<MAXY; j++)
		{
			data[i][j]= (random()/MAXZ) % MAXZ;
			#ifdef Debug
			printf("%d ", data[i][j]);
			#endif
		}
		#ifdef Debug
		printf("\n");
		#endif
	}
} /* end of set_ground */

/*****************************************************************************
**	oh_god
**
**	This function plots merde to the window. It must be passed the 
**	array of points, the height and width of the window its plotting
**	to, the GC, the window, what else, I'm not sure...oh, how about
**	an extra variable just while we're at it, maybe we'll use is as
**	as a spacer between the dots or lines we(I) plot...I'm not looking
**	foreward to writing this function, I'm way over m'hed!
**	
**	x,y,z are for viewpoint
**
**	you have to pass a pointer to the width and height, because I'm having
**	problems making height constant...somehow it is overwritten, and
**	I bet that's very, very bad.
*****************************************************************************/
#define SPACE 2 
#define SPHERICAL_C 100
oh_god(window, gc, data, width, height, x, y, z)
Window window;
GC gc;
Bedrock data;
unsigned int *width, *height;
int *x,*y,*z;
{
	int i,j, m,n;		/* loop, loop, screen, screen */
	double txa, tya, tza;	/* used for the points to be plotted */
	double txb, tyb, tzb;	/* temps */
	int d = SPHERICAL_C;		/* something called spherical convergence? */
	double sr1, sr2, sr3, cr1, cr2, cr3; /* angles */
	double sx, sy;		/* used in the process for scaling points
				* to screen */
/*
	#ifdef Debug
	height = width;
	printf ("I'm in OMYGOD!\n");
	printf("	width = %d\n", *width);
	printf("	height = %d\n", *height);
	#endif
*/
	/* For anyone who's reading this delightful chunk-o-code,
	* I'm coppin' the formulas from THE SHITTIEST source, which,
	* nominally, is the only one that I have...a book on "graf-x"
	* in "C" called, oh, get this... _High_Performance_Graphics
	* in_C,_Animation_and_Simulation_ by Lee Adams. It's repleat
	* with the most global variables, the most packed, uncommented
	* code I've ever seen. If a reader were to grade this, it'd be
	*		THROWN 		OUT
	* (well, +10 for functionality, I'll be fair) */

	for(i=0; i<MAXX; i++)
	{
		#ifdef Debug
		printf("\n	");
		#endif
		for(j=0; j<MAXY; j++)
		{
			/* calc the fake space between each x,y,z */
			txa = i * SPACE + .01;
			tya = j * SPACE + .01;
			tza = (int) data[i][j];

			#ifdef Debug
			printf(".");
			#endif

			/* calculate all the rotation */
			sr1 = sin ( atan ( tya/txa ));
			sr2 = sin ( atan ( tza/txa ));
			sr3 = sin ( atan ( tza/tya ));
			cr1 = cos ( atan ( tya/txa ));
			cr2 = cos ( atan ( tza/txa ));
			cr3 = cos ( atan ( tza/tya ));

			#ifdef Debug
			#endif

			/* calculate the relative x,y of each point */
			/*	This might take some werk... */
			txa = (-1) * txa;
			txb = cr1 * txa - sr1 * tza;
			tzb = sr1 * txa * cr1 * tza;
			txa = cr2 * txb + sr2 * tya;
			tyb = cr2 * tya - sr2 * txb;
			tza = cr3 * tzb - sr3 * tyb;
			tya = sr3 * tzb + cr3 * tyb;
			txa = txa + *x;
			tya = tya + *y;
			tza = tza + *z;
			sx = d * txa / tza;
			sy = d * tya / tza;

			#ifdef Debug
			#endif

			/* scale each x,y to the window to be viewed */
			m = (sx * *width / (MAXX * SPACE)) + *width/2;
			n = (sy * *height / (MAXY * SPACE)); + *height/2;

			/* plot each point */
			#ifdef Debug
			XFillRectangle(theDisplay, window, gc, m,n,5,5);
			#else
			XDrawPoint(theDisplay, window, gc, m, n);
			#endif
		} /* end of for */
	} /* end of for */
} /* end of oh_god */
/* end of file */
