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


#define true 1
#define NULL 0

int     shade (r)
struct ray *r;
{
    int     i,
            refract ();
    struct ray  refr;
    double  lght,
            x,
            y,
            z,
            l,
            k,
            dot (), find (), shadow (),find_facet();
    int     sx,
            objid,
            sy;
    int done=0;
    double  stupid;
    struct vector   new,
                    intersection_pts,
                    norm, save, save_int, temp;
    struct mat  trans;
    struct sphere_data   ss;
    struct object_data *c;
    struct obj_link *ptr, *three_dda(), *three_next();
    int sead_x, sead_y, sead_z;
    double bound_x, bound_y, bound_z;

    if (++level <= LEVEL) {
	c = NULL;
	l = HUGE;
                       /* 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);
        ptr = three_dda(r, &sead_x, &sead_y, &sead_z);
                  /* for starters we find out whether we hit anything. */
        while (ptr != NULL)
          {
            while (ptr != NULL)
              {
                if (ptr->the_object->type == SPHERE_TYPE)
                  {
	          ss.rad = ptr->the_object->object.sphere.rad;
                  SV (ss.cent, ptr->the_object->object.sphere.cent, r->org);
	          if ((k = find (&trans, &ss)) > 0.0 && k < l) 
                    {
	            MV (k * trans.x.x, k * trans.x.y, k * trans.x.z, new);
                    new.l=k;
	            AV (temp, new, r -> org);
                    bound_x = sead_x * num_pts_per_sead;
                    bound_y = sead_y * num_pts_per_sead;
                    bound_z = sead_z * num_pts_per_sead;
/*
printf("shade sead_x = %d sead_y = %d sead_z = %d\n", sead_x, sead_y, sead_z);
printf("bound_x = %8.2f bound_y = %8.2f bound_z = %8.2f\n", bound_x, bound_y, bound_z);
printf("trans.x x= %8.2f y = %8.2f  z = %8.2f  k =%8.2f \n", 
        trans.x.x, trans.x.y, trans.x.z, k);
printf("r->org x = %8.2f  y = %8.2f  z = %8.2f\n", r->org.x, r->org.y, r->org.z);
printf("new.x = %8.2f new.y = %8.2f  new.z = %8.2f\n", new.x, new.y, new.z);
printf("temp.x = %8.2f temp.y = %8.2f temp.z = %8.2f\n", temp.x, temp.y, temp.z);
*/
                    if ((bound_x - 10 * ROUNDOFF <= temp.x) && 
                        (bound_x + 10 * ROUNDOFF + num_pts_per_sead >= temp.x) &&
                        (bound_y - 10 * ROUNDOFF <= temp.y) && 
                        (bound_y + 10 * ROUNDOFF + num_pts_per_sead >= temp.y) &&
                        (bound_z  - 10 * ROUNDOFF <= temp.z) && 
                        (bound_z + 10 * ROUNDOFF + num_pts_per_sead >= temp.z))
                      {
		      c = ptr->the_object;
		      l = k;
                      }
	            }    /* end of if */
                  }   /* end of if SPHERE_TYPE */
                if (ptr->the_object-> type == FACET_TYPE)
                  {
                  if ((k = find_facet(ptr->the_object, 
                                      r, &intersection_pts)) > 0.0 && k < l)
                    {
                    bound_z = sead_z * num_pts_per_sead + z_world_start;    
                                          /* check for intersection being in */
                    bound_y = sead_y * num_pts_per_sead + y_world_start;    /* present sead */
                    bound_x = sead_x * num_pts_per_sead + x_world_start;
                    if ((bound_x - ROUNDOFF <= intersection_pts.x) && 
                        (bound_x + ROUNDOFF + num_pts_per_sead >= intersection_pts.x) &&
                        (bound_y - ROUNDOFF <= intersection_pts.y) && 
                        (bound_y + ROUNDOFF + num_pts_per_sead >= intersection_pts.y) &&
                        (bound_z - ROUNDOFF <= intersection_pts.z) && 
                        (bound_z + ROUNDOFF + num_pts_per_sead >= intersection_pts.z)) 
                      {

                      c = ptr->the_object;
                      save_int.x = intersection_pts.x; 
                      save_int.y = intersection_pts.y; 
                      save_int.z = intersection_pts.z;
                      save_int.l = intersection_pts.l; 
                      save_int.xzl = intersection_pts.xzl;
                      l = k;
                      }
                    }
                  }
	        ptr = ptr->next_obj;
	      }   /* end of inner while */
                  /* check to see if we hit an object in the sead */
	   if (c != NULL && (l * trans.x.y + r -> org.y) > 0.0) 
             ptr = NULL;
	   else 
             ptr = three_next(&sead_x, &sead_y, &sead_z);
         }  /* end of outer while we either have hit an object */
            /* or have gone outside the sead world */


	if (c != NULL && (l * trans.x.y + r -> org.y) > 0.0) 
          {
				/* WE HIT SOMETHING */
          if (c->type == SPHERE_TYPE)
            {
	    MV (l * trans.x.x, l * trans.x.y, l * trans.x.z, new);
	    new.l=l;
                    /* 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, c->object.sphere.cent);
	    norm.l= c->object.sphere.rad;
            }  /* end of if SPHERE_TYPE */
          else
            if (c->type == FACET_TYPE)
              {
              intersection_pts.x = save_int.x;
              intersection_pts.y = save_int.y;
              intersection_pts.z = save_int.z;
              MV(intersection_pts.x, intersection_pts.y, 
                 intersection_pts.z, refr.dir);
              norm.x = c->object.facet.normal.x;
              norm.y = c->object.facet.normal.y; 
              norm.z = c->object.facet.normal.z;
              norm.l = c->object.facet.normal.l; 
              norm.xzl = c->object.facet.normal.xzl;

              MV(intersection_pts.x, intersection_pts.y, 
                 intersection_pts.z, refr.org);
              MV(intersection_pts.x, intersection_pts.y, intersection_pts.z, r->org);
              } /* end of if FACET_TYPE */

                    /* ambient lighting */
          lght = 200.0 * c->amb;

               /* shaded lighting (diffuse). subroutine shadow is in find.c */
	 if (c->dif != 0.0) 
           {
           SV (new, light_source.cent, r -> org);
           new.l = LEN(new);
           objid = c->id;
           if ((k = DOT (new, norm)) > 0.0)
             lght += c->dif * shadow (&(r -> org),objid) * k / (new.l) / (norm.l);
	   }

                     /*reflection... easy */
	  if (c->rfl != 0.0) 
            {
                                   /* make the normal unit length */
            SCMLT ((1.0 / norm.l), norm);
                                   /* get the length of the ray's component 
                                      in the normal direction */
            stupid = 2.0 * DOT (norm, r -> dir);
            SCMLT (stupid, norm);
                                   /* subtract double the normal 
                                      component- !reflection! */
            SV (r -> dir, r -> dir, norm);
/*
            if (c->type == FACET_TYPE)
              printf("FACET dir x = %7.2f  y = %7.2f  z = %7.2f\n",
                r->dir.x, r->dir.y, r->dir.z);
*/
       r->src = c->id;
            lght += c->rfl * (double) shade (r);
	    }

                                  /* refraction. this is ugly, 
                                     which is why I choose to deal with
                                     it in it's own subroutine which comes 
                                     after this one */
          if (c->rfr != 0.0) {
           refr.src = c->id;
            lght += c->rfr * (double) refract (&refr, c);
	  }


	}
	else {			/* hit no objects... */
	    if ((r -> dir.y) < 0.0) {/* crosses floor */
		z = -(r -> org.y) / (r -> dir.y);
		(r -> org.x) += z * (r -> dir.x);
		(r -> org.z) += z * (r -> dir.z);
		(r -> org.y) = 0.0;

		SV (new, light_source.cent, r -> org);
		new.l = LEN(new);
		sx = (int) (r -> org.x / 1.5) % xground;
		if (sx < 0)
		    sx += xground;
		sy = -(int) (r -> org.z / 1.5) % yground;
		if (sy < 0)
		    sy += yground;
                objid = c->id;
		lght = (sam * shared_info.ground[sx][sy] + 1.0 - sam) * (0.8 *
			shadow (&(r -> org),objid) * (new.y) / (new.l) + 40.0);


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