// fvcm.cpp
// Las modification: May 2014 (R. Costa)
// Usage fvcm <mesh_to_convert.sth> <new_mesh.sth> <options>
#include <iostream>
#include <ctime>
#include "FVLib.h"
// convert typ1 to xml
void typ12msh(string str_file_in, string str_file_out)
{
	// declarations
	// vectors to gather the data
	FVDenseM<double> Vertices;
	FVDenseM<unsigned> Triangles,Quadrangles,BoundEdges,Edges;
	char str[50];
	int nb_elements,e1,e2,e3,e4;
	float x,y;
	int code,element_type,nb_tags,k,line=0;
	bool write;
	//// read the typ1 elements
	FILE *file_in=fopen(str_file_in.c_str(),"r");
	if(file_in==NULL)
	{
		cout<<"Error: "<<file_in<<" not found!"<<endl;
		exit(0);
	}
	// read the vertices
	fscanf(file_in,"%s",str);
	line++;
	if((string)str=="vertices")
	{
		fscanf(file_in,"%i",&nb_elements);
		line++;
		Vertices.resize(nb_elements,2);
		for(size_t i=0;i<(size_t)nb_elements;i++)
		{
			fscanf(file_in,"%f%f",&x,&y);
			line++;
			Vertices.setValue(i,0,x);
			Vertices.setValue(i,1,y);
		}
	}
	else
	{
		cout<<"Error: wrong file format at line "<<line<<" in file "<<file_in<<"!"<<endl;
		exit(0);
	}
	// read the triangles
	fscanf(file_in,"%s",str);
	line++;
	if((string)str=="triangles")
	{
		fscanf(file_in,"%i",&nb_elements);
		line++;
		Triangles.resize(nb_elements,3);
		for(size_t i=0;i<(size_t)nb_elements;i++)
		{
			fscanf(file_in,"%i%i%i",&e1,&e2,&e3);
			line++;
			Triangles.setValue(i,0,e1);
			Triangles.setValue(i,1,e2);
			Triangles.setValue(i,2,e3);
		}
	}
	else
	{
		cout<<"Error: wrong file format at line "<<line<<" in file "<<file_in<<"!"<<endl;
		exit(0);
	}
	// read the quadrangles
	fscanf(file_in,"%s",str);
	line++;
	if((string)str=="quadrangles")
	{
		fscanf(file_in,"%i",&nb_elements);
		line++;
		Quadrangles.resize(nb_elements,4);
		for(size_t i=0;i<(size_t)nb_elements;i++)
		{
			fscanf(file_in,"%i%i%i%i",&e1,&e2,&e3,&e4);
			line++;
			Quadrangles.setValue(i,0,e1);
			Quadrangles.setValue(i,1,e2);
			Quadrangles.setValue(i,2,e3);
			Quadrangles.setValue(i,3,e4);
		}
	}
	else
	{
		cout<<"Error: wrong file format at line "<<line<<" in file "<<file_in<<"!"<<endl;
		exit(0);
	}
	// read the boundary edges
	fscanf(file_in," %[a-z ]s",str);
	line++;
	if((string)str=="edges of the boundary " || (string)str==" edges of the boundary" ||
	   (string)str=="edges of the boundary" ||(string)str==" edges of the boundary " ||
	   (string)str==" edges of the boundary  ")
	{
		fscanf(file_in,"%i",&nb_elements);
		line++;
		BoundEdges.resize(nb_elements,2);
		for(size_t i=0;i<(size_t)nb_elements;i++)
		{
			fscanf(file_in,"%i%i",&e1,&e2);
			line++;
			BoundEdges.setValue(i,0,e1);
			BoundEdges.setValue(i,1,e2);
		}
	}
	else
	{
		cout<<"Error: wrong file format at line "<<line<<" in file "<<file_in<<"!"<<endl;
		exit(0);
	}
	// read all the edges
	fscanf(file_in," %[a-z ]s",str);
	line ++;
	if((string)str=="all edges " || (string)str==" all edges" ||
	   (string)str=="all edges" || (string)str==" all edges ")
	{
		fscanf(file_in,"%i",&nb_elements);
		line++;
		Edges.resize(nb_elements,4);
		for(size_t i=0;i<(size_t)nb_elements;i++)
		{
			fscanf(file_in,"%i%i%i%i",&e1,&e2,&e3,&e4);
			line++;
			Edges.setValue(i,0,e1);
			Edges.setValue(i,1,e2);
			Edges.setValue(i,2,e3);
			Edges.setValue(i,3,e4);
		}
	}
	else
	{
		cout<<"Error: wrong file format at line "<<line<<" in file "<<file_in<<"!"<<endl;
		exit(0);
	}
	fclose(file_in);
	//// write the msh file
	FILE *file_out=fopen(str_file_out.c_str(),"w");
	fprintf(file_out,"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n$Nodes\n");
	fprintf(file_out,"%i\n",(int)Vertices.getNbRows());
	// vertices
	for(size_t i=1;i<=Vertices.getNbRows();i++)
		fprintf(file_out,"%i %f %f 0\n",(int)i,Vertices.getValue(i-1,0),Vertices.getValue(i-1,1));
	fprintf(file_out,"$EndNodes\n");
	// elements
	fprintf(file_out,"$Elements\n");
	fprintf(file_out,"%i\n",(int)(Triangles.getNbRows()+Quadrangles.getNbRows()+Edges.getNbRows()*2));
	k=-1;
	// boundary edges
	code=3;
	element_type=1;
	nb_tags=2;
	for(size_t i=1;i<=BoundEdges.getNbRows();i++)
	{
		fprintf(file_out,"%i %i %i %i %i %i %i\n",++k,element_type,nb_tags,code,0,BoundEdges.getValue(i-1,0),BoundEdges.getValue(i-1,1));
		code+=MINUS_ONE_DIM;
		// second element for the code of the vertices
		fprintf(file_out,"%i %i %i %i %i %i %i\n",++k,element_type,nb_tags,code,0,BoundEdges.getValue(i-1,0),BoundEdges.getValue(i-1,1));
	}
	// remaining edges
	element_type=1;
	nb_tags=2;
	code=0;
	for(size_t i=1;i<=Edges.getNbRows();i++)
	{
		write=true;
		for(size_t j=0;j<BoundEdges.getNbRows();j++)
		{
			if((BoundEdges.getValue(j,0)==Edges.getValue(i-1,0) && BoundEdges.getValue(j,1)==Edges.getValue(i-1,1))
			   || (BoundEdges.getValue(j,1)==Edges.getValue(i-1,0) && BoundEdges.getValue(j,0)==Edges.getValue(i-1,1)))
			{
				write=false;
				break;
			}
		}
		if(write)
		{
			fprintf(file_out,"%i %i %i %i %i %i %i\n",++k,element_type,nb_tags,code,0,Edges.getValue(i-1,0),Edges.getValue(i-1,1));
			code+=MINUS_ONE_DIM;
			// second element for the code of the vertices
			fprintf(file_out,"%i %i %i %i %i %i %i\n",++k,element_type,nb_tags,code,0,Edges.getValue(i-1,0),Edges.getValue(i-1,1));
		}
	}
	// triangles
	code=10;
	element_type=2;
	nb_tags=2;
	for(size_t i=1;i<=Triangles.getNbRows();i++)
		fprintf(file_out,"%i %i %i %i %i %i %i %i\n",int(i+Edges.getNbRows()+2*Edges.getNbRows()),element_type,nb_tags,code,0,Triangles.getValue(i-1,0),Triangles.getValue(i-1,1),Triangles.getValue(i-1,2));
	// quadrangles
	code=10;
	element_type=3;
	nb_tags=2;
	for(size_t i=1;i<=Quadrangles.getNbRows();i++)
		fprintf(file_out,"%i %i %i %i %i %i %i %i %i\n",int(i+2*Edges.getNbRows()+Triangles.getNbRows()),element_type,nb_tags,code,0,Quadrangles.getValue(i-1,0),Quadrangles.getValue(i-1,1),Quadrangles.getValue(i-1,2),Quadrangles.getValue(i-1,3));
	// all the elements were written
	fprintf(file_out,"$EndElements\n");
	fclose(file_out);	
}
// convert xml to pdf
void xml2pdf(FVMesh2D& m,string file_in,string file_out,int font_size)
{
	// extract the label to be used in tex, dvi, and ps files
	string label;
	label=&file_out[0];
	label.erase(label.size()-4);
	// extract the figure of the mesh
	FILE* file;
	file=fopen((label+".tex").c_str(),"w");
	// write the tex file
	fprintf(file,"\
\\documentclass{standalone}\n\
\\usepackage[english]{babel}\n\
\\usepackage{amsmath}\n\
\\renewcommand{\\rmdefault}{phv}\n\
\\renewcommand{\\sfdefault}{phv}\n\
\\usepackage[usenames,dvipsnames]{pstricks}\n\
\\usepackage{epsfig}\n\
\\usepackage{pst-plot}\n\
\\usepackage{fix-cm}\n\
\\author{Ricardo Costa}\n\
\\begin{document}\n\
\\fontsize{%u}{%u}\\selectfont\n\
\\scalebox{2}{\n\
\\begin{pspicture}(-0.7,-0.5)(4.5,4.5)\n\
\\psaxes[Ox=0,Dx=1,Oy=0,Dy=1,linewidth=0.005cm,ticksize=-1mm,labels=none]{-}(0,0)(4,4)[x,0][y,90]\n\
\\psxTick[ticksize=-1mm](0){\\text{0}}\n\
\\psxTick[ticksize=-1mm](1){\\text{0.25}}\n\
\\psxTick[ticksize=-1mm](2){\\text{0.5}}\n\
\\psxTick[ticksize=-1mm](3){\\text{0.75}}\n\
\\psxTick[ticksize=-1mm](4){\\text{1}}\n\
\\psyTick[ticksize=-1mm](0){\\text{0}}\n\
\\psyTick[ticksize=-1mm](1){\\text{0.25}}\n\
\\psyTick[ticksize=-1mm](2){\\text{0.5}}\n\
\\psyTick[ticksize=-1mm](3){\\text{0.75}}\n\
\\psyTick[ticksize=-1mm](4){\\text{1}}\n",font_size,font_size);
	// write the lines corresponding to the edges
	FVEdge2D* ptr_e;
	for(unsigned i=0;i<m.getNbEdge();i++)
	{
		ptr_e=m.getEdge(i);
		fprintf(file,"\\psline[linewidth=0.01cm](%.2f,%.2f)(%.2f,%.2f)\n",
				4*ptr_e->firstVertex->coord.x,4*ptr_e->firstVertex->coord.y,
				4*ptr_e->secondVertex->coord.x,4*ptr_e->secondVertex->coord.y);
	}
	//for(size_t i=0;i<m.getNbCell();i++)
	//	fprintf(file,"\\psdots[dotsize=0.07](%.2f,%.2f)\n",m.getCell(i)->centroid.x*4,m.getCell(i)->centroid.y*4);
	fprintf(file,"\\end{pspicture}}\n\\end{document}");
	fclose(file);
}
void getParameterValue(char* arg,string &parameter,string &str_value)
{
	parameter=arg;
	char* ext=strrchr(arg,'=');
	str_value="";
	if(ext)
	{
		str_value=&ext[1];
		parameter.erase(parameter.size()-str_value.size()-1);
	}
}
// main function
int main(int argc, char *argv[])
{
	// error handling
	if(argc==2 && (string)argv[1]=="help")
	{
		cout<<"Convert xml->gmsh: fvcm <in_mesh.xml> <out_mesh.msh> "<<endl;
		cout<<"Convert gmsh->xml: fvcm <in_mesh.msh> <out_mesh.xml> "<<endl;
		cout<<"Convert mphtxt->xml: fvcm <in_mesh.mphtxt> <out_mesh.xml> "<<endl;
		cout<<"Convert typ1->gmsh: fvcm <in_mesh.typ1> <out_mesh.msh> "<<endl;
		cout<<"Convert xml->pdf: fvcm <in_mesh.xml> <out_mesh.pdf> <fontsize=X (default is 4)>"<<endl;
		cout<<"Convert xml->tex: fvcm <in_mesh.xml> <out_mesh.tex> <fontsize=X (default is 4)>"<<endl;
		exit(0);
	}
	else if(argc<3 || argc>4) 
	{
        cout<<"Error: wrong number of arguments! Use the option <help> for usage information."<<endl;    
        exit(0);
	}
	// get the files
	string file_in=argv[1];
	string file_out=argv[2];
	// get the formats of the files
	char* ext=strrchr(argv[1],'.');
	if(!ext)
	{
		cout<<"Error: input file format empty! Use the option <help> for usage information."<<endl;
		exit(0);
	}
	string format_in=ext;
	ext=strrchr(argv[2],'.');
	if(!ext)
	{
		cout<<"Error: output file format empty! Use the option <help> for usage information."<<endl;
		exit(0);
	}
	string format_out=ext;
	// check the formats
	unsigned code_in;
	if(!format_in.compare(".xml")) code_in=XML_FORMAT;
	else if(!format_in.compare(".msh")) code_in=GMSH_FORMAT;
	else if(!format_in.compare(".htxt")) code_in=COMSOL_FORMAT;
	else if(!format_in.compare(".typ1")) code_in=TYP1_FORMAT;
	else
	{
		cout<<"Error: input file format not recognized! Use the option <help> for usage information."<<endl;
		exit(0);
	}
	unsigned code_out;
	if(!format_out.compare(".xml")) code_out=XML_FORMAT;
	else if(!format_out.compare(".msh")) code_out=GMSH_FORMAT;
	else if(!format_out.compare(".pdf")) code_out=PDF_FORMAT;
	else if(!format_out.compare(".tex")) code_out=TEX_FORMAT;
	else
	{
		cout<<"Error: output file format not recognized! Use the option <help> for usage information."<<endl;
		exit(0);
	}
	FILE *file;
	file=fopen(file_in.c_str(),"r");
	if(!file)
	{
		cout<<"Error: input file does not exist!"<<endl;
		exit(0);
	}
	fclose(file);
	file=fopen(file_out.c_str(),"r");
	/*if(file)
	{
		fclose(file);
		cout<<"Warning: output file exists!"<<endl;
		int nb_ch=0;
		cin.sync_with_stdio(false);
		for(size_t i=0;i<8;i++)
		{
			printf("Press enter to proceed or wait %u seconds\r",int(8-i));fflush(NULL);
			clock_t endwait;
			endwait=clock()+CLOCKS_PER_SEC;
			while(clock()<endwait){};
			nb_ch=cin.rdbuf()->in_avail();
			if(nb_ch>0)
				break;
		}
		cout<<endl;
	}*/
	if(code_in==code_out)
	{
		cout<<"Warning: same file format!"<<endl;
		exit(0);
	}
	// treat gmsh->xml
	FVMesh1D m1;
	FVMesh2D m2;
	FVMesh3D m3;
	Gmsh mg;
	Comsol mc;
	if(code_in==GMSH_FORMAT && code_out==XML_FORMAT)
	{
		cout<<"Converting msh->xml"<<endl;fflush(NULL);
		mg.readMesh(file_in.c_str());
		if(mg.getDim()==1) 
		{
			cout<<"1D mesh"<<endl;
			m1.Gmsh2FVMesh(mg); 
			m1.setName("fvcm convertor");  
			m1.write(file_out.c_str());
		}
		if(mg.getDim()==2) 
		{
			cout<<"2D mesh"<<endl;
			m2.Gmsh2FVMesh(mg); 
			m2.setName("fcgm convertor");  
			m2.write(file_out.c_str());
		}  
		if(mg.getDim()==3) 
		{
			cout<<"3D mesh"<<endl;
			m3.Gmsh2FVMesh(mg); 
			m3.setName("fvcm convertor");  
			m3.write(file_out.c_str());
		}          
		if(argc==4)
			cout<<"Warning: last arguments ignored!"<<endl;
		cout<<"Conversion done!"<<endl;
		return(0);
	}
	// treat xml->gmsh
	if(code_in==XML_FORMAT && code_out==GMSH_FORMAT)
	{
		cout<<"Converting xml->msh"<<endl;fflush(NULL); 
		if(m3.read(file_in.c_str())==FVOK)
		{
			cout<<"3D mesh"<<endl;
			mg.FVMesh2Gmsh(m3,1);
		}
		if(m2.read(file_in.c_str())==FVOK)
		{
			cout<<"2D mesh"<<endl;
			mg.FVMesh2Gmsh(m2,1);
		}      
		if(m1.read(file_in.c_str())==FVOK)
		{
			cout<<"1D mesh"<<endl;
			mg.FVMesh2Gmsh(m1);
		}
		mg.writeMesh(file_out.c_str());
		if(argc==4)
			cout<<"Warning: last arguments ignored!"<<endl;
		cout<<"Conversion done!"<<endl;
		return(0);
	}
	// treat mphtxt->xml    
	if(code_in==COMSOL_FORMAT && code_out==GMSH_FORMAT)
    {
		cout<<"Converting mphtxt->xml"<<endl;fflush(NULL);
		mc.readMesh(file_in.c_str());
		if(argc==4) 
        {
			string file_phy=argv[3];
			mc.readComsolPhysical(file_phy.c_str());
			mc.geo2phys();
        }
		mg.Comsol2Gmsh(mc);
		mg.writeMesh(file_out.c_str());     
		cout<<"Convertion done!"<<endl;
		return(0);
    }
	// treat typ1->xml    
	if(code_in==TYP1_FORMAT && code_out==GMSH_FORMAT)
	{
		// only for 2D
		cout<<"Converting typ1->msh"<<endl;fflush(NULL);
		typ12msh(file_in,file_out);
		if(argc==4)
			cout<<"Warning: last arguments ignored!"<<endl;
		cout<<"Convertion done!"<<endl;
		return(0);
	}
	// treat xml->tex   
	if(code_in==XML_FORMAT && code_out==TEX_FORMAT)
	{
		int font_size;
		string parameter,value;
		if(argc==3)
			font_size=4;
		else
		{
			getParameterValue(argv[3],parameter,value);
			if(parameter!="fontsize")
			{
				cout<<"Error: invalid parameter! Use the option <help> for usage information."<<endl;
				exit(0);
			}
			if(!ext)
			{
				cout<<"Error: you forgot the parameter value!"<<endl;
				exit(0);
			}
			if(value=="")
			{
				cout<<"Error: you forgot the parameter value!"<<endl;
				exit(0);
			}
			font_size=atoi(value.c_str());
			// warning if font_size is a null value
			if(font_size==0)
				cout<<"Warning: null font size."<<endl;
			else if(font_size<0)
			{
				cout<<"Error: negative font sizes are not allowed! Process aborted!"<<endl;
				exit(0);
			}
			else if(font_size>7)
				cout<<"Warning: font size too large."<<endl;
		}
		cout<<"Converting xml->tex"<<endl;fflush(NULL);
		if(m1.read(file_in.c_str())==FVOK)
		{
			cout<<"1D mesh"<<endl;
			cout<<"Error: I can only perform this conversion for 2D meshes! Process aborted!"<<endl;
			exit(0);
		}
		if(m2.read(file_in.c_str())==FVOK)
		{
			cout<<"2D mesh"<<endl;
			xml2pdf(m2,file_in,file_out,font_size);
		}
		if(m3.read(file_in.c_str())==FVOK)
		{
			cout<<"3D mesh"<<endl;
			cout<<"Error: I can only perform this conversion for 2D meshes! Process aborted!"<<endl;
			exit(0);
		}
		cout<<"Convertion done!"<<endl;
		return(0);
	}
	// treat xml->pdf   
	if(code_in==XML_FORMAT && code_out==PDF_FORMAT)
	{
		int font_size;
		string parameter,value;
		if(argc==3)
			font_size=4;
		else
		{
			getParameterValue(argv[3],parameter,value);
			if(parameter!="fontsize")
			{
				cout<<"Error: invalid parameter! Use the option <help> for usage information."<<endl;
				exit(0);
			}
			if(!ext)
			{
				cout<<"Error: you forgot the parameter value!"<<endl;
				exit(0);
			}
			if(value=="")
			{
				cout<<"Error: you forgot the parameter value!"<<endl;
				exit(0);
			}
			font_size=atoi(value.c_str());
			// warning if font_size is a null value
			if(font_size==0)
				cout<<"Warning: null font size."<<endl;
			else if(font_size<0)
			{
				cout<<"Error: negative font sizes are not allowed! Process aborted!"<<endl;
				exit(0);
			}
			else if(font_size>7)
				cout<<"Warning: font size too large."<<endl;
		}
		cout<<"Converting xml->tex"<<endl;fflush(NULL);
		if(m1.read(file_in.c_str())==FVOK)
		{
			cout<<"1D mesh"<<endl;
			cout<<"Error: I can only perform this conversion for 2D meshes! Process aborted!"<<endl;
			exit(0);
		}
		if(m2.read(file_in.c_str())==FVOK)
		{
			cout<<"2D mesh"<<endl;
			xml2pdf(m2,file_in,file_out,font_size);
		}
		if(m3.read(file_in.c_str())==FVOK)
		{
			cout<<"3D mesh"<<endl;
			cout<<"Error: I can only perform this conversion for 2D meshes! Process aborted!"<<endl;
			exit(0);
		}
		string label;
		label=&file_out[0];
		label.erase(label.size()-4);
		// compile the tex file and generate the figure
		system(("latex "+label+".tex").c_str());
		system(("dvips "+label+".dvi").c_str());
		system(("ps2pdf "+label+".ps").c_str());
		system(("rm "+label+".dvi "+label+".ps "+label+".aux "+label+".log").c_str());
		cout<<"Convertion done!"<<endl;
		return(0);
	}
	cout<<"Error: conversion type not recongnized! Use the option <help> for usage information."<<endl;
	return(0);
}
// end of fvcm.cpp
