#pragma once

#include "model.h"
#include "camera.h"
#include <list>

struct Ray
{
    Point3d  o; //origin
    Vector3d v; //direction 
};

class RayTracer
{
public:

    //constructor
    RayTracer(list<object3D*>& models, float ambient_light[3], unsigned int light_sample_size=500);

    //render an image with "nray_per_pixel" rays created per pixel
    void render(const Camera& cam, unsigned int img_w, unsigned int img_h, unsigned int nray_per_pixel, unsigned int max_depth=5);

    //save rendered image to file
    bool save2file(const string& filename);

private:

    // render an image with "nray_per_pixel" rays created per pixel

    // create this ray randomly in the given pixel (x,y)
	Ray create_a_random_ray(const Camera& cam, unsigned int x, unsigned int y);

    //returns a model and triangle that intersect ray r
    //return pair<NULL,NULL> if no intersection is found
    pair<object3D *, triangle *> intersect(Ray r);

    //returns a triangle in model m that intersect ray r
    //return NULL if no intersection is found
    triangle * intersect(model& m, Ray r);

    // determine if there is an intersection between triangle t and ray r
    // return true if there is an intersection and store the intersection in x
    // return false otherwise and x is undefined in this case
    bool intersect(model& m, triangle * t, Ray r, Point3d& x);

    // returns true if the sphere intersects ray r
    // return false if no intersection is found
    // x is the location of intersection
    bool intersect(sphere& s, Ray r, Point3d& x);

	//returns a triangle in model m that make closest intersection with ray r
	//return NULL if no intersection is found
	triangle * closest_intersect(model& m, Ray r);

	//check if the point p is in the shadow cast by a light at light_pos
	bool inshadow(const Point3d& p, const Point3d& light_pos);

    //
    // determine the color of ray r
    // 
    Vector3d raycolor(const Ray& r, int depth);

    //
    // determine the color of ray r, by analizing the intersection between t and r 
    // 
    Vector3d raycolor(model& m, triangle * t, const Ray& r, int depth);

    //
    // determine the color of ray r, by analizing the intersection between t and r 
    // 
    Vector3d raycolor(sphere& s, const Ray& r, int depth);

	//sample points from all area lights
	void sample(list<object3D*>& lights, int sample_size_per_obj);

	//sample points from a mesh area light
	void sample(model& m, int size);

	//sample points from a ball area light
	void sample(sphere* ball, int size);

	//max depth of rays, this controls the number of rays created in program
	unsigned int m_max_ray_depth;

    list<object3D*>& m_models;
    list<object3D*>  m_lights; //this is a subset of m_models
	list< pair<object3D*, Point3d> > m_light_samples;
	Vector3d m_ambient_light;

    vector< vector< Vector3d > > m_img; //rendered image

public:

    //for debugging only
    list<Ray> all_rays;
    list<Point3d> intersection_points;
};

