//
import java.awt.*;

public class Sphere extends Shape 
{
	public Sphere(double cx, double cy, double cz, double r) 
	{
		// YOUR CODE GOES HERE
		center[0] = cx;
		center[1] = cy;
		center[2] = cz;
		radius = r;
	}

	// RETURNS THE NUMBER OF ROOTS.  ROOTS ARE PLACED IN t[]
	private final double sq(double x)
	{
		return x*x; 
	}

	private void normalize(double[] n)
	{
		double norm = Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
		n[0] /= norm;
		n[1] /= norm;
		n[2] /= norm;
	}
	
	public int traceRay(double v[], double w[], double t[]) 
	{
		double x1, x2, x3;
		double y1, y2, y3;
		double z1, z2, z3;
		x1 = v[0];
		x2 = w[0] + v[0];
		x3 = center[0];

		y1 = v[1];
		y2 = w[1] + v[1];
		y3 = center[1];
		
		z1 = v[2];
		z2 = w[2] + v[2];
		z3 = center[2];

		double a, b, c;
		a = sq(x2-x1) + sq(y2-y1) + sq(z2-z1);
		b = 2*( (x2-x1)*(x1-x3) + (y2-y1)*(y1-y3) + (z2-z1)*(z1-z3) );
		c = sq(x3) + sq(y3) + sq(z3) + sq(x1) + sq(y1) + sq(z1) - 2*(x3*x1 + y3*y1 + z3*z1) - sq(radius);
	
		//We can solve with the quadratic equation (there may be 2 real solutions)
		//The discriminant (part under the radical in the quad.eq.) will tell us how many.
		double discrim;
		discrim = sq(b) - 4*a*c;
		
		//If the discriminant is less than zero, there was no collision
		if( discrim < 0 )
			return 0;

		//If the discriminant is zero, there is one soln. (w/ multiplicity 2)
		else if( discrim == 0 )
		{
			t[0] = -b/(2*a);
			return 1;
		}

		//Otherwise, there are two solutions
		else
		{
			double sqrt_discrim = Math.sqrt(discrim);
			t[0] = ( -b - sqrt_discrim)  / (2*a);
			t[1] = ( -b + sqrt_discrim)  / (2*a);
			return 2;
		}
	}

	private double[] array(double x, double y, double z)
	{
		return new double[]{x,y,z};
	}
	
	public void computeNormal(double p[], double n[]) 
	{
		//Given a point p on the surface, the normal is (p - center) / r
		for(int i=0; i<3; i++)
		{
			n[i] = (p[i] - center[i]) / radius;
		}

		//We will now perturb the normal with noise to do bumpmapping
		//This will be slow, but oh, well
		//Ultimatly, we want n + (deriv - n*(n dot deriv)), normalized
		
		if(Shape.bumpType == 0)
			return;

		double[] deriv = new double[3];
		double pert;
		double h = 0.0001;
		
		pert = textureFactor(p, Shape.bumpType);
		deriv[0] = textureFactor(new double[]{p[0]+h,p[1],p[2]}, bumpType) - pert;
		deriv[1] = textureFactor(new double[]{p[0],p[1]+h,p[2]}, bumpType) - pert;
		deriv[2] = textureFactor(new double[]{p[0],p[1],p[2]+h}, bumpType) - pert;
		normalize(deriv);	

		double dotND = n[0]*deriv[0]+n[1]*deriv[1]+n[2]*deriv[2];
			
		double disturb[] = new double[3];
		for(int i=0; i<3; i++)
			n[i] = n[i] + deriv[i] - n[i]*dotND;
		
		/*
		n[0] += (pertX - pert) / h;
		n[1] += (pertY - pert) / h;
		n[2] += (pertZ - pert) / h;
		*/

		normalize(n);
	}

	static final int toint(double d)
	{
		return (int)(10000*d);
	}

	static final double trans(double d)
	{
		return 30*(d);
	}
	
	double noise(double[] p, double freq, double amp)
	{
		//freq *= 5;
		//freq *= 9;
		//freq *= 15;
		freq *= 25;
		return Noise.noise(freq*p[0], freq*p[1], freq*p[2]) / amp;
	}

	double abs(double[] p, double freq, double amp)
	{
		return Math.abs( noise(p, freq, amp) );
	}
	
	public double textureFactor(double p[], int type)
	{
		double result;

		switch(type)
		{
		case 1:
		result = (abs(p,1,1)+abs(p,2,2)+abs(p,4,4)+abs(p,8,8)+abs(p,16,16)+abs(p,32, 32));
		result = (Math.sin(30*p[0] + result)+1)/2.0;
    		break;

		case 2:
		result = (noise(p,1,1)+noise(p,2,2)+noise(p,4,4)+noise(p,8,8)+noise(p,16,16)+noise(p,32, 32));
		break;
		
		case 3:
		result = noise(p,1,1);
		break;

		default:
		result = 0;
		break;
		}

		return result;
	}
			
	//255, 217, 160
	//70, 12, 0
	public Color textureColor(double p[])
	{
		//double[] colorMin = { 255.0/255.0, 217.0/255.0, 160.0/255.0 };
		//double[] colorMax = { 70.0/255.0, 12.0/255.0, 0/255.0 };

		double result;
    		result = textureFactor(p, Shape.noiseType);
		
		//return new Color(d2i(result), d2i(result), d2i(result));		
		double[] out = new double[3];
		for(int i=0; i<3; i++)
		{
			out[i] = colorMin[i]*(1-result) + colorMax[i]*(result);
		}
		
		return new Color(d2i(out[0]),d2i(out[1]),d2i(out[2]));
	}
	
	public Color materialColor(double p[])
	{
		return null;	
	}

	private final double clamp(double x, double min, double max)
	{
		if( x < min )
			return min;
		else if( x > max )
			return max;
		else 
			return x;
	}
	
	private final int d2i(double d)
	{
		return (int)(255*clamp(d,0,1));
	}
	
	// YOU MIGHT WANT TO DECLARE SOME PERSISTENT VARIABLES 
	// OK
	double center[] = new double[3];
	double radius;
}