//------------------------------------------------------------
// $Id: volumeGridInfo.h,v 1.2 2000/05/15 14:20:43 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 define grid info data for volume
// 	Volume Rendeing 用
//	Copyright (C) 1998 YAMAUCHI Hitoshi 山内 斉
//
// *** volume の実体は grid である。
//     -> 内部構造の grid を意識しない場合には volume という名前でアクセス
//
// 1.  g---g
//     |   | voxel(voxel 値はこれらの値から補間する。lower left を代表
//     g---g       値にするなどいろいろ考えられる)
//     |<->|
// 	voxel length
//   g 点が grid 位置
// 
//
// class GlobalGridInfoClass 	: public SerializableIF 
// class SubGridInfoClass    	: public SerializableIF 
// class EffectiveGridInfoClass : public SerializableIF 
//------------------------------------------------------------
#ifndef _volumeGridInfo_h
#define _volumeGridInfo_h 1

#include <assert.h>
#include <iostream.h>
#include "YorN.h"
#include "IntVec3.h"
#include "Vector3.h"
#include "GridColor.h"

//------------------------------------------------------------
// grid ID の特殊な値 (id >= 0 の場合には ID 番号)
//------------------------------------------------------------
enum SpecialGridID_e {
    UNINITIALIZED_gridid = -2,	// 未初期化 grid 
    NULLVOLUME_gridid 	 = -1,	// NULL volume 用
    MIN_VALID_NUM_gridid = 0	// valid な ID の最小値
};

//------------------------------------------------------------
// 分割元の大域的な grid の情報
//------------------------------------------------------------
class GlobalGridInfoClass{ //: public SerializableIF { としたいが overhead から
protected:
    int     mID;		// global な ID (volume の分割元 ID)
    IntVec3 mResolSize;		// 分割元の grid サイズ [0, size) が accessible
    Vector3 mMinPos;		// global volume の物理的最小位置 (eg. unit: m)
    Vector3 mMaxPos;		//    〃       	 の物理的最大位置
    double  mVoxelEdgeLen;	// voxel の一辺の物理的長さ 	  (eg. unit: m)
    int	    mVolumeRGBDepth;	// RGB 色の depth

public:
    GlobalGridInfoClass(void)
    {
	this->init();
    }
    GlobalGridInfoClass(int volumeID)
    {
	this->init();
	assert(volumeID >= 0);
	this->mID = volumeID;
    }
    GlobalGridInfoClass(const GlobalGridInfoClass& rhs)    
    {
	this->mID		= rhs.mID;
	this->mResolSize	= rhs.mResolSize;
	this->mMinPos	    	= rhs.mMinPos;		
	this->mMaxPos	    	= rhs.mMaxPos;
	this->mVoxelEdgeLen 	= rhs.mVoxelEdgeLen;
	this->mVolumeRGBDepth	= rhs.mVolumeRGBDepth;
    }
    const GlobalGridInfoClass operator=(const GlobalGridInfoClass& rhs)
    {
	if(this != &rhs)
	{
	    this->mID			= rhs.mID;
	    this->mResolSize		= rhs.mResolSize;
	    this->mMinPos		= rhs.mMinPos;		
	    this->mMaxPos		= rhs.mMaxPos;
	    this->mVoxelEdgeLen 	= rhs.mVoxelEdgeLen;
	    this->mVolumeRGBDepth	= rhs.mVolumeRGBDepth;
	}
	return(*this);
    }

    void init(void)
    {
	this->mID	    = UNINITIALIZED_gridid;
	this->mVoxelEdgeLen = -1.0;
	this->mVolumeRGBDepth	= -1;
    }
    void initAll(const int anID,		const IntVec3& resolSize, 
		 const Vector3& minpos,		const Vector3& maxpos, 
		 const double voxelEdgeLen, 	const int volumeRGBDepth)
    {
  	this->setID(anID);
	this->setResolSize(resolSize);
	this->mMinPos		= minpos;		
  	this->mMaxPos		= maxpos;		
	this->setVoxelEdgeLen(voxelEdgeLen);
	this->setRGBDepth(volumeRGBDepth);
    }
    void initAsNullVolume(void)
    {
  	this->mID		= NULLVOLUME_gridid;
  	this->mResolSize.set(0, 0, 0);
  	this->mMinPos.set(0.0, 0.0, 0.0);		
 	this->mMaxPos.set(1.0, 1.0, 1.0);		
  	this->mVoxelEdgeLen 	= 1.0;
	this->mVolumeRGBDepth	= 0;
    }

    YorN isInited(void) const 
    {
	// ID だけではわからない．本当は全て調べるべきだが，
	// ここまでにとどめておく
    	if((this->mVoxelEdgeLen > 0.0) &&
	   (this->mID 		>= MIN_VALID_NUM_gridid))
	{
	    return(YES);
	}
	else
	{
	    return(NO);	    
	}
    }
    
    YorN isNullVolume(void) const
    {
	return((this->mID == NULLVOLUME_gridid) ? YES : NO);
    }

    void setID(int anID) 
    {
 	if(anID < MIN_VALID_NUM_gridid)
 	{
 	    cerr << "Error! : GlobalGridInfoClass:: init : "
		<< "invalid id : " << anID << endl;	exit(1);
 	}
	this->mID = anID;
    }
    void setResolSize(const IntVec3& resolSize)
    {
	for(int i = 0; i < 3; i++)
 	{
 	    if(resolSize.get(i) < 0)
 	    {
		cout <<  "Error! : GlobalGridInfoClass:: init : " << 
		    "invalid size[" << i << "]= " << resolSize.get(i) << endl;
		exit(1);
 	    }
 	}	    
	this->mResolSize = resolSize;
    }
    void setMinPos(const Vector3& minpos)
    {
	this->mMinPos 	= minpos;
    }
    void setMaxPos(const Vector3 & maxpos) 
    {
	this->mMaxPos 	= maxpos;
    }
    void setVoxelEdgeLen(double voxelEdgeLen)
    {
 	if(voxelEdgeLen < 0.0)
 	{
 	    cout <<  "Error! : GlobalGridInfoClass:: init : "
		<< "invalid voxelEdgeLen " << voxelEdgeLen << endl;  
	    exit(1);
 	}
	this->mVoxelEdgeLen	= voxelEdgeLen;
    }
    void calcVoxelEdgeLen(void)
    {
	Vector3 physicsize = this->getVolumePhysicSize();
	this->mVoxelEdgeLen = physicsize.get(Vector3::X) / 
	    double(this->mResolSize.get(IntVec3::X) - 1);
	assert(this->mVoxelEdgeLen > 0.0);
	assert((fabs(physicsize.get(0) - physicsize.get(1)) < 0.001) &&	// 適当
	       (fabs(physicsize.get(0) - physicsize.get(2)) < 0.001));
    }
    void setRGBDepth(int volumeRGBDepth) 
    {
	if((volumeRGBDepth < 0) || (volumeRGBDepth > GridColorMAX_DEPTH))
	{
	    cout << "Error! : GlobalGridInfoClass::setRGBDepth : "
	    	"illegal color depth." << endl;
	    exit(1);
	}
	this->mVolumeRGBDepth = volumeRGBDepth;
    }

    int    getID(void) const 		     { return(this->mID);	}
    const  IntVec3& getResolSize(void) const { return(this->mResolSize);}
    const  Vector3& getMinPos(void) const    { return(this->mMinPos);	}
    const  Vector3& getMaxPos(void) const    { return(this->mMaxPos);	}
    double getVoxelEdgeLen(void) const       { return(this->mVoxelEdgeLen);}
    int    getRGBDepth(void) const	     { return(this->mVolumeRGBDepth);}

    Vector3 getVolumePhysicSize(void) const
    {
	Vector3 ret = this->mMaxPos - this->mMinPos;
	return(ret);
    }

    void print(void) const
    {
	this->print("");
    }
    void print(const char *pHead) const
    {
	cout << pHead << "GlobalGridInfoClass" 		      << endl;
	cout << pHead << "\t::ID\t" 	      << this->mID << endl;
	cout << pHead << "\t::ResolSize\t"    << this->mResolSize     << endl;
	cout << pHead << "\t::MinPos\t"       << this->mMinPos 	      << endl;
	cout << pHead << "\t::MaxPos\t"       << this->mMaxPos 	      << endl;
	cout << pHead << "\t::VoxelEdgeLen\t" << this->mVoxelEdgeLen  << endl;
	cout << pHead << "\t::VolumeRGBDepth\t"<< this->mVolumeRGBDepth<<endl;
    }

    friend int operator == (const GlobalGridInfoClass &a, 
			    const GlobalGridInfoClass &b)
    {
	return((a.mID 		  == b.mID) 		&&
	       (a.mResolSize 	  == b.mResolSize) 	&&
	       (a.mMinPos	  == b.mMinPos)		&&
	       (a.mMaxPos	  == b.mMaxPos)		&&
	       (a.mVoxelEdgeLen	  == b.mVoxelEdgeLen)	&&
	       (a.mVolumeRGBDepth == b.mVolumeRGBDepth));
    }

    //----------------------------------------
    // SerializableIF : double の表現が同じ計算機でないと上手くいかない    
    //----------------------------------------
    int getSerializedSize(void) const;
    double *newSerializedArray(void);
    void deserialize(double *pBuf);
};


//------------------------------------------------------------
// 分割された部分 grid 情報
//------------------------------------------------------------
class SubGridInfoClass { //  : public SerializableIF としたいが overhead から
protected:
    Vector3 mMinPos;		// subvolume 位置 Min
    Vector3 mMaxPos;		//    〃          Max
    IntVec3 mSpcPos;		// この volume の存在する部分空間 index
    IntVec3 mGridResMin;	// subvolume の存在範囲 Min を voxel 単位で
    IntVec3 mGridResMax;	// subvolume の存在範囲 Max を voxel 単位で

public:
    SubGridInfoClass(void)
    {
	this->mSpcPos.set(-1, -2, -3); // set as not initialized
    }
    SubGridInfoClass(const SubGridInfoClass& rhs)
    {
	this->mMinPos 	= rhs.mMinPos;
	this->mMaxPos	= rhs.mMaxPos;
	this->mSpcPos	= rhs.mSpcPos;
	this->mGridResMin = rhs.mGridResMin;
	this->mGridResMax = rhs.mGridResMax;
    }
    const SubGridInfoClass& operator=(const SubGridInfoClass& rhs)
    {
	if(this != &rhs)
	{
	    this->mMinPos = rhs.mMinPos;
	    this->mMaxPos = rhs.mMaxPos;
	    this->mSpcPos = rhs.mSpcPos;
	    this->mGridResMin = rhs.mGridResMin;
	    this->mGridResMax = rhs.mGridResMax;
	}
	return(*this);
    }
    // ~SubGridInfoClass(void);
    void initAll(const Vector3& minpos, const Vector3& maxpos, 
		 const IntVec3& spcpos, 
		 const IntVec3& resolMin,  const IntVec3& resolMax)
    {
     	this->mMinPos 	= minpos;
     	this->mMaxPos	= maxpos;
     	this->mSpcPos	= spcpos;
	this->mGridResMin = resolMin;
	this->mGridResMax = resolMax;
    }
    void initAsNullVolume(void)
    {
	this->mMinPos.set(0.0, 0.0, 0.0);
	this->mMaxPos.set(1.0, 1.0, 1.0);
	this->mSpcPos.set(  0,   0,   0);
	this->mGridResMin.set(-1, -1, -1);
	this->mGridResMax.set( 0,  0,  0);
    }
    YorN isInited(void) const 
    {
	if(this->mSpcPos.get(IntVec3::X) < 0)
	{
	    return(NO);
	}else
	{
	    return(YES);
	}
    }

    void  setMinPos(const Vector3& rhs)  {  this->mMinPos = rhs;	}
    void  setMaxPos(const Vector3& rhs)  {  this->mMaxPos = rhs;	}
    void  setSpcPos(const IntVec3& rhs)  {  this->mSpcPos = rhs;	}
    void  setMinIdx(const IntVec3& rhs)  {  this->mGridResMin = rhs;	}
    void  setMaxIdx(const IntVec3& rhs)  {  this->mGridResMax = rhs;	}
    const Vector3& getMinPos(void) const {  return(this->mMinPos);	}
    const Vector3& getMaxPos(void) const {  return(this->mMaxPos);	}
    const IntVec3& getSpcPos(void) const {  return(this->mSpcPos);	}
    const IntVec3& getMinIdx(void) const {  return(this->mGridResMin);	}
    const IntVec3& getMaxIdx(void) const {  return(this->mGridResMax);	}
    IntVec3 getResolSize(void) const
    {
	static const IntVec3 one(1, 1, 1);
	IntVec3 r = this->mGridResMax - this->mGridResMin + one;
	return(r);
    }

    // print
    void print(void) const
    {
	cout << "SubGridInfoClass\n\t::minpos\t" << this->mMinPos << endl;
	cout << "\t::maxpos\t"		<< this->mMaxPos << endl;
	cout << "\t::spcpos\t"		<< this->mSpcPos << endl;
	cout << "\t::ResolMin\t"	<< this->mGridResMin << endl;
	cout << "\t::ResolMax\t" 	<< this->mGridResMax << endl;
    }

    friend int operator == (const SubGridInfoClass &a, 
			    const SubGridInfoClass &b)
    {
	return((a.mMinPos	== b.mMinPos)		&&
	       (a.mMaxPos	== b.mMaxPos)		&&
	       (a.mSpcPos	== b.mSpcPos)		&&
	       (a.mGridResMin	== b.mGridResMin)	&&
	       (a.mGridResMax	== b.mGridResMax));
    }


    //----------------------------------------
    // SerializableIF : double の表現が同じ計算機でないと上手くいかない    
    //----------------------------------------
    int getSerializedSize(void) const;
    double *newSerializedArray(void);
    void deserialize(double *pBuf);
};    


//------------------------------------------------------------
// 部分ボリュームの有効な範囲
// 	volume の色計算に有効な grid の Index など。各 volume はある部
// 	分空間に属する。しかし、たとえば補間などのために少し距離の離れ
// 	た grid  を参照する場合がある。そのためある部分空間が保持する 
// 	volume の  grid index の値がいくつかを知る必要がある。 
// 有効範囲は [min, max] である
//------------------------------------------------------------
class EffectiveGridInfoClass {
protected:
    int     mInitedEffectiveRange; // 初期化済み?
    Vector3 mEfMinPos;		   // 有効範囲最小位置
    Vector3 mEfMaxPos;		   // 有効範囲最大位置

public:
    EffectiveGridInfoClass(void)
    {
	this->mInitedEffectiveRange = NO;
    }
    EffectiveGridInfoClass(const EffectiveGridInfoClass& rhs)
    {
	this->mInitedEffectiveRange	= rhs.mInitedEffectiveRange;
	this->mEfMinPos			= rhs.mEfMinPos;
	this->mEfMaxPos			= rhs.mEfMaxPos;
    }
    const EffectiveGridInfoClass& operator=(const EffectiveGridInfoClass& rhs)
    {
	if(this != &rhs)
	{
	    this->mInitedEffectiveRange	= rhs.mInitedEffectiveRange;
	    this->mEfMinPos		= rhs.mEfMinPos;
	    this->mEfMaxPos		= rhs.mEfMaxPos;
	}
	return(*this);
    }
    ~EffectiveGridInfoClass(void){};

    friend int operator == (const EffectiveGridInfoClass &a, 
			    const EffectiveGridInfoClass &b)
    {
	return((a.mInitedEffectiveRange	== b.mInitedEffectiveRange) 	&& 
	       (a.mEfMinPos		== b.mEfMinPos)			&&
	       (a.mEfMaxPos		== b.mEfMaxPos));
    }
    
public:
    void initAll(const Vector3& eMinpos,  const Vector3& eMaxpos) 
    {
	this->mEfMinPos		= eMinpos;
	this->mEfMaxPos		= eMaxpos;
	this->mInitedEffectiveRange	= YES; // 初期化済みとする
    }
    void initAsNullVolume(void)
    {
	this->mInitedEffectiveRange = NULLVOLUME_gridid;
    }

    int     isInited(void) const { return(this->mInitedEffectiveRange);	}
    void    invalidate(void)	 { this->mInitedEffectiveRange = NO;	}
    Vector3 getEfMinPos(void) const { return(this->mEfMinPos);		}
    Vector3 getEfMaxPos(void) const { return(this->mEfMaxPos);		}

    void print(void) const 
    {
	cout << "----- effective grid info : ";
	switch(this->mInitedEffectiveRange)
	{
	case YES:
	    cout << "Initialized" << endl;			break;
	case NO:
	    cout << "Not initialized" << endl;			break;
	case NULLVOLUME_gridid:
	    cout << "Initialized as Null volume" << endl;	break;
	default:
	    cout << "Error! : illegal state" << endl;		break;
	}
	cout << "effective region (" << this->mEfMinPos << ")-(" << 
		this->mEfMaxPos << ")" << endl;
    }
    
public:
    //----------------------------------------
    // SerializableIF 
    //----------------------------------------
    int getSerializedSize(void) const;
    double *newSerializedArray(void);
    void deserialize(double *pBuf);
};

#endif				// #ifndef _volumeGridInfo_h
