/* This file is part of the source code for the following publication:
 * Assembling Self-Supporting Structures, Deuss et al., SIGGRAPH Asia 2014
 *
 * Copyright (C) 2014 Mario Deuss <mario.deuss@epfl.ch>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Loader.h"
#include "Assembly.h"
#include <istream>
#include <fstream>
#include <iostream>
#include "Statistics.h"
#include <set>

namespace SelfAssembly{

#define MYOUT std::cout
#define MYERR std::cerr

bool Loader::uniqueFilename(const std::string &filePathName,
							const std::string &fileExtension,
							std::string &uniqueFullFileName)
{
	int filen=0;
	bool fexists=true;
	do{ //skip existing sequence files
		uniqueFullFileName= filePathName+std::to_string(filen)+fileExtension;
		std::ifstream iff(uniqueFullFileName);
		if(!iff.good()){
			fexists=false;
			break;
		}
		++filen;
	}while(filen<100000);

	return !fexists;
}

bool Loader::loadBlocksFromFile(Assembly& _assembly ,
								std::string &_lastDir,
								std::string *_fileName/*=NULL*/){
	
	std::string fileName;
	if(!_fileName || _fileName->empty() ){
		MYERR << __FUNCTION__ << ": Invalid filename. Aborting" << std::endl;
		return false;
	}else{
		fileName= *_fileName;
	}
	
	Vs vertices;
	B b=B();
	
	std::string line;
	std::ifstream myfile(fileName.c_str());
	if (myfile.is_open())
	{
		_assembly.setName(fileName);
		
		bool gSeen = false;
		bool anyFixed = false;
		while ( std::getline (myfile,line) )
		{
			if(line.empty()) continue;
			if( !(line[0]=='v' || line[0]=='f' || line[0]=='g' || line[0]=='h' || line[0]=='a')) continue;
			
			std::string token;
			std::stringstream sl(line);
			sl >> token;
			
			if(!token.compare("v")){
				V v;
				for(int i=0;i<3;i++) sl >> v[i];
				_assembly.addV(v);
			}
			
			if(!token.compare("f")){
				F f(_assembly.nB());
				int fi;
				while( sl >> fi ){ f.addVi(fi-1);}
				b.addFi(_assembly.nF());
				_assembly.addF(f);
			}
			
			if(!token.compare("g")){
				if(gSeen){ //At first g, block is the empty initialized block
					_assembly.addB(b);
				}
				b=B();
				if( line.find("fix") != std::string::npos ){
					b.setFixed(true);
					anyFixed=true;
				}
				if( line.find("dis") != std::string::npos ){
					b.setEnabled(false);
				}
				gSeen=true;
			}
			
			if(!token.compare("h")){
				if(!gSeen){
					MYERR << __FUNCTION__ << ": Hook before block in the blocks obj file. Aborting.\n";
					return false;
				}
				V v;
				for(int i=0;i<3;i++){ sl >> v[i];}
				int hf=-1; sl >> hf;
				int ht=-1; sl >> ht;
				b.setHook(v,hf,ht);
			}
			
			if(!token.compare("a")){
				V a;
				for(int i=0;i<3;i++) sl >> a[i];
				_assembly.addA(a);
			}
		}
		if(gSeen){ _assembly.addB(b); }
		if(!anyFixed){ MYERR << __FUNCTION__ << ": Assembly without any fixed block loaded. Will not stand." << std::endl;}
		myfile.close();
		
		_assembly.finalizeBlocks();
		//MYOUT << __FUNCTION__ << ": [|V| & |F| & |B| & |B_free| & |C| & |A|]: " << _assembly.nV() <<" & "<< _assembly.nF() << " & " << _assembly.nB();
		//MYOUT << " & " << _assembly.nBFree() << " & " << _assembly.nC() << " & " << _assembly.nA() << std::endl;
		Statistics s; 
		_assembly.volumeStatistics(s,true);
		double d=1./(s.avg() * gravity().norm());
		_assembly.setDensity(d);
		//MYOUT << "Volumes of free blocks: " << s << std::endl;
	}else{ MYOUT << "Unable to open file";
		return false;
	}
	return true;
}

bool Loader::writeBlocksToFile(Assembly& a, std::string &_lastDir, std::string *_fileName)
{
	std::string fileName;
	if(!_fileName || _fileName->empty() ){
		MYERR << __FUNCTION__ << ": Invalid filename. Aborting" << std::endl;
		return false;
	}else{
		fileName= *_fileName;
	}
	
	std::ofstream file;
	file.open (fileName.c_str());
	file << std::setprecision(16);
	
	for(unsigned int ai=0;ai<a.nA();++ai){
		file << "a " << a.a(ai).transpose() << std::endl;
	}
	
	for(unsigned int vi=0;vi<a.nV();++vi){
		file << "v " << a.v(vi).transpose() << std::endl;
	}
	
	for(unsigned int bi=0;bi<a.nB();++bi){
		const B& b = a.b(bi);
		file << "g block_" << bi << (b.fixed()?"_fix":"") << std::endl;
		if(b.hasHook()){
			file << "h " << b.hook().transpose() << " " << b.hookFace() << " " << b.hookTriangle() << std::endl;
		}
		for(unsigned int fii=0; fii<b.nFi();++fii){
			const F& f=a.f(b.fi(fii));
			file << "f";
			for(unsigned int vii=0;vii<f.nVi();++vii){
				file << " " << 1+f.vi(vii); //.obj has one-based indexing
			}
			file << std::endl;
			/*file << "t";
			for(unsigned int ti=0;ti<f.nT();++ti){
				const T& t = f.t(ti);
				file << " " << t.transpose();
			}
			file << std::endl;*/
		}
	}
	
	file.close();
	return true;
}

bool Loader::loadInterfacesFromFile(Assembly& _assembly, 
									std::string &_lastDir,
									std::string *_fileName/*=NULL*/){
	
	std::string fileName;
	if(!_fileName || _fileName->empty()){
		MYERR << __FUNCTION__ << ": Invalid filename. Aborting" << std::endl;
		return false;
	}else{
		fileName= *_fileName;
	}
	
	_assembly.clearInterfaces();
	
	Is is;

	std::string line;
	std::ifstream myfile(fileName.c_str());
	if (myfile.is_open())
	{
		while ( std::getline (myfile,line) )
		{
			if(line.empty()) continue;
			if( !(line[0]=='i') ) continue;

			std::string token;
			std::stringstream sl(line);
			sl >> token;

			if(!token.compare("i")){
				int bs[2];
				bool bb= !(sl >> bs[0]).fail() ;
				bb |= !( sl >> bs[1] ).fail();
				if(!bb){
					MYOUT << __FUNCTION__ << ": Invalid .ifs file parsed. Aborting. " << std::endl;
					return false;
				}

				int ifs[2];
				bool bf= !( sl >> ifs[0] ).fail();
				bf |= !( sl >> ifs[1] ).fail();
				//face indices are not mandatory in .ifs files
				if(!bf){
					_assembly.addI(bs[0],bs[1]);
				}else{
					//_assembly.addI(I(bs[0],bs[1],ifs[0],ifs[1]));
					assert(false); //needs implementation
				}
				//TODO: parse basis and interface vertices
			}

		}
		myfile.close();
		//_assembly.computeInterfaceVertexMultiplicities();
		//MYOUT << __FUNCTION__ << ": |I|=" << _assembly.nI() <<std::endl;

	}else{ MYOUT << "Unable to open file";
		return false;
	}
	_assembly.finalizeInterfaces();
	return true;
}

bool Loader::writeInterfacesToFile( const Assembly& a, std::string *_fileName/*=NULL*/ ){

	if(_fileName==NULL){ return false;}

	std::ofstream ifsFile;
	ifsFile.open (_fileName->c_str());
	if(!ifsFile.good()){
		return false;
	}

	//make interfaces unique
	std::set< std::pair<int,int> > ifss;
	for( unsigned int ii=0;ii<a.nI();++ii){
		const I& ifs=a.i(ii);
		ifss.insert( std::make_pair(ifs.bi(0),ifs.bi(1)) );
	}

	for( auto it=ifss.begin(); it!=ifss.end(); ++it ){
		ifsFile << "i " << it->first << " " << it->second << std::endl;
	}
	ifsFile.close();

	return true;
}

bool Loader::loadSequenceFromFile(Sequence &s, std::string &_lastDir, std::string *_fileName)
{
	std::string fileName;
	if(!_fileName || _fileName->empty()){
		MYERR << __FUNCTION__ << ": Invalid filename. Aborting" << std::endl;
		return false;
	}else{
		fileName= *_fileName;
	}

	s.clearStates();
	std::ifstream myfile(fileName.c_str());
	if (myfile.is_open())
	{
		int b;
		while( myfile >> b){
			State st;
			st.addedBlock_=b;

			int nc;	myfile >> nc;
			for(int ci=0;ci<nc;++ci){
				int ai; myfile >> ai;
				int bi; myfile >> bi;
				State::ChainIdxs cidxs(ai,bi);
				st.activeChains_.push_back(cidxs);
			}
			s.addState(st);
		}
	}

	return true;
}

bool Loader::writeSequenceToFile( const SelfAssembly::AnalyzerParameter& ap, const Sequence& s, std::string &_lastDir, std::string *_fileName)
{
	std::string fileName;
	if(!_fileName || _fileName->empty() ){
		MYERR << __FUNCTION__ << ": Invalid filename. Aborting" << std::endl;
		return false;
	}else{
		fileName= *_fileName;
	}

	std::ofstream file;
	file.open (fileName.c_str());
	file << std::setprecision(16);
	file << s;
	file << std::endl <<  "Analyzer Parameter: ";
	file << ap;
	file.close();
	return true;
}

}

