/* 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  Container3D.hxx  */

#ifndef _Container3D_hxx
#define _Container3D_hxx

// *******************************************************
// template implementations for Container3D
// *******************************************************

template<class Im3DValue>
bool 
Container3D<Im3DValue>::WarnOnUnkownType() const
{
	if(!(IsFloat<Im3DValue>() || IsDouble<Im3DValue>() || IsVect3Df<Im3DValue>() || IsByte<Im3DValue>()
		 || IsInt<Im3DValue>() || IsShortInt<Im3DValue>()))//|| IsComplexDouble<Im3DValue>()  ))
	{
		mprintf("Warning: Container3D value type is unknown, using default routines:typename:%s\n",TypeName<Im3DValue>().c_str());
		return true;
	}
	return false;
}

template<class Im3DValue>
string 
Container3D<Im3DValue>::GetTypeName() const 
{
	string valueName=TypeName<Im3DValue>().c_str();
	if(valueName[valueName.size()-1]=='>'){valueName+=' ';}
	return SPrintf("Container3D<%s>",valueName);
}


template<class Im3DValue>
void 
Container3D<Im3DValue>::Write(ImLib3DFile *file,XMLNode *parentNode,XMLNode *node) const
{
	node=CreateWriteNode(file,parentNode,node);
	// **** Optionally add unset image properties ***
	// set image type (Image3Df, Field3D etc....) 
	if(!file->HasProperty(node,"Type")){node->add_property("Type",GetTypeName());}
	// store Data (binary) information
	if(!file->HasChild(node,"ImageData"))
	{
		XMLNode *dataN=node->add_child("ImageData");
		file->AddBinaryNode(dataN,GetBinarySize());
		// write image's binary block 
//		WriteBinary(file);
		file->template BinaryWrite<Im3DValue,Im3DValue *>(imageData,imageData+GetNVoxels());
	}	   
	// write parents data (image size and properties)
	Image3D::Write(file,NULL,node);
}
template<class Im3DValue>
void 
Container3D<Im3DValue>::Read(ImLib3DFile *file,XMLNode *parentNode,XMLNode *node)
{
	node=CreateReadNode(file,parentNode,node);
	Image3D::Read(file,NULL,node);

	if(file->debug){cout << "Container3D::Read: " << endl;file->ShowXML(node);}
	
	//type checking
	string type=file->GetProperty(node,"Type");
	if(type!=GetTypeName()){ThrowError("ReadFromXMLTree: Expected type:\"%s\" found type:\"%s\"",GetTypeName(),type);}

	//image data
	XMLNode *dataN=file->GetChild(node,"ImageData");
	file->ReadBinaryNode(dataN);
	file->template BinaryRead<Im3DValue>(imageData,imageData+GetNVoxels());
	if(!file->CheckBinaryReadComplete())
	{ThrowError("XMLBinaryFile::BinaryRead bad read size (maybe wrong image type?) ");}
//	ReadBinary(file);
}


template<class Im3DValue>
void
Container3D<Im3DValue>::WriteBinary(ImLib3DFile *file) const
{
	int size=GetBinarySize();
	
	if(IsSmallEndian() || sizeof(Im3DValue)==1)
	{
		file->BinaryWrite((void *)GetData(),size);
	}
	else
	if(IsVect3Df<Im3DValue>())
	{
		float *data0=(float *)(void *)GetData();
		for(int i=0;i<GetNVoxels();i++)
		{
			for(int j=0;j<3;j++)
			{
				float p;
				ReverseEndian(data0[i*3+j],p);
				file->BinaryWrite(&p,sizeof(float));
			}
		}
	}
	else
	{
		Im3DValue p;
		for(int i=0;i<GetNVoxels();i++)
		{
			ReverseEndian(imageData[i],p);
			file->BinaryWrite(&p,sizeof(Im3DValue));
		}
	}
}
template<class Im3DValue>
void
Container3D<Im3DValue>::ReadBinary(ImLib3DFile *file)
{
	int size=GetBinarySize();

	if(IsSmallEndian() || sizeof(Im3DValue)==1)
	{
		file->BinaryRead(GetData(),size);
	}
	else
	if(IsVect3Df<Im3DValue>())
	{
		float *data0=(float *)(void *)GetData();
		for(int i=0;i<GetNVoxels();i++)
		{
			for(int j=0;j<3;j++)
			{
				float p;
				file->BinaryRead(&p,sizeof(float));
				ReverseEndian(p,data0[i*3+j]);
			}
		}
	}
	else
	{
		Im3DValue p;
		for(int i=0;i<GetNVoxels();i++)
		{
			file->BinaryRead(&p,sizeof(Im3DValue));
			ReverseEndian(p,imageData[i]);
		}
	}
	if(!file->CheckBinaryReadComplete())
	{ThrowError("Container3D::ReadBinary bad read size (maybe wrong image type?) ");}
}


// *******************************************************
template<class Im3DValue>
void 
Container3D<Im3DValue>::Resize(const Size3D &newSize)
{
	if(GetData() && newSize==Size()){return;}
	Image3D::Resize(newSize);
	DeAllocate();
	nvoxels=width*height*depth;
	widthXheight=width*height;
	if(nvoxels>0){Allocate();}
	if(nvoxels<0){ThrowError("Container3D::Resize size<0??");}
	if(HasMask()){Mask().Resize(newSize);}
}


// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>& 
Container3D<Im3DValue>::operator=(const Container3D<Im3DValue> & other)
{
	if(other.Size()!=Size()){Resize(other.Size());}
	if (this == &other){return *this;}
	// call parent operator =
	*((Image3D*)this)=other;
	// now copy the actual image data	
	if(IsFloat<Im3DValue>() || IsDouble<Im3DValue>() || IsVect3Df<Im3DValue>() || IsInt<Im3DValue>() || IsByte<Im3DValue>() )
	{
		// memcpy is 2xfaster and works safely on these types
		memcpy((void *)GetData(),(void *)other.GetData(),sizeof(Im3DValue)*GetNVoxels());
	}
 	else
	{
		// use fast iterators for unknown types
		const_iteratorFast pOrig;
		iteratorFast p;
		for (p = begin(),pOrig=other.begin(); p != end(); p++,pOrig++)
		{
			*p = *pOrig;
		}		
	}
	
	return *this;
}

// *******************************************************
template<class Im3DValue>
template<class OtherImageType>
Container3D<Im3DValue>& 
Container3D<Im3DValue>::operator=(const OtherImageType & other)
{
	if(other.Size()!=Size()){Resize(other.Size());}

	typename OtherImageType::const_iteratorFast pOrig;
	iteratorFast p;
	for (p = begin(),pOrig=other.begin(); p != end(); p++,pOrig++)
	{
		*p = (Im3DValue)*pOrig;
	}		
	
	properties=other.properties;
	
	return *this;
}

// *******************************************************
template<class Im3DValue>
bool 
Container3D<Im3DValue>::operator==(const Container3D<Im3DValue> & other) const
{
	if(other.Size()!=Size()){return false;}

	const_iteratorFast pOrig;
	const_iteratorXYZ p;
	for (p = begin(),pOrig=other.begin(); p != end(); p++,pOrig++)
	{
		if(!((*p) == (*pOrig))){return false;}
	}		
	
	if(!(properties==other.properties)){return false;}
	
	return true;
}


// *******************************************************
template<class Im3DValue>
void
Container3D<Im3DValue>:: Allocate()
{
	DeAllocate();
	try
	{
//  			mprintf("Container3D::Allocate: nvoxels:%d sizeof element:%d\n",nvoxels,sizeof(Im3DValue));
//  			mprintf("Container3D::Allocate: %d %d %d\n",Width(),Height(),Depth());
		imageData=new Im3DValue[GetNVoxels()];		
		if(!imageData)
		{
			ThrowError("Container3D::Allocate: allocate failed. nvoxels:%d sizeof element:%d\n",GetNVoxels(),sizeof(Im3DValue));
		}
		imageDataEnd=imageData+GetNVoxels();
	}
	catch(...)
	{
		ThrowError("Container3D::Allocate: allocate failed. nvoxels:%d sizeof element:%d\n",GetNVoxels(),sizeof(Im3DValue));
	}
}

// *******************************************************
template<class Im3DValue>
void 
Container3D<Im3DValue>::DeAllocate()
{
	if(imageData){delete [] imageData;}
	imageData=NULL;
	imageDataEnd=NULL;
}

// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>::~Container3D()
{
	DeAllocate();
}

// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>::Container3D(int _width, int _height, int _depth) : 
	Image3D(_width,_height,_depth),
	imageData(NULL),imageDataEnd(NULL)
{
	Resize(Size3D(_width,_height,_depth));
}
// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>::Container3D():
	Image3D(),
	imageData(NULL),imageDataEnd(NULL)
{
	Resize(Size3D(0,0,0));
}

// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>::Container3D(int cubeSideSize) :
	Image3D(cubeSideSize,cubeSideSize,cubeSideSize),
	imageData(NULL),imageDataEnd(NULL)
{
	Resize(Size3D(cubeSideSize,cubeSideSize,cubeSideSize));
}

// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>::Container3D(const Size3D &size):
	Image3D(size),
	imageData(NULL),imageDataEnd(NULL)
{
	Resize(size);
}

// *******************************************************
template<class Im3DValue>
Container3D<Im3DValue>::Container3D(const Container3D & other) :
	Image3D(other),
	imageData(NULL),imageDataEnd(NULL)
{
	Resize(other.Size());
	(*this)=other;
}

#endif // _Container3D_hxx
