import java.util.*;

/** Represents a scene, including both the surfaces and the
 * light sources in the scene. */
public class Scene {
	private ArrayList<Surface> models = new ArrayList<Surface>();
	private ArrayList<Point> lights = new ArrayList<Point>();
	private List<Point> lightView = Collections.unmodifiableList(lights);
	private double ambient = 0.3;

	public Scene() { }

	/** Adds the given surface into this scene. */
	public void add(Surface toAdd) {
		models.add(toAdd);
	}

	/** Adds a light at the given poin into this scene with full
	 * brightness. */
	public void addLight(Point source) {
		lights.add(source);
	}

	/** Returns an unmodifiable list holding the locations of
	 * all lights in the scene. */
	public List<Point> getLights() {
		return lightView;
	}

	/** Sets the brightness of the scene's ambient light. */
	public void setAmbient(double value) {
		ambient = value;
	}

	/** Returns the brightness of the scene's ambient light. */
	public double getAmbient() {
		return ambient;
	}

	/** Determines the closest intersection of the given ray
	 * with the scene that lies at least 1 unit away - ie, where
	 * the difference between hit point and the ray's origin is
	 * at least as long as the ray's direction vector. */
	public Intersection traceRay(Ray query) {
		Surface closest = null;
		Intersection closest_i = Intersection.NONE;
		double closest_d = Double.POSITIVE_INFINITY;
		for(Surface m : models) {
			Intersection i = m.getIntersection(query);
			double d = i.getDistance();
			if(d < closest_d) {
				closest = m;
				closest_d = d;
				closest_i = i;
			}
		}
		return closest_i;
	}

	/** Returns true if there are any surfaces in the scene
	 * between the given point and the given light source. */
	public boolean inShadow(Point query, Point light) {
		Vector diff = light.subtract(query);
		double diffLen = diff.getLength();
		Ray ray = Ray.create(query, diff.scale(0.0001 / diffLen));
		double maxDist = (diffLen - 0.0001) / 0.0001;
		for(Surface m : models) {
			Intersection i = m.getIntersection(ray);
			double d = i.getDistance();
			if(d < maxDist) return true;
		}
		return false;
	}
}
