// ===================================================================
//
// OBJrader.h
// Reads OBJ file
//
// Initial coding by Martin Ruzicka

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <sstream>
#include <ctype.h>
#include <vector>
using namespace std;

#include "AbstractModelReader.h"

#ifndef OBJ_READER_H
#define OBJ_READER_H

const string OBJ_VCOORD_TAG="v";
const string OBJ_TCOORD_TAG="vt";
const string OBJ_NCOORD_TAG="vn";
const string OBJ_FCOORD_TAG="f";
const string OBJ_GROUP_TAG="g";
const string OBJ_S_TAG="s";
const string OBJ_USEMTL_TAG="usemtl";


const char OBJ_VERTEX_DELIMER= ' ';
const char OBJ_INDICES_DELIMER='/';
const char SPACE=' ';
const char MINUS='-';


/// OBJ parser which reads data from given file
template< typename Storage>
class ObjReader:public ModelReader<Storage>
{
private:
	string file_buffer;

	bool parseNextVcoord(const string &line, float dst_vcoord[3]);
	bool parseNextTcoord(const string &line, float dst_tcoord[3]);
	bool parseNextNcoord(const string &line, float dst_ncoord[3]);
	bool parseNextFcoord(const string &line, vector<int>& dst_fvcoord, vector<int>& dst_ftcoord,vector<int>& dst_fncoord);

public:
	ObjReader(const string &fileName);

	/**<  Reads from file geometry data group (~ 1 mesh group) and return int throught the param.
	If the parser is on the end of file then ratursn FALSE, otherwise returns TRUE */
	virtual bool readNextGroup(Storage& dst_storage);
};

template< typename Storage> ObjReader<Storage>::ObjReader(const string &fileName):ModelReader(fileName)
{
}

template< typename Storage> bool ObjReader<Storage>::readNextGroup(Storage& dst_storage)
{
	//hledam nejaky znamy TAG kde se chytnu a zacnu s parsovanim
	while (this->file_buffer!=OBJ_GROUP_TAG && this->file_buffer!=OBJ_VCOORD_TAG &&
			this->file_buffer!=OBJ_TCOORD_TAG && this->file_buffer!=OBJ_NCOORD_TAG && 
			this->file_buffer!=OBJ_FCOORD_TAG && this->file_buffer!=OBJ_USEMTL_TAG && !file.eof())
	{
		file>>this->file_buffer;
	}

	if (file.eof())
	{
		return false;
	}

	//nasel sem group tag
	if (this->file_buffer==OBJ_GROUP_TAG)
	{
		file>>file_buffer;
		dst_storage.setGroupName(file_buffer);
	}
	
	do
	{

		string line;
		float vcoord[3];

		if (file_buffer==OBJ_VCOORD_TAG)
		{
			getline(file,line);
			parseNextVcoord(line,vcoord);

			dst_storage.pushVcoord(vcoord[0], vcoord[1], vcoord[2]);		
		}

		float tcoord[3];
		if (file_buffer==OBJ_TCOORD_TAG)
		{
			getline(file,line);
			parseNextTcoord(line,tcoord);

			dst_storage.pushTcoord(tcoord[0], tcoord[1]);
		}
	

		float ncoord[4];
		if (file_buffer==OBJ_NCOORD_TAG)
		{
			getline(file,line);
			parseNextNcoord(line,ncoord);

			dst_storage.pushNcoord(ncoord[0], ncoord[1], ncoord[2]);
		}
	
		vector<int> fvcoord,ftcoord,fncoord;

		if (file_buffer==OBJ_FCOORD_TAG )
		{
			getline(file,line);
			parseNextFcoord(line,fvcoord,ftcoord,fncoord);
			dst_storage.pushFcoord(fvcoord,ftcoord,fncoord);
		}

		if (file_buffer==OBJ_USEMTL_TAG )
		{
			if ( dst_storage.hasMaterialName() )
			{
				//group has already material -> close this group ( and create new group in next loop )
				return !file.eof();
			}

			string material;
			file>>material;
			dst_storage.setMaterialName(material);
			
		}
		
		file>>file_buffer;
	}
	while (this->file_buffer!=OBJ_GROUP_TAG && !file.eof());
	
	return !file.eof();
}

template< typename Storage> bool ObjReader<Storage>::parseNextVcoord(const string &line, float dst_vcoord[3])
{
	//JAK TO VYPADA V SOUBORU: 
	//v 6.170669555664063 1.918527364730835 -5.971015163114851e-017  (line obsahuje VSE mimo 'v' )

	int startPos1= line.find_first_not_of(OBJ_VERTEX_DELIMER,1);	//zjistim kde konci whitespace a zacina cislo
	int delimer1=line.find(OBJ_VERTEX_DELIMER,startPos1);			//zjistim kde konci cislo a zacina whitespace

	int startPos2= line.find_first_not_of(OBJ_VERTEX_DELIMER,delimer1+1);
	int delimer2=line.find(OBJ_VERTEX_DELIMER,startPos2);

	int startPos3= line.find_first_not_of(OBJ_VERTEX_DELIMER,delimer2+1);
	int delimer3=line.find(OBJ_VERTEX_DELIMER,startPos3);

	string v_x=line.substr(startPos1,delimer1-startPos1);	//vezmu 1. cislo	(=x)
	string v_y=line.substr(startPos2,delimer2-startPos2);	//vezmu 2. cislo	(=y)
	string v_z=line.substr(startPos3,delimer3-startPos3);	//vezmu 3. cislo	(=z);
	
	
	dst_vcoord[0]= strtod(v_x.c_str(),NULL);
	dst_vcoord[1]= strtod(v_y.c_str(),NULL);
	dst_vcoord[2]= strtod(v_z.c_str(),NULL);

	return true;
}
template< typename Storage> bool ObjReader<Storage>::parseNextTcoord(const string &line, float dst_tcoord[2])
{
	//JAK TO VYPADA V SOUBORU: 
	//vt 0.7887409925460815 0.04024564847350121  (line obsahuje VSE mimo 'vt' )

	int delimer1=line.find(OBJ_VERTEX_DELIMER,1); //od pozice 1 protoze na pozici 0 je mezera kterou nechci 
	int delimer2=line.find(OBJ_VERTEX_DELIMER,delimer1+1);

	string t_x=line.substr(1,delimer1-1);
	string t_y=line.substr(delimer1,delimer2-delimer1-1);
	
	dst_tcoord[0]= strtod(t_x.c_str(),NULL);
	dst_tcoord[1]= strtod(t_y.c_str(),NULL);

	return true;
}

template< typename Storage> bool ObjReader<Storage>::parseNextNcoord(const string &line, float dst_ncoord[3])
{
	//JAK TO VYPADA V SOUBORU: 
	//vn -0.9092200994491577 0.0002903409767895937 0.4163155853748322  (line obsahuje VSE mimo 'vn' )

	int delimer1=line.find(OBJ_VERTEX_DELIMER,1); //od pozice 1 protoze na pozici 0 je mezera kterou nechci 
	int delimer2=line.find(OBJ_VERTEX_DELIMER,delimer1+1);

	string n_x=line.substr(1,delimer1-1);
	string n_y=line.substr(delimer1,delimer2-delimer1-1);
	string n_z=line.substr(delimer2+1,line.size()-delimer2-1);
	
	
	dst_ncoord[0]= strtod(n_x.c_str(),NULL);
	dst_ncoord[1]= strtod(n_y.c_str(),NULL);
	dst_ncoord[2]= strtod(n_z.c_str(),NULL);

	return true;
}

template< typename Storage> bool ObjReader<Storage>::parseNextFcoord(	const string &line, vector<int>& dst_fvcoord, 
																		vector<int>& dst_ftcoord,vector<int>& dst_fncoord)
{
	//JAK TO VYPADA V SOUBORU: 
	//f  v1/vt1/vn1   v2/vt2/vn2   v3/vt3/vn3  ...   (nacteno vse krome 'f')

	dst_fvcoord.clear();
	dst_ftcoord.clear();
	dst_fncoord.clear();

	string tmp_string;

	for (unsigned int i=1;i<line.length();++i)
	{
		if (!isspace(line[i]) )
		{
			tmp_string+=line[i];
		}
		
		if ( isspace(line[i]) || i==line.length()-1)
		{
			int delimer1=tmp_string.find(OBJ_INDICES_DELIMER);
			int delimer2=tmp_string.find(OBJ_INDICES_DELIMER,delimer1+1);

			string vcoord=tmp_string.substr(0,delimer1);
			string tcoord=tmp_string.substr(delimer1+1,delimer2-delimer1-1);
			string ncoord;

			if (delimer2<tmp_string.size())
			{
				//kontrola pripadu 'x/y/' 
				ncoord=tmp_string.substr(delimer2+1,tmp_string.size()-delimer2-1);
			}

			dst_fvcoord.push_back( atoi( vcoord.c_str()));

			if (!tcoord.empty())
			{
				dst_ftcoord.push_back( atoi( tcoord.c_str()));
			}

			if (!ncoord.empty())
			{
				dst_fncoord.push_back( atoi( ncoord.c_str()));
			}
			
			tmp_string.clear();

		}
	}

	return true;
}



void pokus_render(void);

#endif