import java.awt.*;

import java.util.*;


// my renderable object, a hemispherical shell (like a bowl)

class Hemisphere implements Renderable {

    Surface surface;

    Vector3D center;
	
	Vector3D radVector;

    float radius;

    float radSqr;



    public Hemisphere(Surface s, Vector3D c, Vector3D f) {

        surface = s;

        center = c;

		radVector = new Vector3D(c, f);

        radius = radVector.length();

        radSqr = radius*radius;

    }



    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;
		
		float dSqr = dx*dx + dy*dy + dz*dz;
        float t = radSqr + v*v - dSqr;

		if (dSqr < radSqr) { // ray starts inside the sphere

			t = v + ((float) Math.sqrt(t));
			Vector3D p = Vector3D.XplusKY(ray.origin, t, ray.direction);
			Vector3D temp = new Vector3D(center, p);
			if (temp.dot(radVector)<0)
				return false;
			if ((t > ray.t) || (t < 0))
				return false;
		}
		else { // ray starts outside the sphere

			// Test if the ray actually intersects the sphere
			if (t < 0)
				return false;

	        // Test if the intersection is in the positive
	        // ray direction and it is the closest so far
			t = (float) Math.sqrt(t);
			float w = v - t;
			Vector3D p = Vector3D.XplusKY(ray.origin, w, ray.direction);
			Vector3D temp = new Vector3D(center, p);
			if (temp.dot(radVector)<0) {
				w = v + t;
				p = Vector3D.XplusKY(ray.origin, w, ray.direction);
				temp = new Vector3D(center, p);
				if (temp.dot(radVector)<0)
					return false;
				else // the ray hit the inside of the sphere
					t = w;
			}
			else // the ray hit the outside of the sphere
				t = w;

			if ((t > ray.t) || (t < 0))
	            return false;
		};

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

    }



    public Color Shade(Ray ray, Vector lights, Vector objects, Color bgnd) {

        // 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();
		if (v.dot(n)<0)
			n.reverse();

        // The illumination model is applied
        // by the surface's Shade() method

        return surface.Shade(p, n, v, lights, objects, bgnd);
    }



    public String toString() {

        return ("hemisphere "+center+" "+radVector);

    }

}