/* Parser.cpp
 *
 * Definition of the Parser class.
 */

#include "Parser.h"
#include "Error.h"
#include <iostream>
#include <cassert>

using namespace std;

Parser::Parser(void)
	: loadMe_(NULL),
	  verbose_(0) // default to quiet
{
	// Nothing to do
}

Parser::~Parser(void)
{
	// Nothing to do
}

void Parser::setVerbosity(int verbose)
{
	verbose_ = verbose;
	if (verbose_ < 0)
		verbose_ = 0;
	if (verbose_ > MAX_VERBOSE)
		verbose_ = MAX_VERBOSE;
	reportVerbosity();
}
void Parser::cycleVerbosity()
{
	++verbose_;
	if (verbose_ > MAX_VERBOSE)
		verbose_ = 0;
	reportVerbosity();
}

void Parser::reportVerbosity()
{
	cout << "Verbose = " << verbose_ << ": ";
	switch(verbose_)
	{
	case 0:
		cout << "No parsing output\n";
		break;
	case 1:
		cout << "Only extra commands reported\n";
		break;
	case 2:
		cout << "Everything reported\n";
		break;
	default:
		cout << "***ERROR: Verbosity at " << verbose_ << endl;
		break;
	}
}

/*
 * loadFile()
 */
void Parser::loadFile(World* pWorld)
{
	string cmd;
	bool notDone = true;

	// put world in its proper place
	loadMe_ = pWorld;
	assert(loadMe_ != NULL);

	// get filename
	while(notDone)
	{
		notDone = false; // only do one command unless otherwise specified

		cout << "Please enter filename of input file.\n"
			 << "\tnone   - Oops, I don't want to mess up my beautiful world!\n"
			 << "\tcin    - Accept input from console instead of file\n"
			 << "\tformat - Display acceptable input file format\n"
			 << "-> ";
		cin >> cmd;

		if(cmd == "none" || cmd == "n")
		{
			// do nothing
		}
		else if(cmd == "format" || cmd == "f")
		{
			cout << "The format of the input file is as follows:\n\n"
				 << "\tN\n"
				 << "\tx0   y0   z0   r0   g0   b0\n"
				 << "\tx1   y1   z1   r1   g1   b1\n"
				 << "\t...\n"
				 << "\txN-1   yN-1   zN-1   rN-1   gN-1   bN-1\n"
				 << "\tM\n"
				 << "\tu0   v0   w0\n"
				 << "\tu1   v1   w1\n"
				 << "\t...\n"
				 << "\tuM-1   vM-1   wM-1\n"
				 << "\n"
				 << "N is the number of vertices, M is the number of triangles, x_i, y_i,\n"
				 << "z_i are floats representing the coordinates of vertex i, r_i, g_i, b_i\n"
				 << "is the color of vertex i, and u_j, v_j, w_j are integers representing\n"
				 << "indices in the vertex list of the vertices in triangle j as they\n"
				 << "appear from the front of the triangle in counter-clockwise order.\n"
				 << "\n"
				 << "Additional cmds (must come after the normal format):\n"
				 << "\tball x y z\t- moves the ball to (x,y,z)\n"
				 << "\tvel x y z\t- sets ball's velocity to (x,y,z)\n"
				 << "\tlight on/off\t- turns on or off shading\n"
				 << "\n";
			notDone = true;
		}
		else if(cmd == "help" || cmd == "h")
		{
			notDone = true;
		}
		else if(cmd == "cin")
		{
			// Make sure we start with an empty obstructions_ vector
			loadMe_->destroyWorld();
		
			int error = parse(cin);
			if (error)
			{
				notDone = true;
			}
			meshCleanUp();
		}
		else // interpret cmd as a filename
		{
			int error = getFile(cmd, NULL);
			if (error)
			{
				notDone = true;
				continue;
			}
		}
	}
}

int Parser::getFile(const string& filename, World* pWorld)
{
	int error;

	// put world in its proper place
	if (loadMe_ == NULL)
		loadMe_ = pWorld;
	assert(loadMe_ != NULL);

	// get file
	fstream input(filename.c_str(), ios_base::in);

	// make sure input stream is good
	if (!(input))
	{
		printf("***ERROR: Invalid filename: %s\n", filename.c_str());
		return BADFILE;
	}

	// Make sure we start with an empty obstructions_ vector
	loadMe_->destroyWorld();

	error = parse(input);
	meshCleanUp();
	input.close();

	return error;
}

/*
 * parse()
 *
 * This populates a world's obsructions_ vector based on an input stream.
 */
int Parser::parse(istream& input)
{
	int nVert; // Number of vertices taken in
	int nTri;  // Number of trianlges taken in
	double x,y,z;
	double r,g,b;
	int i1,i2,i3;
	vector<Vertex*> vertices;

	// Take in vertices
	input >> nVert;
	for (int i = 0; i < nVert; ++i)
	{
		input >> x >> y >> z >> r >> g >> b;
		if (verbose_ > 1)
			printf("Vertex %d: (%.2f,%.2f,%.2f) r=%.2f g=%.2f b=%.2f\n", i,x,y,z,r,g,b);

		// Make sure the colors are valid
		if (r < 0 || r > 1 ||
			g < 0 || r > 1 ||
			b < 0 || b > 1)
		{
			printf("***ERROR: Only colors between 0 and 1 are allowed!\n\tOffending Vertex: \"%.2f %.2f %.2f r=%.2f g=%.2f b=%.2f\"\n",
					x,y,z,r,g,b);
			return BADCOLOR;
		}

		vertices.push_back(loadMe_->addVertex(Tuple(x,y,z), Tuple(r,g,b)));
	}

	// Take in Triangles and produce both Triangles and Edges.
	// i1,i2,i3 are the indices of the vertices that will make up
	// the given triangle.
	input >> nTri;
	for (int i = 0; i < nTri; ++i)
	{
		input >> i1 >> i2 >> i3;

		// Make sure that the indices given are valid
		// (We should probably do this differently than assert
		//  statments - like return an error)
		if (i1 < 0 || i1 >= nVert || i1 >= (int)loadMe_->obstructions_.size() ||
			i2 < 0 || i2 >= nVert || i2 >= (int)loadMe_->obstructions_.size() ||
			i3 < 0 || i3 >= nVert || i3 >= (int)loadMe_->obstructions_.size())
		{
			printf("***ERROR: Invalid Index in Triangle: \"%d %d %d\"", i1, i2, i3);
			return BADINDEX;
		}

		// Get the vertices that have been specified by index
		Vertex* v1 = vertices[i1];
		Vertex* v2 = vertices[i2];
		Vertex* v3 = vertices[i3];

		// Make sure the Vertices are non-null
		if (v1 == NULL || v2 == NULL || v3 == NULL)
		{
			printf("***ERROR: Null pointer vertex in Triangle: \"%d %d %d\"", i1, i2, i3);
			return GENERROR;
		}

		// Report Triangle
		if (verbose_ > 1)
		{
			cout << "Triangle " << i << ": "
				 << v1->v() << " "
				 << v2->v() << " "
				 << v3->v() << endl;
		}

		// Make the Triangle
		Triangle* temp = loadMe_->addTriangle(v1,v2,v3);
		curMesh_.push_back(temp);

		// Update the average normals of its verticies
		Tuple normal = temp->calcNormal(Tuple());
		v1->addNormal(normal);
		v2->addNormal(normal);
		v3->addNormal(normal);

		// Make the 3 corresponding Edges
		loadMe_->addEdge(v1,v2);
		loadMe_->addEdge(v1,v3);
		loadMe_->addEdge(v2,v3);
	}

	// parse the rest
	return parseExtra(input);
}


/*
 * parseExtra()
 *
 * Parses anything left in the stream after parse is done
 */
int Parser::parseExtra(istream& input)
{
	string extra;

	do
	{
		extra = "";
		input >> extra;
		if (verbose_ > 0)
			printf("Additional Cmd: \"%s\"\n", extra.c_str());

		// Insert new commands in here
		if (extra == "ball" || extra == "Ball" || extra == "BALL")
		{
			double x,y,z;
			input >> x >> y >> z;
			if (verbose_ > 0)
				printf("\tMove to (%.2f,%.2f,%.2f)\n",x,y,z);
			loadMe_->ball_.moveTo(Tuple(x,y,z));
		}
		else if (extra == "vel" || extra == "Vel" || extra == "VEL")
		{
			double x,y,z;
			input >> x >> y >> z;
			if (verbose_ > 0)
				printf("\tAccel to (%.2f,%.2f,%.2f)\n",x,y,z);
			loadMe_->ball_.accelTo(Tuple(x,y,z));
		}
		else if (extra == "newmesh" || extra == "newMesh" || 
				 extra == "NewMesh" || extra == "NEWMESH")
		{
			meshCleanUp();
			parse(input);
		}
		else if (extra == "meshcolor" || extra == "meshColor" ||
				 extra == "MeshColor" || extra == "MESHCOLOR")
		{
			// Get colors: ambient, diffuse, specular, emissive (in order)
			vector <Tuple> colors;
			double r,g,b;
			for (int i = 0; i < NUM_OF_COLORS; ++i)
			{
				input >> r >> g >> b;
				colors.push_back(Tuple(r,g,b));
			}

			if (verbose_ > 0)
			{
				cout << "\tMeshColor:";
				for (int color = 0; color < (int)colors.size(); ++color)
				{
					cout << "  " << colors[color];
				}
				cout << endl;
			}

			// Apply colors to mesh
			for (int i = 0; i < (int)curMesh_.size(); ++i)
			{
				curMesh_[i]->setColors(colors);
				if (verbose_ > 1)
				{
					cout << "\tColorified triangle " << i << ": "; 
					curMesh_[i]->print();
				}
			}
			colors.clear(); //////
		}
		else if (extra == "hole" || extra == "Hole" || extra == "HOLE")
		{
			// Make all of mesh into hole
			for (int i = 0; i < (int)curMesh_.size(); ++i)
			{
				curMesh_[i]->setIsHole(true);
				if (verbose_ > 1)
				{
					cout << "\tHoleified triangle " << i << ": ";
					curMesh_[i]->print();
				}
			}
		}
		else if (extra == "light" || extra == "Light" || extra == "LIGHT")
		{
			string choice;
			input >> choice;
			if (choice == "off" || choice == "Off" || choice == "OFF" || choice == "0")
			{
				if (verbose_ > 0)
					printf("\tTurning OFF lights\n");
				loadMe_->lightOff();
			}
			else if (choice == "on" || choice == "On" || choice == "ON" || choice == "1")
			{
				if (verbose_ > 0)
					printf("\tTurning ON lights\n");
				loadMe_->lightOn();
			}
			else
			{
				printf("***WARNING: Bad value for light on/off: \"%s\"\n\tIgnoring light change request.\n",
						choice.c_str());
			}
		}
		else if (extra == "worldbottom" || extra == "worldBottom" ||
				 extra == "WorldBottom" || extra == "WORLDBOTTOM")
		{
			double newBottom;
			input >> newBottom;
			if (verbose_ > 0)
				printf("New bottom of world = %f\n", newBottom);
			loadMe_->setBottom(newBottom);
		}
		else
		{
			if (extra != "")
				printf("***WARNING: Unknown command \"%s\"\n", extra.c_str());
		}
	} while (extra != "" && input);


	return NOERROR;
}

void Parser::meshCleanUp()
{
	curMesh_.clear();
}

