/*
 * C++ RayTracer
 * file: scene.h
 *
 * Radek Brich, 2006
 */

#ifndef SCENE_H
#define SCENE_H

#include <vector>

#include "noise.h"

#include "vector.h"

using namespace std;

/* axis-aligned bounding box */
class BBox
{
public:
	Vector3 L;
	Vector3 R;
	BBox(): L(), R() {};
};

class Ray
{
public:
	Vector3 a, dir;
	Ray(const Vector3 &aa, const Vector3 &adir):
		a(aa), dir(adir) {};
};

class Light
{
public:
	Vector3 pos;
	Colour colour;
	int shadows;

	Light(const Vector3 &position, const Colour &acolour):
		pos(position), colour(acolour), shadows(1) {};
	void castshadows(bool ashadows) { shadows = ashadows; };
};

class Texture
{
public:
	Colour colour;
	Colour evaluate(Vector3 point)
	{
		float sum = 0.0;
		for (int i = 1; i < 5; i++)
			sum += fabsf(perlin(point.x*i, point.y*i, point.z*i))/i;
		float value = sinf(point.x + sum)/2 + 0.5;
		return Colour(value*colour.r, value*colour.g, value*colour.b);
	};
};

class Material
{
public:
	float ambient, diffuse, specular, shininess; // Phong constants
	float reflection; // how much reflectife is the surface
	float refraction; // refraction index
	float transmitivity;
	Texture texture;

	Material(const Colour &acolour) {
		texture.colour = acolour;
		ambient = 0.1;
		diffuse = 0.5;
		specular = 1.0;
		shininess = 20.0;
		reflection = 0.5;
	}
};

class Shape
{
public:
	Material *material;
	Shape() {};
	virtual ~Shape() {};

	// first intersection point
	virtual bool intersect(const Ray &ray, float &dist) = 0;

	// all intersections (only for CSG)
	virtual bool intersect_all(const Ray &ray, float dist, vector<float> &allts) = 0;

	// normal at point P
	virtual Vector3 normal(Vector3 &P) = 0;

	virtual BBox get_bbox() = 0;
};

class Sphere: public Shape
{
	float sqr_radius;
	float inv_radius;
public:
	Vector3 center;
	float radius;

	Sphere(const Vector3 &acenter, const float aradius, Material *amaterial):
		sqr_radius(aradius*aradius), inv_radius(1.0f/aradius),
		center(acenter), radius(aradius) { material = amaterial; }
	bool intersect(const Ray &ray, float &dist);
	bool intersect_all(const Ray &ray, float dist, vector<float> &allts);
	Vector3 normal(Vector3 &P) { return (P - center) * inv_radius; };
	BBox get_bbox();
};

class Plane: public Shape
{
public:
	Vector3 N;
	float d;

	Plane(const Vector3 &normal, const float ad, Material *amaterial):
		N(normal), d(ad) { material = amaterial; };
	Plane(const Vector3 &normal, const Vector3 &point):
		N(normal), d(0) { /*TODO*/};
	bool intersect(const Ray &ray, float &dist);
	bool intersect_all(const Ray &ray, float dist, vector<float> &allts) {return false;};
	Vector3 normal(Vector3 &) { return N; };
	BBox get_bbox();
};

class Triangle: public Shape
{
	int k; // dominant axis
	float nu, nv, nd;
	float bnu, bnv;
	float cnu, cnv;
public:
	Vector3 A, B, C, N;

	Triangle(const Vector3 &aA, const Vector3 &aB, const Vector3 &aC, Material *amaterial);
	bool intersect(const Ray &ray, float &dist);
	bool intersect_all(const Ray &ray, float dist, vector<float> &allts) {return false;};
	Vector3 normal(Vector3 &) { return N; };
	BBox get_bbox();
};

#endif
