//--------------------------------------------------------
// COMP 238 Programming Assignemt 1 - Whitted ray tracer
// Adrian Ilie
//--------------------------------------------------------

#include <list>
#include <string>
using namespace std;

//--------------------------------------------------------
// Point class
//--------------------------------------------------------
class cPoint
{
public:
	cPoint();
	cPoint(double vX, double vY, double vZ);
	~cPoint(){};

	double X, Y, Z;

	double Distance(const cPoint& p) const;
	bool operator==(cPoint& p) const;
};

//--------------------------------------------------------
// Vector class
//--------------------------------------------------------
class cVector
{
public:
	cVector();
	cVector(const cPoint& p1, const cPoint& p2);
	cVector(double vI, double vJ, double vK);
	~cVector(){};

	double I, J, K;

	cVector Normalize() const; // returns a unit vector of this vector
	double Magnitude() const;
	double DotProd(const cVector& v) const; // returns the dot product of this and rVector
	cVector CrossProd(const cVector& v) const; // returns the cross product of this and rVector
	cVector operator+ (cVector v) const; // returns the sum of two vectors
	cVector operator- (cVector v) const; // returns the difference of two vectors
	cPoint operator+ (cPoint p) const; // returns the sum of a vector and a Point 
	cVector operator* (double s) const; // returns multiplication of a scalar with a vector
	cPoint VectorToPoint() const;	// converts a vector to a point
};

//--------------------------------------------------------
// Color class
//--------------------------------------------------------
class cColor
{
public:
	cColor(){};
	cColor(double vR, double vG, double vB);
	~cColor(){};
	
	double R, G, B;

	cColor operator* (double s) const;// multiply with a scalar
	cColor operator+ (const cColor& c) const;//add two colors
	cColor operator* (const cColor& c) const;//point multiply two colors
	unsigned char DoubleToByte(double s) const;//scale 0..1 to 0..255

	unsigned char bR() const;
	unsigned char bG() const;
	unsigned char bB() const;
};

//--------------------------------------------------------
// Ray class
//--------------------------------------------------------
class cRay
{
public:
	cRay(){};
	cRay(const cPoint& p, const cVector& d);
	cRay(const cPoint& p1, const cPoint& p2);
	~cRay(){};

	cVector Direction;
	cPoint Origin;
};

//--------------------------------------------------------
// Shape class (abstract)
//--------------------------------------------------------
class cShape
{
public:
	cShape();
	virtual ~cShape(){};

	string Type;
	cColor Ambient;
	cColor Diffuse;
	cColor Specular;
	double SpecularPower;
	double Transparency;
	double Reflectivity;
	double RefractionIndex;

	virtual bool Intersect(const cRay& r, cPoint& p) const = 0;
	virtual cVector Normal(const cPoint& p1, const cPoint& p2) const = 0;//normal at p1 towards p2
	cRay Reflect(const cPoint& p, const cRay& r) const;
	cRay Refract(const cPoint& p, const cRay& r) const;
};

//--------------------------------------------------------
// Sphere class
//--------------------------------------------------------
class cSphere : public cShape
{
public:
	cSphere(){};
	cSphere(cPoint p, double r);
	cSphere(double r, double x, double y, double z);
	~cSphere(){};

	cPoint Center;
	double Radius;

	bool Intersect(const cRay& r, cPoint& p) const;
	cVector Normal(const cPoint& p1, const cPoint& p2) const;
};

//--------------------------------------------------------
// Plane class
//--------------------------------------------------------
class cPlane : public cShape
{
public:
	cPlane();
	cPlane(const cPoint& p, const cVector& n);
	cPlane(double x, double y, double z, double nx, double ny, double nz);
	~cPlane();

	cPoint Point;
	cVector Normal2;

	bool Intersect(const cRay& r, cPoint& p) const;
	cVector Normal(const cPoint& p1, const cPoint& p2) const;
};

//--------------------------------------------------------
// Surface class
//--------------------------------------------------------
class cSurface
{

public:
	cSurface();
	~cSurface();

	string name;
	cColor Ambient, Diffuse, Specular;
	double SpecularPower, Transparency, Reflectivity, RefractionIndex;

};

//--------------------------------------------------------
// Light class
//--------------------------------------------------------
class cLight
{
public:
	cLight(); //light at (0,0,0)
	cLight(const cPoint& p);
	cLight(const cPoint& p, const cColor& c);
	cLight(const double x, const double y, const double z, const double r, const double g, const double b);
	~cLight(){};

	cPoint Point;
	cColor Color;
	cColor Contribution(const cRay& r, const cShape* s, const cPoint& p);
};

//--------------------------------------------------------
// Scene list typedefs
//--------------------------------------------------------
typedef list<cLight*> cLightList;
typedef list<cLight*>::iterator cLightListIterator;

typedef list<cShape*> cShapeList;
typedef list<cShape*>::iterator cShapeListIterator;

typedef list<cSurface*> cSurfaceList;
typedef list<cSurface*>::iterator cSurfaceListIterator;

//--------------------------------------------------------
// Tracer class
//--------------------------------------------------------
class cTracer
{

public:
	cTracer();
	~cTracer();

	cColor Background;
	cPoint Eye;
	string FileName;
	cShapeList* Shapes;
	cLightList* Lights;
	cSurfaceList* Surfaces;
	int Width;
	int Height;
	cVector u,v,o,lookAt,upVector;//frustum vectors
	int fov;//angle in degrees

	void Trace();
	void AddShape(cShape* s);
	void AddLight(cLight* l);
	void AddSurface(cSurface* s);
	void SetupCamera();
	cSurface* GetLastSurface();
	cSurface* GetSurfaceByName(char* sn);
	cShape* GetLastShape();
	void ApplySurface(cShape* sh, char* sn);
	cColor Cast(const cRay& r, int treeDepth);
	cShape* Query(const cRay& r, cPoint& p);
	cColor Shade(const cRay& r, const cShape* s, const cPoint& p);
	cColor Reflect(const cRay& r, const cShape* s, const cPoint& p, int treedepth);
	cColor Refract(const cRay& r, const cShape* s, const cPoint& p, int treedepth);
};

