/*
 * $Id: SpaceParam.h,v 1.6 2000/06/14 13:27:47 yamauchi Exp $
 *
 *	(M\pi)^2 : space parameter  
 *	1996/7/6 start as `dataCollection.h'
 *	SpcParam : 空間の情報
 *	Copyright (C) 1996--2000 YAMAUCHI Hitoshi 山内 斉
 */
#ifndef _SpaceParam_h
#define _SpaceParam_h 1

#include <assert.h>
#include "Vector3.h"
#include "IntVec3.h"
#include "mpi2exit.h"

/*
 * space parameter
 *  	subdivnum が set されたかで初期化されたかを見る
 */
class SpaceParam {
private:
    Vector3 mDefSpcMin,  mDefSpcMax;  // partvol は defspc を含むことに注意
    Vector3 mPartVolMin, mPartVolMax; // partvol >= defspc
    int     mSubDivNum;

    double  mCellLen;
    IntVec3 mMinDefSpcIdxNum;	// define spaceの指標の最小値(指標はこれを含む)
    IntVec3 mMaxDefSpcIdxNum;	// define spaceの指標の最大値(指標はこれを含む)
    Vector3 mAlignedDefSpcMin;	// 分割空間に合うよう拡張した定義空間位置
    Vector3 mAlignedDefSpcMax;	// partvol の分割に合わせるように計算する

public:
    SpaceParam(void)
    {
	this->init();
    }
    SpaceParam(const SpaceParam& rhs)
    {
	this->mDefSpcMin	= rhs.mDefSpcMin;
	this->mDefSpcMax	= rhs.mDefSpcMax;
	this->mPartVolMin	= rhs.mPartVolMin;
	this->mPartVolMax	= rhs.mPartVolMax;
	this->mSubDivNum	= rhs.mSubDivNum;

	this->mCellLen		= rhs.mCellLen;
	this->mMinDefSpcIdxNum	= rhs.mMinDefSpcIdxNum;
	this->mMaxDefSpcIdxNum	= rhs.mMaxDefSpcIdxNum;
	this->mAlignedDefSpcMin	= rhs.mAlignedDefSpcMin;
	this->mAlignedDefSpcMax	= rhs.mAlignedDefSpcMax;
    }
    void init(void)
    {
	this->mDefSpcMin.set(0.0,  0.0, 0.0);
	this->mDefSpcMax.set(0.0,  0.0, 0.0);
	this->mPartVolMin.set(0.0, 0.0, 0.0);
	this->mPartVolMax.set(0.0, 0.0, 0.0);
	this->mSubDivNum = 0;
	this->mCellLen   = 0.0;
	this->mMinDefSpcIdxNum.set(-1, -1, -1);
	this->mMaxDefSpcIdxNum.set(-1, -1, -1);
	this->mAlignedDefSpcMin.set(0.0, 0.0, 0.0);
	this->mAlignedDefSpcMax.set(0.0, 0.0, 0.0);
    }
    void setDefSpace(const Vector3& defspcmin, const Vector3& defspcmax)
    {
	if((defspcmin.get(Vector3::X) >= defspcmax.get(Vector3::X)) ||
	   (defspcmin.get(Vector3::Y) >= defspcmax.get(Vector3::Y)) ||
	   (defspcmin.get(Vector3::Z) >= defspcmax.get(Vector3::Z)))
	{
	    cerr << "Error! : illegal define space. max >= min " << endl;
	    cerr << "min " << defspcmin << ", max " << defspcmax << endl;
	    commMpi2_exit(1);
	}
	this->mDefSpcMin = defspcmin;
	this->mDefSpcMax = defspcmax;
    }
    void setPartVolume(const Vector3& partvolmin, const Vector3& partvolmax)
    {
	if((partvolmin.get(Vector3::X) >= partvolmax.get(Vector3::X)) ||
	   (partvolmin.get(Vector3::Y) >= partvolmax.get(Vector3::Y)) ||
	   (partvolmin.get(Vector3::Z) >= partvolmax.get(Vector3::Z)))
	{
	    cerr << "Error! : illegal partition volume. min max" << 
		" at SpaceParam::setPartVolume" << endl;
	    commMpi2_exit(1);
	}
	Vector3 volsize = partvolmax - partvolmin;
	if((volsize.get(Vector3::X) != volsize.get(Vector3::Y)) || 
	   (volsize.get(Vector3::X) != volsize.get(Vector3::Z)))
	{
	    cerr << "Error! : illegal partition volume. not cubic" <<
		" at SpaceParam::setPartVolume" << endl;
	    commMpi2_exit(1);
	}
	this->mPartVolMin = partvolmin;
	this->mPartVolMax = partvolmax;
    }
    void setSubDivNumNinitCellLen(int subdivnum)
    {
	// check subvivnum
	if(subdivnum <= 0)
	{
	    cerr << "Error! : illegal subdivnum."<<endl;	
	    commMpi2_exit(1);
	}
	this->mSubDivNum = subdivnum;

	// calculate cell length
	this->mCellLen 	= 
	    (this->mPartVolMax.get(Vector3::X) - 
	     this->mPartVolMin.get(Vector3::X)) / double(this->mSubDivNum);
	if(this->mCellLen <= 0.0)
	{
	    cerr << "Error! : partition volume is not setted." <<
		" at SpaceParam::setSubDivNum" << endl;
	    commMpi2_exit(1);
	}

	// check defined space size
	Vector3 defSpcSize =  this->mDefSpcMax - this->mDefSpcMin;
	if(defSpcSize.getNorm() == 0.0)
	{
	    cerr << "Error! : define space is not setted." <<
		" at SpaceParam::setSubDivNum" << endl;
	    commMpi2_exit(1);
	}

	// check defined space and partition volume consistency
	// partition volume includes define space.
	int i;
	for(i = 0; i < 3; i++)
	{
	    if(!
	       (((this->mPartVolMin.get(i) <= this->mDefSpcMin.get(i))) &&
		((this->mDefSpcMax.get(i)  <= this->mPartVolMax.get(i))))
		)
	    {
		cerr << "Error! : partition volume is not include "
		     << "defined space. check space info." << endl;
		commMpi2_exit(1);		
	    }
	}

	// calculate defined space index
	int dmin[3], dmax[3];
	for(i = 0; i < 3; i++)
	{			
	    dmin[i] = (int)
		(floor((this->mDefSpcMin.get(i) - this->mPartVolMin.get(i)) 
		       / this->mCellLen));
	    dmax[i] = (int)
		(ceil((this->mDefSpcMax.get(i) - this->mPartVolMin.get(i)) 
		      / this->mCellLen) - 1.0);
	    if((!((0 <= dmin[i]) && (dmin[i] <= this->mSubDivNum))) ||
	       (!((0 <= dmax[i]) && (dmax[i] <= this->mSubDivNum))))
	    {
		cerr << "min idx [" << i << "] = " << dmin[i] << ", "
		     << "max idx [" << i << "] = " << dmax[i] 
		     << ", subdivnum = " << this->mSubDivNum  << endl;
		cerr << "Error! : Inconsistency is found between "
		     << "definespace and partvol" << endl;
		commMpi2_exit(1);
	    }
	}
	this->mMinDefSpcIdxNum.set(dmin[0], dmin[1], dmin[2]);
	this->mMaxDefSpcIdxNum.set(dmax[0], dmax[1], dmax[2]);

	// calculate defined space aligned to partition volume and subdivision
	double dpmin[3], dpmax[3];
	for(i = 0; i < 3; i++)
	{
	    dpmin[i] = this->mPartVolMin.get(i) + 
		(double)(this->mMinDefSpcIdxNum.get(i)) * this->mCellLen;
	    dpmax[i] = this->mPartVolMin.get(i) + 
		(double)(this->mMaxDefSpcIdxNum.get(i) + 1) * this->mCellLen;
	}
	this->mAlignedDefSpcMin.set(dpmin[0], dpmin[1], dpmin[2]);
	this->mAlignedDefSpcMax.set(dpmax[0], dpmax[1], dpmax[2]);
    }
    double  getCellLen(void) const 	
    {
	return(this->mCellLen);	
    }
    /*
     * define space index
     */
    IntVec3 getMinDefSpcIdx(void) const
    {
	return(this->mMinDefSpcIdxNum); 	
    }
    IntVec3 getMaxDefSpcIdx(void) const
    {
	return(this->mMaxDefSpcIdxNum); 	
    }
    /*
     * attention : define space is included by partiton volume:
     *  	parvol >= defspc
     */
    /*
    Vector3 getDefSpaceMin(void) const 
    {
	return(this->mDefSpcMin);	
    }
    Vector3 getDefSpaceMax(void) const 
    {
	return(this->mDefSpcMax);	
    }
    */
    /*
     * defined space with aligned partition volume
     *   partition volumeを分割した部分空間に align して返す
     */
    Vector3 getAlignedDefSpaceMin(void) const 
    {
	return(this->mAlignedDefSpcMin);		
    }
    Vector3 getAlignedDefSpaceMax(void) const 
    {
	return(this->mAlignedDefSpcMax);	
    }

    Vector3 getPartVolumeMin(void) const
    {
	return(this->mPartVolMin);	
    }
    Vector3 getPartVolumeMax(void) const
    {
	return(this->mPartVolMax);	
    }
    int     getSubDivNum(void) const 
    {
	return(this->mSubDivNum);	
    }
    void fprint(FILE *pOut, const char*pHead) const
    {
	fprintf(pOut, "%spartition volume (%g %g %g)-(%g %g %g)\n", pHead,
		this->mPartVolMin.get(Vector3::X),
		this->mPartVolMin.get(Vector3::Y),
		this->mPartVolMin.get(Vector3::Z),
		this->mPartVolMax.get(Vector3::X),
		this->mPartVolMax.get(Vector3::Y),
		this->mPartVolMax.get(Vector3::Z));
	fprintf(pOut, "%sdefspace         (%g %g %g)-(%g %g %g)\n", pHead,
		this->mDefSpcMin.get(Vector3::X),
		this->mDefSpcMin.get(Vector3::Y),
		this->mDefSpcMin.get(Vector3::Z),
		this->mDefSpcMax.get(Vector3::X),
		this->mDefSpcMax.get(Vector3::Y),
		this->mDefSpcMax.get(Vector3::Z));
	fprintf(pOut, "%saligneddefspace  (%g %g %g)-(%g %g %g)\n", pHead,
		this->mAlignedDefSpcMin.get(Vector3::X),
		this->mAlignedDefSpcMin.get(Vector3::Y),
		this->mAlignedDefSpcMin.get(Vector3::Z),
		this->mAlignedDefSpcMax.get(Vector3::X),
		this->mAlignedDefSpcMax.get(Vector3::Y),
		this->mAlignedDefSpcMax.get(Vector3::Z));
	fprintf(pOut, "%snumber of subdivision %d\n", pHead, this->mSubDivNum);
	fprintf(pOut, "%sdefine space idx (%d %d %d)-(%d %d %d)\n", pHead, 
		this->mMinDefSpcIdxNum.get(Vector3::X),
		this->mMinDefSpcIdxNum.get(Vector3::Y),
		this->mMinDefSpcIdxNum.get(Vector3::Z),
		this->mMaxDefSpcIdxNum.get(Vector3::X),
		this->mMaxDefSpcIdxNum.get(Vector3::Y),
		this->mMaxDefSpcIdxNum.get(Vector3::Z));
	fprintf(pOut, "%scell length %g\n", pHead, this->mCellLen);
	fflush(pOut);
    }
    void print(const char *pHead) const
    {
	this->fprint(stdout, pHead);	
    }
    void printerr() const
    {
	this->fprint(stderr, "");
    }

    const SpaceParam& operator=(const SpaceParam& rhs)
    {
	if(this != &rhs)
	{
	    this->mDefSpcMin	= rhs.mDefSpcMin;
	    this->mDefSpcMax	= rhs.mDefSpcMax;
	    this->mPartVolMin	= rhs.mPartVolMin;
	    this->mPartVolMax	= rhs.mPartVolMax;
	    this->mSubDivNum	= rhs.mSubDivNum;
	    this->mCellLen	= rhs.mCellLen;	
	    this->mMinDefSpcIdxNum	= rhs.mMinDefSpcIdxNum;
	    this->mMaxDefSpcIdxNum	= rhs.mMaxDefSpcIdxNum;
	    this->mAlignedDefSpcMin	= rhs.mAlignedDefSpcMin;
	    this->mAlignedDefSpcMax	= rhs.mAlignedDefSpcMax;
	}
	return(*this);
    }


    /*
     * positioning funcs
     */
    Vector3 getMinPosOfSpcIdx(const IntVec3 &spcpos) const
    {
	Vector3 spcposOfVec3(double(spcpos.get(0)), double(spcpos.get(1)),
			     double(spcpos.get(2)));
	Vector3 ret = this->mPartVolMin + (this->mCellLen * spcposOfVec3);
	return(ret);
    }
    Vector3 getMaxPosOfSpcIdx(const IntVec3 &spcpos) const
    {
	Vector3 spcposOfVec3p1(double(spcpos.get(0) + 1), 
			       double(spcpos.get(1) + 1),
			       double(spcpos.get(2) + 1));
	Vector3 ret = this->mPartVolMin + (this->mCellLen * spcposOfVec3p1);
	return(ret);
    }

    /*
     * cell index -> position
     */
    Vector3 getCellMinPos(const IntVec3 &idx) const
    {
	double dx = this->mCellLen * (double)(idx.get(IntVec3::X));
	double dy = this->mCellLen * (double)(idx.get(IntVec3::Y));
	double dz = this->mCellLen * (double)(idx.get(IntVec3::Z));
	
	Vector3 ret = this->mPartVolMin + Vector3(dx, dy, dz);
	return(ret);
    }
    Vector3 getCellMaxPos(const IntVec3 &idx) const
    {
	double dx = this->mCellLen * (double)(1 + idx.get(IntVec3::X));
	double dy = this->mCellLen * (double)(1 + idx.get(IntVec3::Y));
	double dz = this->mCellLen * (double)(1 + idx.get(IntVec3::Z));
	
	Vector3 ret = this->mPartVolMin + Vector3(dx, dy, dz);
	return(ret);
    }
};


#endif				// #define _SpaceParam_h 1
