/*
 * this subroutine does all the gritty work- it calculates 
 * what shade each pixel should be. I like recursion.
 */
#include <math.h>
#include <stdio.h>
#include "MyMath.h"
#include "rtd.h"
#include "macros.h"
#include "extern.h"

extern	FFP	big,
		little;

int	shade (r)
struct	ray	*r;
{
	int	i,
		c,
		refract ();

	struct	ray	refr;

	FFP	lght,
		z,
		l,
		k,
		sanity,
		find (), shadow ();

	int	sx,
		sy;

	FFP	stupid;

	struct	vector	new,
			norm;

	struct	mat	trans;

	struct	sphere	ss;

	if (++level <= LEVEL) {
		c = -1;
		l = ieee_to_ffp(1.0e37);

		/* get vector length and xz component for mt() */
		r -> dir.l = LEN (r -> dir);
		r -> dir.xzl = XZL (r -> dir);

		/*
			make a transform matrix that rotates
			something in space so that the ray will
			be aligned with the x axis
		*/
		mt (&(r -> dir), &trans);

		/* for starters we find out whether we hit anything. */
		for (i = 0; i < nob; i++) {
			ss.rad = bl[i] -> s.rad;
			SV (ss.cent, bl[i] -> s.cent, r -> org);
			k = find (&trans, &ss);
/*
			if( SPTst( k ) > 0 )
				printf("k = %f\n", ffp_to_ieee(k) );
*/
			if (
				( SPTst( k ) > 0 )	/* k>0.0 */
				&& 
				( SPCmp(l, k) > 0 )	/* k<l */
			) {
				c = i;
				l = k;
			}
		}

		if (
			c >= 0 && 
			SPTst( SPAdd( SPMul(l, trans.x.y), r->org.y)) > 0
		) {
			/* WE HIT SOMETHING */
/*
			printf("HIT: ball # %d, l = %f\n",
				c, ffp_to_ieee(l) );
*/
			MV (SPMul(l, trans.x.x),SPMul(l, trans.x.y),SPMul(l, trans.x.z),new );

			/*
				move the new orgin of the ray
				to the intersection
			*/
			AV (refr.org, new, r -> org);
			AV (r -> org, new, r -> org);
			MV (r -> dir.x, r -> dir.y, r -> dir.z, refr.dir);

			/* get a normal vector for the intersection point */
			SV (norm, r -> org, bl[c] -> s.cent);
			norm.l = LEN (norm);

			/* ambient lighting */
			lght = SPMul( SPFlt(200), bl[c] -> amb);

			/*
				shaded lighting (diffuse).
				subroutine shadow is in find.c
			*/
			if ( SPTst( bl[c] -> dif) ) {
				SV (new, ls.cent, r -> org);
				new.l = LEN (new);

				if( SPTst( (k=DOT(new, norm)) ) > 0)
					lght =	SPAdd(
						 SPDiv(
						  (norm.l),
						  SPDiv(
						   (new.l),
						   SPMul(
						    SPMul(
						     bl[c]->dif,
						     shadow( &(r -> org) )
						    ),
						    k
						   )
						  )
						 ),
						 lght
						);

/*
					lght += bl[c]->dif *
						shadow( &(r -> org) ) *
						k / (new.l) / (norm.l);
*/
			}

			/*reflection... easy */
			if ( SPTst(bl[c] -> rfl) ) {
				/* make the normal unit length */
				norm.l = LEN (norm);
				SCMLT ( SPDiv(norm.l, SPFlt(1) ), norm);

				/*
					get the length of the ray's 
					component in the normal direction
				*/
				stupid = SPMul(
						SPFlt(2),
						DOT (norm, r -> dir)
					);
				SCMLT (stupid, norm);

				/*
					subtract double the normal
					component-!reflection!
				*/
				SV (r -> dir, r -> dir, norm);
				lght =	SPAdd(
						lght,
						SPMul(
							bl[c] -> rfl, 
							SPFlt( shade (r) )
						)
					);
/*
				lght += bl[c] -> rfl * (double) shade (r);
*/
			}

			/*
				refraction. this is ugly as sin,
				which is why I choose to deal with
				it in it's own subroutine, which is 
				in it's own file now.
			*/
			if ( SPTst(bl[c] -> rfr) ) {
				lght = 	SPAdd(
					  lght,
					  SPMul(
					    bl[c] -> rfr,
					    SPFlt( refract (&refr, bl[c] ) )
					  )
					);
/*
				lght += bl[c] -> rfr *
					(double) refract (&refr, bl[c]);
*/
			}

		} else {
			/* hit no objects... */
			if (SPTst(r -> dir.y) < 0) {
				/* crosses floor */
				z = SPDiv(
					(r -> dir.y),
					SPNeg(r -> org.y)
				);

				(r -> org.x) =	SPAdd(
							(r -> org.x),
							SPMul(
								z, 
								(r -> dir.x)
							)
						);

				(r -> org.z) =	SPAdd(
							(r -> org.z),
							SPMul(
								z, 
								(r -> dir.z)
							)
						);
/*
				z = -(r -> org.y) / (r -> dir.y);
				(r -> org.x) += z * (r -> dir.x);
				(r -> org.z) += z * (r -> dir.z);
*/

				(r -> org.y) = SPFlt(0);

				/*
					find out the mod of the value to 
					see where it hits susie. (ouch)
					mess here if you only want the
					pattern once.
				*/
				SV (new, ls.cent, r -> org);
				new.l = LEN (new);

				sx =	SPFix(
						SPDiv(
							ieee_to_ffp(1.5),
							r -> org.x
						)
					) % xsue;

				if (sx < 0)
					sx += xsue;

				sy =	-SPFix(
						SPDiv(
							ieee_to_ffp(1.5),
							r -> org.z
						)
					) % ysue;

/*
				sy = -(int) (r -> org.z / 1.5) % ysue;
*/

				if (sy < 0)
					sy += ysue;
/*
				lght =	(
						sam *
						suzie[sx][sy]+ 
						1.0 - sam
					) * 
					(0.8 * shadow (&(r -> org)) *
					(new.y) / (new.l) + 40.0);
*/

				sanity = SPMul(
					  sam,
					  SPAdd(
					    SPSub(
					      sam,
					      SPFlt(1)
					    ),
					    SPDiv(
					      big,
					      SPSub(
						little,
						SPFlt(suzie[sx][sy])
					      )
					    )
					  )
					);
			
				lght =	SPAdd(
					  SPFlt(40),
					  SPDiv(
					    (new.l),
					    SPMul(
					      (new.y),
					      SPMul(
						ieee_to_ffp(0.8),
						SPMul(
						  shadow ( &(r -> org) ),
						  sanity
						)
					      )
					    )
					  )
					);

			} else {
				/* check to see if it hit lightsource */
				SV (ss.cent, ls.cent, r -> org);
				ss.rad = ls.rad;
				if (SPTst(find (&trans, &(ss.cent))) > 0)
					lght = SPFlt(255);
				else
					lght = SPFlt(0);
			}
		}
	}
		/* too many levels return 0 cause it shouldn't matter */
	else
		lght = SPFlt(0);
	level--;

	if (SPTst(lght) < 0)
		lght = SPFlt(0);

	if (SPCmp(lght, SPFlt(255) ) > 0 )
		lght = SPFlt(255);


	return ( SPFix(lght) );
}
