import java.awt.Color;
import java.util.Vector;

// An example "Renderable" object
public 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) {
        ///////////////////////////////////////////////////////////////
        // Equation for a sphere at origin:
        //  2    2    2    2
        // x  + y  + z  - r  = 0
        //
        // Want t such that:
        // x = Ox + Dx * t
        // y = Oy + Dy * t
        // z = Oz + Dz * t
        //
        // Comes out to:
        //    2
        // a*t  + b*t + c = 0
        // 
        // Where:
        //       2   2   2
        // a = Dx +Dy +Dz 
        // b = 2OxDx + 2OyDy + 2OzDz
        //       2   2   2   2
        // c = Ox +Oy +Oz - r
        //
        // Plug these values into the quadratic formula
        // to solve for t
        ///////////////////////////////////////////////////////////////

        // Transform ray points to sphere's object points
        // to consider the sphere at the origin
        float Ox = ray.origin.x - center.x;
        float Oy = ray.origin.y - center.y;
        float Oz = ray.origin.z - center.z;
        float Dx = ray.direction.x;
        float Dy = ray.direction.y;
        float Dz = ray.direction.z;
        
        float a = 1.0f; // Dx*Dx + Dy*Dy + Dz*Dz = 1
        float b = 2*(Ox*Dx + Oy*Dy + Oz*Dz);
        float c = Ox*Ox + Oy*Oy + Oz*Oz - radSqr;
        
        //            2
        // Calculate b  - 4*a*c and make sure it is > 0
        float disc = b*b - 4*a*c;
        //System.out.println("disc = "+disc); //debug
        if (disc < 0) {
            return false;
        }
        
        // Calculate square root of disc
        float sqdisc = (float) Math.sqrt((double) disc);

        // t = (-b +/- sqdisc)/2a = intersection point
        float t = (-b - sqdisc)/(2*a);
        if ((t > ray.t) || (t < 0)) {
            t = (-b + sqdisc)/(2*a);
            if ((t > ray.t) || (t < 0)) {
                return false;
            }
        }
        
        //if (inside(ray.origin.x, ray.origin.y, ray.origin.z)) {
        //    System.out.println("Inside: "+ray.origin);
            //t = -b/(2*a);
        //}
        
        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();

        // Determine whether inside or outside
        boolean in = inside(ray.origin.x, ray.origin.y, ray.origin.z);
        
        // The illumination model is applied
        // by the surface's Shade() method
        return surface.Shade(p, n, v, lights, objects, bgnd, in);
    }

    // NEW
    public boolean inside (float x, float y, float z) {
        // Returns true if the given 3D point is inside the sphere
        // Returns false otherwise and resets the depth
        float a = (x - center.x)*(x - center.x) +
                  (y - center.y)*(y - center.y) +
                  (z - center.z)*(z - center.z);
        if (a <= radSqr)
            return true;
        Ray.depth = 0;
        return false;
    }

    public String toString() {
        return ("sphere "+center+" "+radius);
    }
}
