//------------------------------------------------------------
// $Id: searchNextSpcDDA.h,v 1.3 2000/05/21 18:43:03 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 
// 	次空間探索用
//	いろいろ紆余曲折があったができてみればなんのことはない。
//	つまりは DDA ユニットの独立性の高いものである。
//	1998年1月27日(火)
//	Copyright (C) 1997--1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#ifndef _searchNextSpcDDA_h
#define _searchNextSpcDDA_h 1

#include "mpi2.h"
#include "Vector3.h"
#include "IntVec3.h"
#include "ray.h"
#include "array3d.h"
#include "objectInfoInASpace.h"

//------------------------------------------------------------
// 内部状態のある interface などを考えていたがそれは普通のクラスだ。
//     C++ で virtual なものを派生した時には constructor と destructor 
//     の扱いが繁雑すぎる。
//------------------------------------------------------------
// 3DDDA 法を用いた次空間探索
//------------------------------------------------------------
class searchNextSpcDDA {
private:
    searchNextSpcDDA(const searchNextSpcDDA& rhs);
    const searchNextSpcDDA& operator=(const searchNextSpcDDA& rhs);

public:				 
    // 3DDDA の計算に必要となるデータ public なアクセスに注意
    Vector3  	  mPartVolMin; 	 // DDA を適用する空間の最小位置
    double   	  mCellLength; 	 // DDA で traverse するセルの大きさ
    int		  mSpcIdxMin[3]; // cell の最小 index (subvolume がある
    int		  mSpcIdxMax[3]; // 同最大 index:      ので0が最小ではない。)
    int      	  mRayPropSkipMethod;// Skip 方法(Skip するしない)
    int 	  mNetworkType;	 // ネットワークの形状

    // 空間中の物体の存在状況
    array3dClass < objectExistInASpaceClass > mObjInSpcMap; 

    // derived values
    double 	  mCellHalf; // cell の長さの半分

protected:				 
    void exitIfIllegalParam(double cellLength, const int *pSpcIdxMin, 
			    const int *pSpcIdxMax, 
			    int rayPropSkipMethod, int networkType);

protected:				 
    DIR_3D getDirByDDA(DDAInfoClass *pDInfo);
    void setTraverseAxisNVector(DDAInfoClass* pDInfo, const Vector3& rayDir);
    YorN testCanGo(IntVec3* pSpcPos, DIR_3D dir) const;
    void goNextSpace(IntVec3* pSpcPos, DIR_3D dir) const;
    YorN testAndGoIfItCan(IntVec3* pSpcPos, DIR_3D dir) const
    {
	// もし移動できるのならその方向に移動し、YES を返す。
	// できない場合には移動せずに NO を返す。
	if(this->testCanGo(pSpcPos, dir) == YES)
	{
	    this->goNextSpace(pSpcPos, dir);
	    return(YES);
	}else
	{
	    return(NO);
	}
    } 

    // volume 部分と共有するための次空間探索 method
    // ddainfo を引数にすることで volume でも subspace でも OK!
    YorN moveRayToNextSubspaceNoSkip(DDAInfoClass *pDinfo, 
				     const Vector3& rayPos,// 視点
				     const Vector3& rayDir,//視線ベクトル
				     IntVec3      *pSpcPos);
    
public:
    searchNextSpcDDA(void);
    virtual ~searchNextSpcDDA(void);

    // この有効範囲は [SpcIdxMin, SpcIdxMax] である．この interface どうするか
    void initWithoutObjInSpcMap(const Vector3& defspcmin, // global defspc min
				double celllen, 
				const int *pSpcIdxMin, 
				const int *pSpcIdxMax,
				int  	   skipMethod,
				int        systemNetworkType);
    void initObjInSpcMap(array3dClass < objectExistInASpaceClass > *pObjMap);
    YorN isObject(const IntVec3& spcpos)
    {
	int spcidx[3];
	spcpos.getAsArray(spcidx);
	return(this->mObjInSpcMap.get(spcidx).isObj());
    }

    void initializeDDA(DDAInfoClass* pDInfo, 
		       const Vector3& rayPos, const Vector3& rayDir,
		       const IntVec3& raySpcPos);

    // 本来は subclass を作成するか，これを interface にするべきだが，
    // 今回は見送る
    // ray の位置を次空間に変更する : Skip 無し
    YorN moveRayToNextSubspaceNoSkip(Ray *pRay);

    // 素朴なスキップ
    YorN moveRayWithSobokuSkip(Ray *pRay);
    
    // volume 移動用
    YorN isValidASpcpos(const IntVec3& spcpos);
    void forceMoveRayToNextSpace(DDAInfoClass *pDinfo, 
				 const Vector3& rayPos, // 視点
				 const Vector3& rayDir, //視線ベクトル
				 IntVec3      *pSpcPos);

};

#endif				// #ifndef _searchNextSpc_h

