/*
 *	vtk.h - VTK file Import-export 
 *	Copyright (C) 2018, D Haley
 
 *	This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 
 *	You should have received a copy of the GNU General Public License
 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef VTK_H 
#define VTK_H

#include <vector>
#include <string>

#include "ionhit.h"
#include "common/voxels.h"
#include "common/stringFuncs.h"

enum
{
	VTK_ERR_FILE_OPEN_FAIL=1,
	VTK_ERR_NOT_IMPLEMENTED,
	VTK_ERR_ENUM_END
};

enum
{
	VTK_ASCII,
	VTK_BINARY,
	VTK_FORMAT_ENUM_END
};

//write ions to a VTK (paraview compatible) file. 
// FIXME : This currenly only supports ASCII mode. 
//	Need binary mode because of the large files we have
unsigned int vtk_write_legacy(const std::string &filename, 
	unsigned int format, const std::vector<IonHit> &ions);

//Read VTK file (paraview compatible) file
template<class T>
unsigned int vtk_write_legacy(const std::string &filename, unsigned int format,

		const Voxels<T> &vox)
{

	std::ofstream f;

	if(format != VTK_ASCII)
	{
		cerr << "Binary mode is not implemented"
			<< endl;

		return VTK_ERR_NOT_IMPLEMENTED;
	}

	f.open(filename.c_str());

	if(!f)
		return VTK_ERR_FILE_OPEN_FAIL;
		


	f << "# vtk DataFile Version 3.0\n";
	f << "Saved using AtomProbe Tools\n";
	f << "ASCII\n\n";

	size_t nx,ny,nz;
	vox.getSize(nx,ny,nz);
	f << "DATASET RECTILINEAR_GRID\n";
	f << "DIMENSIONS " << nx << " " << ny << " " << nz << endl;


	f << "X_COORDINATES " << nx << " float" << endl;
	for(unsigned int ui=0;ui<nx;ui++)
	{
		f << vox.getPoint(ui,0,0)[0] << " ";
	}
	f << endl; 
	
	f << "Y_COORDINATES " << ny << " float" << endl;
	for(unsigned int ui=0;ui<ny;ui++)
	{
		f << vox.getPoint(0,ui,0)[1] << " ";
	}
	f << endl; 
	
	f << "Z_COORDINATES " << nz << " float" << endl;
	for(unsigned int ui=0;ui<nz;ui++)
	{
		f << vox.getPoint(0,0,ui)[2] << " ";
	}
	f << endl; 
	

	f << "POINT_DATA " << vox.size() << endl;
	f << "SCALARS masstocharge float\n"; 
	f << "LOOKUP_TABLE default\n";

	for(unsigned int ui=0;ui<vox.size(); ui++)
	{
		f << vox.getData(ui)<< "\n";
	}
	return 0;
}

template<class T>
unsigned int vtk_read_legacy(const std::string &filename, unsigned int format,

		Voxels<T> &vox)
{
	ASSERT(format == VTK_ASCII);
	ifstream f;
	f.open(filename.c_str());

	if(!f)
		return VTK_ERR_FILE_OPEN_FAIL;


	std::string s;
	bool foundAscii=false,foundRectGrid=false;
	//Scan for "ASCII", DATASET tags tag 
	while(getline(f,s))
	{
		s=stripWhite(s);

		if(s == "ASCII")
			foundAscii=true;
		
		if(s == "DATASET RECTILINEAR_GRID")
		{
			foundRectGrid=true;
			break;
		}

	}
	if(!foundRectGrid || !foundAscii)
		return VTK_ERR_FILE_OPEN_FAIL;
	
	//Scan for dimesions data
	if(!getline(f,s))
		return VTK_ERR_FILE_OPEN_FAIL;
	
	vector<string> strs;
	splitStrsRef(s.c_str()," \t",strs);


	if(strs.size() != 4) 
		return VTK_ERR_FILE_OPEN_FAIL;
	
	if(strs[0] != "DIMENSIONS") 
		return VTK_ERR_FILE_OPEN_FAIL;

	unsigned int nx,ny,nz; 
	if(stream_cast(nx,strs[1]))
		return VTK_ERR_FILE_OPEN_FAIL;
	if(stream_cast(ny,strs[2]))
		return VTK_ERR_FILE_OPEN_FAIL;
	if(stream_cast(nz,strs[3]))
		return VTK_ERR_FILE_OPEN_FAIL;

	
	vox.resize(nx,ny,nz);

	
	//Read the x-y-z coordinates
	vector<string> strCoords[3];
	for(unsigned int ui=0;ui<3;ui++)
	{
		if(!getline(f,s))
			return VTK_ERR_FILE_OPEN_FAIL;
		if(!getline(f,s))
			return VTK_ERR_FILE_OPEN_FAIL;
		splitStrsRef(s.c_str()," \t",strCoords[ui]);
	}

	//Check coords match the expected coords
	if(	strCoords[0].size() != nx ||
		strCoords[1].size() != ny ||
		strCoords[2].size() != nz )
		return VTK_ERR_FILE_OPEN_FAIL;

	//parse coords into upper and lower bounds
	Point3D pMin,pMax;
	for(unsigned int ui=0;ui<3;ui++)
	{
		if(stream_cast(pMin[ui],strCoords[ui][0]))
			return VTK_ERR_FILE_OPEN_FAIL;
		if(stream_cast(pMax[ui], strCoords[ui][strCoords[ui].size()-1]))
			return VTK_ERR_FILE_OPEN_FAIL;
	}

	//Check each axis is oriented such that pMin < pMax
	bool haveSwapped[3] = {false,false,false};
	for(auto ui=0;ui<3;ui++)
	{
		if(pMax[ui] < pMin[ui])
		{
			std::swap(pMin[ui],pMax[ui]);
			haveSwapped[ui]=true;
		}
	}


	vox.setBounds(pMin,pMax);

	//Discard POINT_DATA, SCALARS and LOOKUP line
	getline(f,s);
	getline(f,s);
	getline(f,s);

	unsigned int nTotal = nx*ny*nz;

	for(unsigned int ui=0;ui<nTotal;ui++)
	{
		if(!getline(f,s))
			return VTK_ERR_FILE_OPEN_FAIL;

		float fV;
		if(stream_cast(fV,s))
			return VTK_ERR_FILE_OPEN_FAIL;

		vox.setData(ui,fV);
	}

	//If we swapped the coordinates, then we have effectively reversed
	// the data representation, but not the underlying data. Fix this for
	// each swapped axis.
	//FIXME: Performance - could do this at setData time, 
	// save double handling the data
	for(auto ui=0;ui<3;ui++)
	{
		if(haveSwapped[ui])
			vox.reverseAxis(ui);
	}

	return 0;
}

#ifdef DEBUG
//unit testing
bool testVTKExport();
#endif
#endif 
