#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <locale>
#include <cstdio>

using namespace std;

// This was done due to the format of 3DMFs, and their tendancy to use OS9 antics.
void customGetline(ifstream& in, string& line)
{
	line.clear();
	
	while(in.good())
	{
		int c = in.get();
		if(c == '\r' || c == '\n' || c == EOF)
		{
			break;
		}
		line += (char)c;
	}
}

// strip leading and trailing spaces
string stripLine(const string& line)
{
	int a = INT_MAX, b = INT_MIN;
	for(int i = 0; i < (int)line.length(); ++i)
	{
		if(!isspace(line[i]))
		{
			a = min(a, i);
			b = max(b, i);
		}
	}
	
	if(b < 0)
	{
		// no non-whitespace chars found -- return empty string
		a = 0;
		b = -1;
	}
	
	return line.substr(a, b-a+1);
}

struct Vertex
{
	float x, y, z;
	Vertex(float x, float y, float z) : x(x), y(y), z(z) {}
};

struct Triangle
{
	int v1, v2, v3;
	Triangle(int v1, int v2, int v3) : v1(v1), v2(v2), v3(v3) {}
};

struct UVCoord
{
	float u, v;
	UVCoord(float u, float v) : u(u), v(v) {}
};

struct Mesh
{
	vector<Vertex> vertices;
	vector<Triangle> triangles;
	vector<UVCoord> uvcoords;
};

struct Model
{
	vector<Mesh> meshes;
};

struct ParseState
{
	enum Type
	{
		kUnknown,
		kTriMeshHeader,
		kTriMeshTriangle,
		kTriMeshVertex,
		kTriMeshUVCoord,
		kTextureHeaders,
		kTextureParser
	};
	
	const char* stateName(Type type)
	{
		switch(type)
		{
			case kTriMeshHeader: return "TriMeshHeader";
			case kTriMeshTriangle: return "TriMeshTriangle";
			case kTriMeshVertex: return "TriMeshVertex";
			case kTriMeshUVCoord: return "TriMeshUVCoord";
			case kTextureHeaders: return "TextureHeaders";
			case kTextureParser: return "TextureParser";
			case kUnknown:
			default:
				return "Unknown";
		}
	}
	
	Model& model;
	Mesh mesh;
	int lineNumber;
	int arrayCounter;
	Type expected;
	
	bool gotTriMeshHeader;
	struct TriMeshHeader{
		int numTriangles;
		int unknown2;
		int unknown3;
		int unknown4;
		int numVertices;
		int unknown6;
		int numUVCoords;
	} triMeshHeader;
	
	struct TextureHeaders{
		int unknown1;
		int unknown2;
		int unknown3;
		int unknown4;
		int unknown5;
	} textureHeaders;
	
	ParseState(Model& model)
	: model(model)
	, lineNumber(0)
	, expected(kUnknown)
	, gotTriMeshHeader(false) {}
};

bool parseLine(ParseState& parse, const string& line)
{
	parse.lineNumber++;
	
	// If (the line is empty, or is a comment)
	if(line.length() == 0 || line[0] == '#')
	{
		return true;
	}
		
		switch(parse.expected)
		{
			case ParseState::kUnknown:
				if(line.find("TriMesh") != string::npos)
				{
					if(parse.gotTriMeshHeader)
					{
						// two TriMeshes ???
						return false;
					}
					parse.expected = ParseState::kTriMeshHeader;
				}
				else
				{
					/// ...
				}
				break;
			case ParseState::kTriMeshHeader:
			{
				ParseState::TriMeshHeader& header = parse.triMeshHeader;
				if(6 != sscanf(line.c_str(), "%d %d %d %d %d %d",
							   &header.numTriangles, &header.unknown2, &header.unknown3,
							   &header.unknown4, &header.numVertices, &header.unknown6))
				{
					return false;
				}
				parse.gotTriMeshHeader = true;
			}
				parse.expected = ParseState::kTriMeshTriangle;
				break;
			case ParseState::kTriMeshTriangle:
			{
				int v1, v2, v3;
				if(3 != sscanf(line.c_str(), "%d %d %d", &v1, &v2, &v3))
				{
					return false;
				}
				parse.mesh.triangles.push_back(Triangle(v1, v2, v3));
				if(parse.mesh.triangles.size() == parse.triMeshHeader.numTriangles)
				{
					// vertices come after triangles
					parse.expected = ParseState::kTriMeshVertex;
				}
			}
				break;
			case ParseState::kTriMeshVertex:
			{
				float x, y, z;
				if(3 != sscanf(line.c_str(), "%f %f %f", &x, &y, &z))
				{
					return false;
				}
				parse.mesh.vertices.push_back(Vertex(x, y, z));
				if(parse.mesh.vertices.size() == parse.triMeshHeader.numVertices)
				{
					parse.expected = ParseState::kTextureHeaders;
				}
				
			}
				break;
			case ParseState::kTextureHeaders:
				if(line.find("AttributeArray") != string::npos)
				{
					ParseState::TextureHeaders& header = parse.textureHeaders;
					if(parse.arrayCounter == 0)
					{
					if(5 != sscanf(line.c_str(), "AttributeArray ( %d %d %d %d %d",
								   &header.unknown1, &header.unknown2,
								   &header.unknown3, &header.unknown4, &header.unknown5))
					{
						return false;
					}
					}
					if(parse.arrayCounter > 2)
					{
						// Three texture headers???
						return false;
					}
					parse.arrayCounter++;
					if (parse.arrayCounter == 2) 
					{							
						parse.expected = ParseState::kTriMeshUVCoord;
					}
					
				}
				break;				
			case ParseState::kTriMeshUVCoord:
			{
				float u, v;
				if(2 != sscanf(line.c_str(), "%f %f", &u, &v))
				{
					return false;
				}
				parse.mesh.uvcoords.push_back(UVCoord(u, v));
				if(parse.mesh.uvcoords.size() == parse.triMeshHeader.numVertices)
				{
					parse.expected = ParseState::kUnknown;
					parse.gotTriMeshHeader = false;
					parse.model.meshes.push_back(parse.mesh);
					parse.mesh.vertices.clear();
					parse.mesh.triangles.clear();
					parse.mesh.uvcoords.clear();
					parse.arrayCounter = 0;
				}
				
			}
				break;
		}
		
		return true;
	}

int main(int argc, char **argv)
{
	if(argc != 2)
	{
		printf("Usage: %s [file]\n", argv[0]);
	}
	
	ifstream fin;
	fin.open(argv[1]);
	
	if(fin.fail())
	{
		printf("Input file failed to open.\n");
		return 0;
	}
	
	Model model;
	ParseState parse(model);
	bool success = true;
	
	string line;
	do
	{
		customGetline(fin, line);
		string stripped = stripLine(line);
		if(!parseLine(parse, stripped))
		{
			printf("Parse failure at line %d in state %s\n", parse.lineNumber, parse.stateName(parse.expected));
			printf("Line content: %s\n", line.c_str());
			success = false;
		}
	} while(success && fin.good());
	
	if(success)
	{
		for (int i = 0;i < model.meshes.size();i++)
		{
			Mesh& mesh = model.meshes[i];
			
			printf("Read %d triangles, %d vertices and %d UV Coordinates.\n", (int)mesh.triangles.size(), (int)mesh.vertices.size(), (int)mesh.uvcoords.size());
			
			printf("Triangles:\n");
		
			vector<Triangle>& ts = mesh.triangles;
			for(size_t i = 0; i < ts.size(); ++i)
			{
				Triangle& t = ts[i];
				printf("%d %d %d\n", t.v1, t.v2, t.v3);
			}
		
			printf("Vertices:\n");
		
			vector<Vertex>& vs = mesh.vertices;
			for(size_t i = 0; i < vs.size(); ++i)
			{
				Vertex& v = vs[i];
				printf("%.3f %.3f %.3f\n", v.x, v.y, v.z);
			}
			
			printf("UVCoordinates:\n");
			
			vector<UVCoord>& uvs = mesh.uvcoords;
			for(size_t i = 0; i < uvs.size(); ++i)
			{
				UVCoord& uv = uvs[i];
				printf("%f %f\n", uv.u, uv.v);
			}
		}
	}
	
	fin.close();
}