//------------------------------------------------------------
// $Id: PEvolume.cc,v 1.2.2.1 2000/06/14 15:00:10 yamauchi Exp $ 
//------------------------------------------------------------
//	PEvolume.cc Processing Element functions for volume
//	1998/11/17
//	Copyright (C) 1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include "SLList.h"
#include "PE.h"
#include "mpi2exit.h"
#include "mpi2_funcs.h"
#include "volume.h"


// 体積を持つか
static YorN isVolume(VolumeClass *pVol)
{
    IntVec3 resolSize = (pVol->getSubGrid()).getResolSize();

    for(int xyz = 0; xyz < 3; xyz++)
    {
	if(resolSize.get(xyz) <= 1)
	{
	    // 1 の場合には grid 数なので体積を持っていない．
	    cout << "Error! : isVolume :: This volume has no volume. "
	    "resolSize[" << xyz << "] = " << resolSize << endl;
	    return(NO);
	}
    }
    return(YES);
}

/**------------------------------------------------------------
 * volume が正しい volume かを調べる
 * 	 体積を持つか
 *  	 fill されているか (= 内容は 0 以上であるか)
 *
 *    @param 調べる volume のリスト
 *-----------------------------------------------------------*/
static void checkAllVolumes(VolumeSetClass * pVolSet)
{
    const int totalVolNum = pVolSet->getTotalLocalSpaceNum();
    for(int i = 0; i < totalVolNum; i++)
    {
	SLList < VolumeClass * > *pVolList = pVolSet->getListAs1DimArray(i);
	for(Pix p = pVolList->first(); p != NULL; pVolList->next(p))
	{
	    if(isVolume((*pVolList)(p)) == NO)
	    {
		abort();
	    }
	    if(((*pVolList)(p)->isAllFill()) == NO)
	    {
		abort();
	    }
	}
    }
}

/**-----------------------------------------------------------
 * volume のリストからそのリストに属する volume 群の subgridinfo を生
 * 成する． 
 * このとき，SubVolume の MinPos, MaxPos は grid index から求める
 *------------------------------------------------------------*/
static SubGridInfoClass 
getSubGridInfoFromVolList(SLList < VolumeClass * > *pSameSpIdVlist)
{
    // この spcpos にいる grid の範囲を求める
    Pix firstPix   = pSameSpIdVlist->first();	
    const IntVec3 spcpos = (*pSameSpIdVlist)(firstPix)->getSubVolumeSpcPos();
    const int volumeID   = (*pSameSpIdVlist)(firstPix)->getID();

    int gridMin[3], gridMax[3];
    gridMin[0] = gridMin[1] = gridMin[2] = INT_MAX;
    gridMax[0] = gridMax[1] = gridMax[2] = -1;
    Pix pix;
    for(pix = pSameSpIdVlist->first(); pix != NULL; pSameSpIdVlist->next(pix))
    {
	// check : spcpos, volumeID は全て同じである必要あり
	if(spcpos != (*pSameSpIdVlist)(pix)->getSubVolumeSpcPos())
	{			
	    cout << "Error! : inconsistent spcpos." << endl;
	    commMpi2_exit(1);
	}
	if(volumeID != (*pSameSpIdVlist)(pix)->getID())
	{
	    cout << "Error! : inconsistent volume ID." << endl;
	    commMpi2_exit(1);
	}

	// get grid max, grid min
	for(int xyz = 0; xyz < 3; xyz++)
	{
	    if((*pSameSpIdVlist)(pix)->getGridMinIdx(xyz) < gridMin[xyz])
	    {
		gridMin[xyz] = (*pSameSpIdVlist)(pix)->getGridMinIdx(xyz);
	    }
	    if(gridMax[xyz] < (*pSameSpIdVlist)(pix)->getGridMaxIdx(xyz))
	    {
		gridMax[xyz] = (*pSameSpIdVlist)(pix)->getGridMaxIdx(xyz);
	    }
	}
    }
    GlobalGridInfoClass gginfo = (*pSameSpIdVlist)(firstPix)->getGlobalGrid();

    Vector3 minpos = 
	getPosition(gginfo.getMinPos(), 
		    gginfo.getVoxelEdgeLen(),
		    Vector3(double(gridMin[0]), double(gridMin[1]), 
			    double(gridMin[2])));
    Vector3 maxpos = 
	getPosition(gginfo.getMinPos(), 
		    gginfo.getVoxelEdgeLen(),
		    Vector3(double(gridMax[0]), double(gridMax[1]), 
			    double(gridMax[2])));

    SubGridInfoClass sginfo;
    sginfo.initAll(minpos, maxpos, spcpos, IntVec3(gridMin), IntVec3(gridMax));

    return(sginfo);
}

//------------------------------------------------------------
// initialize volume set
//	分配された volume は細切れなため、部分空間ごとに再構成する
//------------------------------------------------------------
// ある部分空間指標を持つ同じ volume ID の volume を 1つ 取り出す
//      部分空間指標と volume ID によってふりわける
//	arg: 同一 spcpos, volume ID の volume list
//------------------------------
void PEClass::
getSetOfSameSpcIDVolume(SLList < VolumeClass * > *pSameSpIdVlist,
			SLList < VolumeClass * > *pVolPtrListCopy)
{
    Pix firstPix   = pVolPtrListCopy->first();
    IntVec3 spcpos = (*pVolPtrListCopy)(firstPix)->getSubVolumeSpcPos();
    const int id   = (*pVolPtrListCopy)(firstPix)->getID();
    SLList < VolumeClass * > restVlist;

    for(Pix pix = pVolPtrListCopy->first(); pix != NULL;
	pVolPtrListCopy->next(pix))
    {
	VolumeClass *pVol = (*pVolPtrListCopy)(pix);
	if((pVol->getID() == id) && 
	   (pVol->getSubVolumeSpcPos() == spcpos))
	{
	    pSameSpIdVlist->append(pVol);
	}else
	{
	    restVlist.append(pVol);
	}
    }
    (*pVolPtrListCopy) = restVlist; // 残りを戻す
}

//------------------------------------------------------------
// initialize volume set main
//	分配された volume は細切れなため、部分空間ごとに再構成する
//	ここでの初期化は受信に伴う初期化のみ
//	何か計算用に初期化したい場合には， initializeVolumes() を利用
//------------------------------------------------------------
void PEClass::
initRecvVolumeSet(void)
{
    int subspcInPE[3];
    this->getHavingSubSpcNum(subspcInPE);
    IntVec3 localTotalSpcNum(subspcInPE[0], subspcInPE[1], subspcInPE[2]);
    this->mVolSet.init(localTotalSpcNum);
    
    // 統合前の volume の effective range を計算し範囲が正しいかを求める
    Pix pix;
    for(pix = this->mVolList.first(); pix != NULL; this->mVolList.next(pix))
    {
	IntVec3 spcpos = (this->mVolList(pix)->getSubGrid()).getSpcPos();
	Vector3 cellMinPos = this->mSpcParam.getMinPosOfSpcIdx(spcpos);
	Vector3 cellMaxPos = this->mSpcParam.getMaxPosOfSpcIdx(spcpos);

	if((this->mVolList(pix))->calcEffectiveGridRange(cellMinPos,cellMaxPos)
	   == NO)
	{
	    cout << "Error! : PEClass::initVolumeSet" 		  << endl;
	    this->mVolList(pix)->print();
	    cout << "Error! : Can not calc effective grid range." << endl;
	    this->exitprog();
	}
 	// DEBUG : (this->mVolList(pix))->print();
    }

    // ここで，ポインタリストのコピーを生成し，そのコピーに対して振り
    // 分けの処理をしようと思ったが，ポインタの先で統合したものなどが
    // あるので，コピーには意味がない．

    while(!(this->mVolList.empty()))
    {
	// 部分空間指標と volume ID によって volume をふりわける
	SLList < VolumeClass * > sameSpIdVlist; // 同じ spcpos の volume 入れ
	this->getSetOfSameSpcIDVolume(&sameSpIdVlist, &this->mVolList);
	
	// sameSpIdVlist に同一 spcpos && 同一 ID の volume が集まった。
	// これらを統合させる
	
	// volume の統合と送信時の部分 volume の消去
	GlobalGridInfoClass gginfo = (sameSpIdVlist.front())->getGlobalGrid();
	SubGridInfoClass    sginfo = getSubGridInfoFromVolList(&sameSpIdVlist);
	
	VolumeClass *pUnifiedVol = new VolumeClass;
	pUnifiedVol->initGlobalGridInfo(gginfo);
	pUnifiedVol->initSubGridInfo(sginfo);

	// volume (pUnifiedVol) に pSubGrid を統合していく．
	while(!(sameSpIdVlist.empty()))
	{
	    VolumeClass *pSubGrid = sameSpIdVlist.remove_front();
	    assert(pSubGrid->isAllFill() == YES);
	    pUnifiedVol->addSubVolume(pSubGrid); 
	    delete pSubGrid;	// delete !
	}
	assert(pUnifiedVol->isAllFill() == YES);

	IntVec3 nowSpcpos = sginfo.getSpcPos();
	Vector3 cellMinPos = this->mSpcParam.getMinPosOfSpcIdx(nowSpcpos);
	Vector3 cellMaxPos = this->mSpcParam.getMaxPosOfSpcIdx(nowSpcpos);

	// grid の有効範囲の計算
	if(pUnifiedVol->calcEffectiveGridRange(cellMinPos, cellMaxPos) == NO) 
	{
	    cout << "Error! : unified volume has something wrong." << endl;
	    this->exitprog();
	}
	
	// volume の登録 (本来は終了時に pUnifiedVol を delete するべきだがし
	// ていない。mVolSet 内部にはポインタを渡しているのでここで 
	// delete できない。
	this->mVolSet.add(pUnifiedVol->getSubVolumeSpcPos(), pUnifiedVol);
    }
    // 全ての volume 以下の条件を満たすか
    // 	 体積を持つ
    // 	 fill されている
    // 	 内容は 0 以上である．
    checkAllVolumes(&(this->mVolSet));
}
