#pragma once
#include "includes.h"
#include "config.h"
//#include "parse/parser.h"

#define SCENE_OBJECT_TYPE_POINT 1
#define SCENE_OBJECT_TYPE_SPHERE 2
#define SCENE_OBJECT_TYPE_PLANE 3
#define SCENE_OBJECT_TYPE_TRIANGLE 4

struct SceneElement {
	string* name;
	bool is_default;

	SceneElement(string str);
	~SceneElement();
};

struct Camera:SceneElement {
	double position[3];
	double center[3];
	double up[3];
	double aperture;
	int aperture_samples;
	double image_plane;
	double recip_focal_length;

	Camera();
};

struct Image:SceneElement {
	int width;
	int height;
	int samples;

	Image();
};

struct Light;

struct Material:SceneElement {
	double ambient[3];
	double diffuse[3];
	double specular[3];
	//double emissision[3];
	//double transmission;
	double refractive_index;
	double exponent_refractive;
	double shininess;
	SDL_Surface* texture;

	map<Light*,int>* to_shadow;
	bool reflect, refract;
	double coefficient_reflection, coefficient_refraction;
	int samples_reflection, samples_refraction;

	double pow_table[NUM_POW_TABLE];

	void init(void);
	Material(string name);
	~Material();
};

class SceneParser;
struct Object:SceneElement {
	friend list<string*>* scene_parse(SceneParser*,
		Camera*,
		Image*,
		map<string,Material*>*, list<Object*>*, list<glLibObject*>*, map<string,Light*>*,
		double*);
	
	//friend list<string*>* parse_scene(SceneParser*,Camera*,Image*,map<string,Material*>*, list<Object*>*, list<glLibObject*>*, map<string,Light*>*, double*);
	public:
		double position[3];
		Material* material;
		char type;
	private:
		bool _must_delete_material;

	public:
		virtual inline void set_normal(double hit_position[3], double n[3]) = 0;

		Object(char type, string name);
		~Object();
};
struct Point:Object {
	virtual inline void set_normal(double hit_position[3], double n[3]) {}

	Point();
};
struct Sphere:Object {
	double radius;

	virtual inline void set_normal(double hit_position[3], double n[3]) {
		glLibMathVec::subtract3(hit_position,position,n);
		glLibMathVec::normalize3(n);
	}

	Sphere();
};
struct Plane:Object {
	double normal[3];

	virtual inline void set_normal(double hit_position[3], double n[3]) {
		glLibMathVec::copy3(normal,n);
	}

	Plane();
};
struct Triangle:Object {
	double position2[3], position3[3];
	double normal1[3], normal2[3], normal3[3];
	double uv1[2], uv2[2], uv3[2];

	double _bary_vec0[3],_bary_vec1[3],_bary_vec2[3];
	double _bary_dot00, _bary_dot01, _bary_dot02, _bary_dot11, _bary_dot12;
	double _bary_value_vec0[3], _bary_value_vec1[3];
	bool _bary_inited;
	void _bary_init(void);

	void barycentric(double hit_position[3], double quan1[], double quan2[], double quan3[], double result[], int length);
	
	inline void set_uv(double hit_position[3], double uv[2]) {
		barycentric(hit_position,uv1,uv2,uv3,uv,2);
	}
	virtual inline void set_normal(double hit_position[3], double n[3]) {
		barycentric(hit_position,normal1,normal2,normal3,n,3);
		glLibMathVec::normalize3(n);
	}

	Triangle();
};

struct Light : SceneElement {
	public:
		Object* primitive;
		double color[3];
	private:
		bool _is_default;
		static double _random_sphere_points[NUM_RANDOM_SAMPLES*3];

	public:
		static void init(void);

		Light(string name);
		~Light();
		void get_random_point(double point[3]);
};