#include "stdafx.h"

#include "Scene.hxx"
#include "Triangle.hxx"
#include "Image.hxx"
#include "PerspectiveCamera.hxx"
#include "Sphere.hxx"
#include "InfinitePlane.hxx"

#include "EyeLightShader.hxx"
#include "PhongShader.hxx"
#include "BumpMappedPhongShader.hxx"
#include "PointLight.hxx"
#include "TexturedEyeLightShader.hxx"

void RenderFrame(Scene &aScene)
{
	Image img(aScene.camera->resX,aScene.camera->resY); // image array
	Ray ray;                                          // primary ray

	for (int y=0;y<aScene.camera->resY;y++)
		for (int x=0;x<aScene.camera->resX;x++) {
			aScene.camera->InitRay(x+0.5f,y+0.5f,ray); // initialize ray

			//Color values must be clamped between 0 and 1
			img[y][x] = Min(Max(aScene.RayTrace(ray),Vec3f(0,0,0)),Vec3f(1,1,1));
		}

		img.WritePPM("result.ppm"); // write final image
}

int main(int argc, char* argv[])
{
	int exNum = -1;
	if(argc == 2)
		exNum = atol(argv[1]);

	if(exNum == -1)
	{
cmd_usage:
		std::cerr << "Usage: " << argv[0] << "<ex-number>" << std::endl;
		std::cerr << "  <ex-number> = " << std::endl;
		std::cerr << "  1 - for ex.1 with cylinder" << std::endl;
		std::cerr << "  2 - for ex.1 with cone" << std::endl;
		std::cerr << "  3 - for ex.2" << std::endl;
		std::cerr << "  4 - for ex.3" << std::endl;
		exit(1);
	}

	Scene scene(exNum >= 4);

	//////////////////////////////////////////////////////////////////////////
	//Note: the primitive factory for exercise 1
	//////////////////////////////////////////////////////////////////////////
	Scene::SmoothTriangleFactory *smoothTriangleFactory = new Scene::SmoothTriangleFactory;
	smoothTriangleFactory->shader = new EyeLightShader(&scene, Vec3f(1.0,0.5,0.0));

	//////////////////////////////////////////////////////////////////////////
	//Note: A triangle factory for test purposes.
	//////////////////////////////////////////////////////////////////////////
	Scene::TriangleFactory *triangleFactory = new Scene::TriangleFactory;
	triangleFactory->shader = new EyeLightShader(&scene, Vec3f(1.0,0.5,0.0));

	switch(exNum)
	{
		case 1:
			//////////////////////////////////////////////////////////////////////////
			//TODO: Change to smoothTriangleFactory once you get the obj loader working
			//////////////////////////////////////////////////////////////////////////
			scene.ParseOBJ("cylinder16.obj", triangleFactory);
			scene.camera = 
				new PerspectiveCamera(Vec3f(0,0,-0.8f),Vec3f(0,0,1),Vec3f(0,1,0),60,640,480);
			break;
		case 2:
			scene.ParseOBJ("cone32.obj", smoothTriangleFactory);
			scene.camera = 
				new PerspectiveCamera(Vec3f(0,0,-0.8f),Vec3f(0,0,1),Vec3f(0,1,0),60,640,480);
			break;
		case 3:
			{
				Shader *shd;

				shd = new BumpMappedPhongShader(&scene, Vec3f(1,0,0), 0.1f, 0.5f, 0.5f, 40); // red surface
				scene.Add(new Sphere(Vec3f(-2, 1.7f, 0), 2, shd));

				shd = new PhongShader(&scene, Vec3f(0,1,1), 0.1f, 0.5f, 0.5f, 40); // cyan surface
				scene.Add(new Sphere(Vec3f(1, -1, 1), 2.2f, shd));

				shd = new PhongShader(&scene, Vec3f(0,0,1), 0.1f, 0.5f, 0.5f, 40); // blue surface
				scene.Add(new Sphere(Vec3f(3, 0.8f, -2), 2, shd));

				shd = new BumpMappedPhongShader(&scene, Vec3f(1,1,0), 0.1f, 0.5f, 0.5f, 40); // yellow surface
				scene.Add(new InfinitePlane(Vec3f(0, -1, 0), Vec3f(0, 1, 0), shd));


				scene.Add(new PointLight(&scene, Vec3f(-3,5,+4), Vec3f(7,7,7)));
				scene.Add(new PointLight(&scene, Vec3f(0,1,+4), Vec3f(7,7,7)));

				scene.camera = new PerspectiveCamera(Vec3f(0,0,10),Vec3f(0,0,-1), Vec3f(0,1,0),60,640,480);
			}
			break;
		case 4:
			{
				Scene::TexturedSmoothTriangleFactory *texTriangleFactory = new Scene::TexturedSmoothTriangleFactory;
				texTriangleFactory->shader = new TexturedEyeLightShader(&scene, Vec3f(0.5f, 0.5f, 0.5f), "barney.ppm");
				scene.ParseOBJ("barney.obj", texTriangleFactory);
				scene.camera = new PerspectiveCamera(Vec3f(0,8,25),Vec3f(0,0,-1), Vec3f(0,1,0),60,640,480);
			}
			break;
		default:
			goto cmd_usage;
	}

	scene.BuildAccelStructure();
	RenderFrame(scene);
}
