// OgreMaxFix.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <vector>
#include <map>
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <locale>

using namespace std;

class texture_unit {
public:
	texture_unit(){
		texture = "";
		tex_coord_set = "";
		options = "colour_op modulate";
	}
	texture_unit(const texture_unit &t){
		(*this) = t;
	}
	~texture_unit(){}

	void operator=(const texture_unit &t){
		texture = t.texture;
		tex_coord_set = t.tex_coord_set;
		options = t.options;
	}
	string texture;
	string tex_coord_set;
	string options;
};
class pass {
public:
	pass(){
		ambient = "";
		diffuse = "";
		emissive = "";
		specular = "";
	}
	pass(const pass &p){
		(*this) = p;
	}
	~pass(){}

	void operator=(const pass &p){
		ambient = p.ambient;
		specular = p.specular;
		diffuse = p.diffuse;
		emissive = p.emissive;

		units = p.units;
	}
	string ambient;
	string specular;
	string diffuse;
	string emissive;

	vector<texture_unit> units;
};
class technique{
public:
	technique(){}
	~technique(){}

	technique(const technique& t){
		(*this) = t;
	}
	void operator=(const technique &t){
		passes = t.passes;
	}
	vector<pass> passes;
};
class material{
public:
	material(){name = "";}
	~material(){}

	material(const material& m){
		(*this) = m;
	}
	void operator=(const material &m){
		name = m.name;
		image = m.image;
		techniques = m.techniques;
	}

	string name;
	string image;
	vector<technique> techniques;
};
void uae(){
	printf("OgreMaxFix <input material> <image lookup> <output material>\n");
	exit(1);
}

vector<material> materials;
int matID = -1;
int techID = -1;
int passID = -1;
int textID = -1;

void parse(const string &name);
void parse_material(ifstream &stream, string oldline);
void parse_pass(ifstream &stream, string oldline);
void parse_technique(ifstream &stream, string oldline);
void parse_texture_unit(ifstream &stream, string oldline);

void write(const string &name);

void Tokenize(const string& str,vector<string>& tokens,const string& delimiters=" ");
void ltrim(std::string& str, const std::locale& loc = std::locale());

typedef vector<string> Tokens;

int main(int argc, char* argv[])
{
	if(argc != 4)uae();

	parse(argv[1]);
	write(argv[3]);
	return 0;
}

void write_new(const string &name){
	ofstream file(name.c_str());

	if(file.is_open()){
		for(vector<material>::iterator mat = materials.begin(); mat != materials.end(); mat++){
			///First check if this node follows .scene conventions
			if((*mat).techniques.size() == 2 &&
			   (*mat).techniques[0].passes.size() == 1 &&
			   (*mat).techniques[1].passes.size() == 1 &&
			   (*mat).techniques[0].passes[0].units.size() == 1 &&
			   (*mat).techniques[1].passes[0].units.size() == 1)
			{
				printf("  - material %s follows conventions\n",(*mat).name.c_str());
				file << (*mat).name.c_str() << "\n";
				file << "{\n";
				file << "     technique\n";
				file << "     {\n";
				file << "          pass\n";
				file << "          {\n";
				file << "               ambient 1 1 1\n";
				file << "               diffuse 1.0 1.0 1.0\n";
				file << "               specular 1.0 1.0 1.0 9800.0\n";
				file << "               emissive 0.0 0.0 0.0 1\n";
				file << "               texture_unit\n";
				file << "               {\n";
				file << "                    texture " << (*mat).image.c_str() << "\n";
				file << "                    tex_coord_set 0\n";
				file << "                    colour_op modulate\n";
				file << "               }\n";
				file << "          }\n";
				file << "          pass\n";
				file << "          {\n";
				file << "               scene_blend modulate\n";
				file << "               texture_unit\n";
				file << "               {\n";
				file << "                    " << (*mat).techniques[1].passes[0].units[0].texture.c_str() << "\n";
				file << "                    tex_coord_set 1\n";
				file << "                    colour_op modulate\n";
				file << "               }\n";
				file << "          }\n";
				file << "     }\n";
				file << "}\n\n";

			} else {
				printf("  - Warning: material %s doesn't follow conventions, skipping\n",(*mat).name.c_str());
			}
		/*	file << (*mat).name.c_str() << "\n";
			
			//file << "Image: " << (*mat).image.c_str() << "\n" ;
			for(vector<technique>::iterator tech = (*mat).techniques.begin(); tech != (*mat).techniques.end(); tech++){
				file << "     technique\n";
				file << "     {\n";
				int passcount = 0;
				for(vector<pass>::iterator pass = (*tech).passes.begin(); pass != (*tech).passes.end(); pass++){
					file << "          pass\n";
					file << "          {\n";
					file << "               " << (*pass).ambient << "\n";
					file << "               " << (*pass).diffuse << "\n";
					file << "               " << (*pass).specular << "\n";
					file << "               " << (*pass).emissive << "\n";
					
					for(vector<texture_unit>::iterator tu = (*pass).units.begin(); tu != (*pass).units.end(); tu++){
						if(passcount >= 1)file << "                scene_blend modulate\n";
						file << "                texture_unit\n";
						file << "                {\n";
						file << "                     " << (*tu).texture << "\n";
						file << "                     " << (*tu).tex_coord_set << "\n";
						file << "                     " << (*tu).options << "\n";
						file << "                }\n";
					}
					file << "          }\n";
				}

				file << "     }\n";
			}
			file << "}\n";*/
		}
		file.close();
	} else {
		printf("  Error opening %s for writing\n",name.c_str());
	}
}
void write(const string &name){
	write_new(name);
	return;

	ofstream file(name.c_str());

	if(file.is_open()){
		for(vector<material>::iterator mat = materials.begin(); mat != materials.end(); mat++){
			file << (*mat).name.c_str() << "\n";
			file << "{\n";
			for(vector<technique>::iterator tech = (*mat).techniques.begin(); tech != (*mat).techniques.end(); tech++){
				file << "     technique\n";
				file << "     {\n";
				int passcount = 0;
				for(vector<pass>::iterator pass = (*tech).passes.begin(); pass != (*tech).passes.end(); pass++){
					file << "          pass\n";
					file << "          {\n";
					file << "               " << (*pass).ambient << "\n";
					file << "               " << (*pass).diffuse << "\n";
					file << "               " << (*pass).specular << "\n";
					file << "               " << (*pass).emissive << "\n";
					
					for(vector<texture_unit>::iterator tu = (*pass).units.begin(); tu != (*pass).units.end(); tu++){
						if(passcount >= 1)file << "                scene_blend modulate\n";
						file << "                texture_unit\n";
						file << "                {\n";
						file << "                     " << (*tu).texture << "\n";
						file << "                     " << (*tu).tex_coord_set << "\n";
						file << "                     " << (*tu).options << "\n";
						file << "                }\n";
					}
					file << "          }\n";
				}

				file << "     }\n";
			}
			file << "}\n";
		}
		file.close();
	} else {
		printf("  Error opening %s for writing\n",name.c_str());
	}
}

void parse(const string &name){
	ifstream file(name.c_str());

	if(file.is_open()){
		while(!file.eof()){
			string line;
			Tokens tok;
			getline(file,line);
		
			ltrim(line);		
			Tokenize(line,tok," ");
			if(tok.size() > 0){
				string id = tok[0];
				if(id.compare("material")==0){
					parse_material(file,line);
				}else if(id.compare("}")==0){
					return;
				}
			}
		}
		file.close();
	} else {
		printf("  - Error, couldn't open %s for reading\n",name.c_str());
	}
	
}

void parse_material(ifstream &file, string oldline){
	material tmp;
	tmp.name = oldline;

	{	
		Tokens tok;
		Tokenize(oldline,tok," ");
		if(tok.size() >= 2){
			///Original image name should be first part of material name
			size_t pos = tok[1].find("_SG");
			if(pos != tok[1].npos){
				tmp.image = tok[1].substr(0,pos) + ".bmp";
			}
		}
	}
	materials.push_back(tmp);
	matID++;
	techID = -1;

	printf("adding material...");

	if(file.is_open()){
		while(!file.eof()){
			string line,nline;
			Tokens tok;
			getline(file,line);
			ltrim(line);
			
			Tokenize(line,tok," ");
			if(tok.size() > 0){
				string id = tok[0];
				if(id.compare("technique")==0){
					parse_technique(file,line);
				} else if(id.compare("}")==0){
					printf("Complete.\n");
					return;
				}
			}
		}
	} else {
		printf("Failed!\n");
		return;
	}
	printf("Complete.\n");
	return;
}
void parse_technique(ifstream &file, string oldline){
	technique tmp;
	materials[matID].techniques.push_back(tmp);
	printf("technique[%d]...",techID+1);
	techID++;
	passID = -1;
	if(file.is_open()){
		while(!file.eof()){
			string line,nline;
			Tokens tok;
			getline(file,line);
			ltrim(line);
			Tokenize(line,tok," ");
			if(tok.size() > 0){
				string id = tok[0];
				if(id.compare("pass")==0){
					parse_pass(file,line);
				} else if(id.compare("}")==0){
					printf("(/technique)...");
					return;
				}
			}
		}
	} else {
		printf("failed...\n");
		return;
	}
	printf("(/technique)...\n");
	return;
}
void parse_pass(ifstream &file, string oldline){
	pass tmp;
	materials[matID].techniques[techID].passes.push_back(tmp);
	printf("pass[%d]...",passID+1);
	passID++;
	textID = -1;	
	
	if(file.is_open()){
		while(!file.eof()){
			string line,nline;
			Tokens tok;
			getline(file,line);
			ltrim(line);
			
			Tokenize(line,tok," ");
			if(tok.size() > 0){
				string id = tok[0];
				if(id.compare("texture_unit")==0){
					parse_texture_unit(file,line);
				} else if(id.compare("ambient")==0){
					materials[matID].techniques[techID].passes[passID].ambient = line;
				} else if(id.compare("specular")==0){
					materials[matID].techniques[techID].passes[passID].specular = line;
				} else if(id.compare("diffuse")==0){
					materials[matID].techniques[techID].passes[passID].diffuse = line;
				} else if(id.compare("}")==0){
					printf("(/pass)...");
					return;
				}
			}
		}
	} else {
		printf("failed...\n");
		return;
	}
	printf("(/pass)...\n");
	return;
}
void parse_texture_unit(ifstream &file, string oldline){
	texture_unit tmp;
	materials[matID].techniques[techID].passes[passID].units.push_back(tmp);
	printf("texture_unit[%d]...",textID+1);
	textID++;

	if(file.is_open()){
		while(!file.eof()){
			string line,nline;
			Tokens tok;
			getline(file,line);
			ltrim(line);
			
			Tokenize(line,tok," ");
			if(tok.size() > 0){
				string id = tok[0];
				if(id.compare("texture")==0){
					materials[matID].techniques[techID].passes[passID].units[textID].texture = line;
				} else if(id.compare("tex_coord_set")==0){
					materials[matID].techniques[techID].passes[passID].units[textID].tex_coord_set = line;
				} else if(id.compare("}")==0){
					printf("(/texture_unit)...");
					return;
				}
			}
		}
	} else {
		printf("failed...\n");
		return;
	}
	printf("(/texture_unit)...\n");
	return;
}
void ltrim(string& str, const locale& loc) {
  string::size_type pos = 0;
  while (pos < str.size() && isspace(str[pos], loc)) pos++;
  str.erase(0, pos);
}

void Tokenize(const string& str,
                      vector<string>& tokens,
                      const string& delimiters)
{
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}


