import java.awt.Color;
import java.util.*;
import Surface;
import Vector3D;
import Ray;
import Renderable;

/****
 *
 *   An instructional Ray-Tracing Renderer written
 *   for MIT 6.837  Fall '98 by Leonard McMillan.
 *
 ****
 *
 *   Fix in intersect(Ray ray) to allow for rays inside
 *   the sphere.  --Jonathan Lie, 12/1998
 *
 ****/

// An example "Renderable" object
class Sphere implements Renderable {
  Surface surface;
  Vector3D center;
  float radius;
  float radSqr;

  public Sphere(Surface s, Vector3D c, float r) {
    surface = s;
    center = c;
    radius = r;
    radSqr = r*r;
  }

  public boolean intersect(Ray ray) {
    float dx = center.x - ray.origin.x;
    float dy = center.y - ray.origin.y;
    float dz = center.z - ray.origin.z;
    float v = ray.direction.dot(dx, dy, dz);

    // Do the following quick check to see if there is even a chance
    // that an intersection here might be closer than a previous one
    if (v - radius > ray.t)
      return false;

    // Test if the eye is outside the sphere
    float cSqr = dx*dx + dy*dy + dz*dz;
    float t;
    if (cSqr > radSqr) {

      // Test if the ray actually intersects the sphere
      t = radSqr + v*v - cSqr;
      if (t < 0)
	return false;

      // Test if the intersection is in the positive
      // ray direction and it is the closest so far
      t = v - ((float) Math.sqrt(t));
      if ((t > ray.t) || (t < 0))
	return false;

    } else {
      t = v + ((float) Math.sqrt(radSqr + v*v - cSqr));
    }

    ray.t = t;
    ray.object = this;
    return true;
  }

  public Color Shade(Ray ray, Vector lights, Vector objects, Color bgnd, int depth) {
    // An object shader doesn't really do too much other than
    // supply a few critical bits of geometric information
    // for a surface shader. It must must compute:
    //
    //   1. the point of intersection (p)
    //   2. a unit-length surface normal (n)
    //   3. a unit-length vector towards the ray's origin (v)
    //
    float px = ray.origin.x + ray.t*ray.direction.x;
    float py = ray.origin.y + ray.t*ray.direction.y;
    float pz = ray.origin.z + ray.t*ray.direction.z;

    Vector3D p = new Vector3D(px, py, pz);
    Vector3D v = new Vector3D(-ray.direction.x, -ray.direction.y, -ray.direction.z);
    Vector3D n = new Vector3D(px - center.x, py - center.y, pz - center.z);
    n.normalize();

    // The illumination model is applied
    // by the surface's Shade() method
    return surface.Shade(p, n, v, lights, objects, bgnd, depth);
  }

  public String toString() {
    return ("sphere "+center+" "+radius);
  }
}
