/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*!  \file   XMLBinaryFile.hpp */



#ifndef _XMLBinaryFile_hpp
#define _XMLBinaryFile_hpp

//  #ifndef _Image3D_hpp
//  #error "FileParser_hpp must be included from _Image3D_hpp"
//  #endif // _Image3D_hpp

#include <stdio.h>
#include<ImLib3D/xml++.h>
#include<ImLib3D/CppTools.hpp>
#include<ImLib3D/Vect3D.hpp>


// ********************** FileParser ********************
/*! \class XMLBinaryFile 
    \brief Class for reading/writing an xml file with binary data appended to it

  File structure is:<xml header><binary data>.
  The <binary data> may contain several blocks : <binary data>=<bin1><bin2><bin3>
  For example an image using a mask would contain: <binary data>=<mask data><image data>
  The xml header is read-to/written-from an XML tree using xml++ (that uses libxml)
 ****************************************************/
class XMLBinaryFile
{
public:
	// Create XMLBinaryFile. If mode is set, it will also open the file.
	XMLBinaryFile(const string &_fileName="",const string &mode="",bool _exceptionOnFail=false);
	// Closes the file (if necesary)
	~XMLBinaryFile(){if(isOpen){Close();}}
	void OpenRead(const string &_fileName);
	void OpenWrite(const string &_fileName);
	void Close();
	
	XMLNode *AddBinaryNode(XMLNode *parent,int size=-1);

	//! endian preserving write of a value range. meant only for simple types
	template<class T,class iterator>
	void BinaryWrite(iterator begin,iterator end);
	//! endian preserving write of a value range. meant only for simple types
	template<class T,class iterator>
	void BinaryRead(iterator begin,iterator end);
	//! Write some binary data. Use this only after AddBinaryNode.
	void BinaryWrite(void *data,int size);
	//! Add "size" bytes of data to binary node (use this!)
	void BinaryWrite(XMLNode *binNode,void *data,int size);
	void ReadBinaryNode(XMLNode *parent);
	void EndBinaryWrite(XMLNode *binNode);
	// Write some binary data. Use this only after ReadBinaryNode.
	void BinaryRead(void *data,int size);
	bool CheckBinaryReadComplete();

	//@{ Convenience functions
  	XMLNode *AddChild(XMLNode *parent,const string &name,const string &a1="",const string &a2="",const string &a3="",const string &a4="");
	XMLNode *GetChild(XMLNode *parent,const string &name);
	bool   HasChild(XMLNode *parent,const string &name);
	int    GetIntProperty   (XMLNode *node,const string &name){return (int)LongInt(GetProperty(node,name));}
	double GetDoubleProperty(XMLNode *node,const string &name){return       Double(GetProperty(node,name));}
	string GetProperty      (XMLNode *node,const string &name);
	bool   HasProperty      (XMLNode *node,const string &name);
	//! remove any chars that confuse xml
	static string Quote(const string &src);
	//! restore any chars that confuse xml, that where removed by "Quote"
	static string UnQuote(const string &src);
    //@}


protected:
	//! Allocate space for a new binary block (for ex.image data) 
	/*! if size==0 block is extensible, must call EndBinaryNode */
	int    AllocInBinary(int size);
    //@{ \name Accesors
	string GetBinFileName(){return binFileName;}
	string GetFileName(){return fileName;}
	int    GetBinaryBegining(){return binaryBegining;}
public:
	XMLNode *GetRoot(const string &rootName="");
	XMLNode *SetRoot(XMLNode *root){return tree.set_root(root);}
	//@}

public:
	//! Print an XML tree (for debuging)
	void   ShowXML(XMLNode *node=NULL,int depth=0);

	int debug;
protected:

	//! The real file name
	string fileName;
	//! The temporary binary file name
	string binFileName;
	//! The temporary binary file
	FILE *binaryFile;

	XMLTree tree;
	//! Current allocation position within the binary file
	size_t binaryCurrentAllocPos;
	size_t posInsideCurrentAllocBlock;
	size_t currentAllocBlockSize;
	//! Size of xml header, or equivalently, pos of begining of bin data
	/*! Only used when reading */
	size_t binaryBegining;


	bool isWrite;
	bool isOpen;
	bool exceptionOnFail;
	//! Read xml header from fileName
	/*!
	   Finds the end of the xml header and reads it
	 */
	string ReadXMLHeader();
	void Error(const char *fmt, ...);

public:
	void ExceptionOnFail();
	

};


template<class T,class iterator>
void 
XMLBinaryFile::BinaryWrite(iterator begin,iterator end)
{
	uint size=end-begin;
	if(IsSmallEndian() || sizeof(*begin)==1)
	{
		BinaryWrite((void *)begin,size*sizeof(T));
	}
	else
	if(typeid(T) == typeid(Vect3Df))
	{
		float *data0=(float *)(void *)begin;
		for(uint i=0;i<size;i++)
		{
			for(int j=0;j<3;j++)
			{
				float p;
				ReverseEndian(data0[i*3+j],p);
				BinaryWrite(&p,sizeof(float));
			}
		}
	}
	else
	{		
		T v;
		for(iterator p=begin;p!=end;++p)
		{
			ReverseEndian(*p,v);
			BinaryWrite(&v,sizeof(T));
		}
	}
}
template<class T,class iterator>
void 
XMLBinaryFile::BinaryRead(iterator begin,iterator end)
{
	uint size=end-begin;

	if(IsSmallEndian() || sizeof(T)==1)
	{
		BinaryRead(begin,size*sizeof(T));
	}
	else
	if(typeid(T) == typeid(Vect3Df))
	{
		float *data0=(float *)(void *)begin;
		for(uint i=0;i<size;i++)
		{
			for(int j=0;j<3;j++)
			{
				float p;
				BinaryRead(&p,sizeof(float));
				ReverseEndian(p,data0[i*3+j]);
			}
		}
	}
	else
	{
		T v;
		for(iterator p=begin;p!=end;++p)
		{
			BinaryRead(&v,sizeof(T));
			ReverseEndian(v,*p);
		}
	}
}

#endif // _XMLBinaryFile_hpp
