//------------------------------------------------------------
// $Id: calcRadioFRG.cc,v 1.1.1.1 1999/11/25 06:26:09 yamauchi Exp $
//------------------------------------------------------------
//	calcRadioFRG.cc
//	calculation of First Ray Generation unit for radiosity 
//	1996/8/9
//	(C) Copyright YAMAUCHI Hitoshi 山内 斉
//
//	初期レイ発生
//------------------------------------------------------------
#include "Option.h"
#include "FileInfo.h"
#include "hemicube.h"
#include "mpi2_funcs.h"
#include "ProfileMon.h"

// stand alone 
#ifdef FOR_STANDALONE
#include "StandAloneModeObjects.h"
#else
#include "PE.h"
#endif

//------------------------------------------------------------
// get unshot radiosity energy from a objList
//	渡された object list に連なっている patch の未放射エネルギを返す
//------------------------------------------------------------
double getTotalUnshotRadiosityEnergy(SLList < objBaseClass * > *pObjList)
{
    double unshotEnergy = 0.0;
    for(Pix pix = pObjList->first(); pix != NULL; pObjList->next(pix))
    {
	objBaseClass *pObj = (*pObjList)(pix);	
	switch(pObj->who())
	{
	case TRIANGLE_OBJ_E:
	    unshotEnergy += pObj->getNonRadEnergy();	    
	    break;	    
	case SPHERE_OBJ_E:
	case NULL_OBJ_E:
	default:
	    break;
	}
    }
    return(unshotEnergy);
}

//------------------------------------------------------------
// initForRadiosity() 
//	- initialize for patches
//   パッチの初期化を行う．
//   総未放射ラジオシティエネルギを返す
//------------------------------------------------------------
void PEClass::
initializePatches(void)
{
    if(this->mSysInfo.isRadiosityCalc() == NO)
    {
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << genHd(VERBOSEHEAD, this->getMyRank()) << 
	    "radiosity calculation is off. skip radiosity stage." << endl;
	}
	this->mIsMultiPassStageOver = YES;
    }

    // 放射するパッチを検索し、キューに登録する．
    // その際未放射エネルギの総量を計算
    Pix pix;
    for(pix = this->mObjList.first(); pix != NULL; this->mObjList.next(pix))
    {
	objBaseClass *pObj = this->mObjList(pix);
	if(pObj->who() == TRIANGLE_OBJ_E) // 三角形パッチ以外の放射はない
	{
	    if(pObj->getNonRadEnergy() >= this->mViewData.getMinRadEnergy())
	    {
		// エネルギの放射が指定より大きかったら放射キューに入れる
		assert(pObj->isInRadiationQueue() == NO);
		pObj->setInRadiationQueue();
		this->mRadPatchQueue.append(pObj);
	    }
	}
    }

    // make HemiCube for this PE
    this->mpHemiCube = 
	new hemiCubeClass(this->mViewData.getHemres(), this->mID);
    // ray pointer array for communicate HC <-> PE
    // ray の受け渡しの配列の作成    
    this->mppRadiosityRays = new Ray *[this->mpHemiCube->getGenRayNum()];
    
    if(Option::getInstance()->getVerboseMode() == YES)
    {
	cout << genHd(VERBOSEHEAD, this->getMyRank()) << "init radpatches " <<
	" ( " << this->mRadPatchQueue.length() << " )" << ", HCGenRays/once = "
	<< this->mpHemiCube->getGenRayNum() << endl;
    }
}

//------------------------------------------------------------
// 放射パッチの候補の中から最大のエネルギのものを選択し、放射させる
//------------------------------------------------------------
void PEClass::
radiateRadFromMaxRadPatch(void)
{
    double maxEnergy = 0.0;
    objBaseClass *pMaxEnergyPatch = NULL;
    
    // list.length() が重そうなので search ついでに長さも計る
    // DLList には length を記録するようにしていないので必要。
    // これは最大のものを search してそれを取り出すため、queue のよう
    // に先頭を取るのではないため。しかし改良は可能なはず。
    int queueLen = 0;	

    Pix pidx;
    Pix delpidx = NULL;
    for(pidx = this->mRadPatchQueue.first(); 
	pidx != NULL;
	this->mRadPatchQueue.next(pidx))
    {
	if((this->mRadPatchQueue(pidx))->getNonRadEnergy() > maxEnergy)
	{
	    maxEnergy = (this->mRadPatchQueue(pidx))->getNonRadEnergy();
	    pMaxEnergyPatch = this->mRadPatchQueue(pidx);
	    delpidx = pidx;
	}
	queueLen++;
    }
    // この関数はパッチキュー長さ > 0 で呼ぶから必ず放射パッチが存在する
    assert((pMaxEnergyPatch != NULL) && (delpidx != NULL)); 
    this->mRadPatchQueue.del(delpidx, 1); // 1 (meanless here) see libg++ info

    double nonRadEnergyOfThePatch = pMaxEnergyPatch->getNonRadEnergy();

    this->mpHemiCube->genRays(pMaxEnergyPatch, 
			      this->mSysInfo.isDistributedRadiation(),
			      this->mppRadiosityRays);

    int i;
    const int raynum = this->mpHemiCube->getGenRayNum();
    for(i = 0; i < raynum; i++)
    {
	this->mRayPtrQueue.enq(this->mppRadiosityRays[i]);
	// 過去の version では実体を渡したので必要だったが、今は 
	// pointer がコピーされていれば良い 
	// delete this->mpRadiosityRays[i];
	// しかし、ポインタは既にリストにコピーされ、実体を指すポイン
	// タが 2 つになったので片方を切っておく 
	this->mppRadiosityRays[i] = NULL;
    }

    //------------------------------
    // Hemi Cube の生成をしるす
    //------------------------------
    this->mGStat.incHCGenNum();
    if(FileInfo::getInstance()->isRadioOutAtStep() == YES)
    {
	this->outRadiosityIntermediateResult();
    }

    // Profiling Monitor
    double unshotRadEnergy = -1.0;
    if(FileInfo::getInstance()->isTraceOutWithUnshotRadiosity() == YES)
    {
	unshotRadEnergy = 
	    getTotalUnshotRadiosityEnergy(&(this->mObjList));
    }
    ProfileMon::getInstance()->outHemiCubeInfo(queueLen, unshotRadEnergy);

    // verbose, debug mode
    if(Option::getInstance()->getVerboseMode() == YES)
    {
	cout << genHd(VERBOSEHEAD, this->getMyRank()) << "make HC : "
		"radpat nonrad enegy " << nonRadEnergyOfThePatch <<
		", rest nonradpatches " << queueLen << endl; 
	if(Option::getInstance()->getDebugMode() == YES)
	{
	    if(unshotRadEnergy < 0.0)
	    {
		unshotRadEnergy = 
		    getTotalUnshotRadiosityEnergy(&(this->mObjList));
	    }
	    cout << genHd(DEBUGHEAD, this->getMyRank()) << "rest of total "
	    	"energy of this PE " << unshotRadEnergy << endl;
	}
    }
}

void PEClass::
outRadiosityIntermediateResult(void)
{
    if(FileInfo::getInstance()->isInLoop() == YES)
    {
	if(FileInfo::getInstance()->getLoopVarValue() == 
	   this->mGStat.getHCGenNum())
	{
	    char fnamebuf[256];
	    static int output_counter = 0;
	    // output radiosity file 
	    switch(FileInfo::getInstance()->getRadioIntermFileType())
	    {
	    case 0:		// shot radiosity
		sprintf(fnamebuf, "%s.shot.%03d",   
			(const char*)(FileInfo::getInstance()->
				      getRadioIntermediateBaseName()),
			output_counter);
		this->outRadiosityMpi2Data(fnamebuf, YES);
		break;
	    case 1:		// unshot radiosity
		sprintf(fnamebuf, "%s.unshot.%03d", 
			(const char*)(FileInfo::getInstance()->
				      getRadioIntermediateBaseName()),
			output_counter);
		this->outRadiosityMpi2Data(fnamebuf, NO);
		break;
	    case 2:		// both
		sprintf(fnamebuf, "%s.shot.%03d",   
			(const char*)(FileInfo::getInstance()->
				      getRadioIntermediateBaseName()),
			output_counter);
		this->outRadiosityMpi2Data(fnamebuf, YES);
		sprintf(fnamebuf, "%s.unshot.%03d", 
			(const char*)(FileInfo::getInstance()->
				      getRadioIntermediateBaseName()),
			output_counter);
		this->outRadiosityMpi2Data(fnamebuf, NO);
		break;
	    default:
		cout << "Internal Error! : illegal intermediate radio "
		"out type" << endl;
		this->exitprog();
		break;
	    }
	    output_counter++;
	    // next loop
	    FileInfo::getInstance()->nextLoopVar();
	}
    }
}    
