/*
 * $Id: RadioRenderer.java,v 1.19.4.2 2000/06/19 17:46:20 yamauchi Exp $
 *     (M\pi)^2 : Radio Renderer (Radiosity Renderer)
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source PERadiosity.cc
 *
 *	ラジオシティ法の実装
 * 
 * 1999年8月17日(火)
 */

package jp.ac.tohoku.is.archi.mpi2.radiosity;

import  java.io.*;

import	jp.ac.tohoku.is.archi.mpi2.common.*;
import	jp.ac.tohoku.is.archi.mpi2.dda.*;
import	jp.ac.tohoku.is.archi.mpi2.net.*;
import	jp.ac.tohoku.is.archi.mpi2.obj.*;
import	jp.ac.tohoku.is.archi.mpi2.host.*;
import	jp.ac.tohoku.is.archi.mpi2.sysparam.*;
import	jp.ac.tohoku.is.archi.mpi2.util.*;

import  jp.ac.tohoku.is.archi.mpi2.jdk12src.Map;
import  jp.ac.tohoku.is.archi.mpi2.jdk12src.Iterator;
import  jp.ac.tohoku.is.archi.mpi2.jdk12src.HashMap;
import  jp.ac.tohoku.is.archi.mpi2.jdk12src.Vector;


/**
 * sub functions for radiosity stage
 *
 * radiosity の処理 複数本の delta-radiosity ray を処理する
 *    交差判定、次空間計算等を行い、送信する ray を NetworkInterface に送る
 */
public final class RadioRenderer 
implements Mpi2Enum, Mpi2Constants, HostConstants {
    // PE
    private PE mPE;

    // ラジオシティステージの終了判定用
    private boolean mIsRadiosityStageOver;

    // 現在 Ray の生成は停止中か
    private boolean mIsStopGenNewRay;

    // hostへのrayの発生停止要求を提出しているか
    private boolean mSendingRayStopComToHost;

    //  放射パッチキュー : 中途の delete が必要なので VectorQueue 
    private VectorQueue	mRadPatchQueue;

    //  処理を行う ray のキューの参照 この実体は PE.java に置く
    // private Queue mRayQueueRef;

    // ray generator for radiosity delta-radiosity 
    private HemiCube    mHemiCube;
    private Ray	[]      mRadiosityRays;	// radiosity 時に hemicube との通信用

    // reference of member of PEs
    private HashMap 		mRadSpaceMapRef;
    // private Material [] 	mMatrialArrayRef;
    private SearchNextSpcDDA 	mRadDDAUnitRef;    
    private NetIntUnit		mRadNIUnitRef;
    
    public RadioRenderer(PE pe)
    {
	this.mPE 			= pe;
	this.mIsRadiosityStageOver 	= false;
	this.mIsStopGenNewRay		= false;
	this.mSendingRayStopComToHost	= false;
	this.mRadPatchQueue 		= new VectorQueue();
        this.mHemiCube			= null;
	this.mRadiosityRays		= null;	
	this.mRadSpaceMapRef		= this.mPE.peekSpaceMap();
	// this.mMatrialArrayRef		= this.mPE.peekMatArray();
     	this.mRadDDAUnitRef		= this.mPE.peekDDAUnit();
	this.mRadNIUnitRef		= this.mPE.peekNIUnit();
    }
    
    /**
     * 新たなレイの発生は可能か
     */
    private boolean isStopGenNewRay()
    {
	return(this.mIsStopGenNewRay);
    }

    /**
     * Radiosity stage は終了か 
     */
    private boolean isRadiosityStageOver()
    {
	return(this.mIsRadiosityStageOver);
    }

    /**
     * initialize for Radiosity 
     */
    void initForRadiosity()
    {
	// initialize Network Interface unit
	this.mPE.initNetIntRayBuff();

	// このステージの計算を行うか
	if(this.mPE.peekSysInfo().isRadiosityCalc() == false)
	{
	    if(Mpi2Option.getInstance().getVerboseMode())
	    {
		String s = Mpi2Option.toHead(VERBOSEHEAD, 
					     this.mPE.getMyRank()) + 
		    "radiosity calculation is off. skip radiosity stage.";
		System.out.println(s);
	    }
	    this.mIsRadiosityStageOver = true;
	    return;		// このステージの計算はしない
	}

	// このステージの計算を行う
	this.mIsRadiosityStageOver 	= false;
	
	// patch の初期化
	this.initializePatches();	// calcRadioFRG.cc DELETE COMMENT
	
	// HemiCube の初期化
	this.initializeHemiCube();

	// NIN this.mRadNIUnitRef.initPEStatHostCommBuff(this.mHostCommRecvQueue);

	// volume の初期化
	/* volume 関係は NIN 
	   SLList < VolumeClass * > volumeList;
	   this.mVolSet.getAllElementAsList(volumeList);
	   if(!(volumeList.empty()))
	   {
	   initializeVolumes(volumeList);
	   }
	   */
	
	// PE status init
	this.mPE.initBusyIdleStat();

	// hostからのrayの発生停止要求はない
	this.mIsStopGenNewRay = false; 	 

	// hostへのrayの発生停止要求は未提出
	this.mSendingRayStopComToHost = false; 
    }

    /**
     * initForRadiosity() 
     *	- initialize for patches
     *   パッチの初期化を行う．
     *   総未放射ラジオシティエネルギを返す :: NIN
     */
    private void initializePatches()
    {
	/*
	 * 放射するパッチを検索し、キューに登録する．
	 * その際未放射エネルギの総量を計算
	 */
	for(Iterator i = this.mRadSpaceMapRef.values().iterator(); 
	    i.hasNext();) 
	{
	    Vector objlist = (Vector)(i.next());
	    for(int j = 0; j < objlist.size(); j++)
	    {
		ObjectPrimitive obj = (ObjectPrimitive)(objlist.elementAt(j));

		// TriangleObject のみが放射可能
		if(obj instanceof TriangleObject)
		{
		    TriangleObject tobj = (TriangleObject)obj;

		    // 未放射エネルギを material から初期化
		    if(tobj.getNonRadEnergy() == 0.0)
		    {
			final Material mat = 
			    (this.mPE.peekMatArray())[tobj.getMaterialID()];
			tobj.addUnshotRadiosity(mat.peekDiffEmit());
		    }else
		    {
			// 既に初期化されているものがある．skip したかったか?
			String s = "Error! : non radiosity energy already " + 
			    "initialized, do you want skip radiosity stage?";
			System.out.println(s);
		    }

		    if(tobj.getNonRadEnergy() >= 
		       (this.mPE.peekViewData()).getMinRadEnergy())
		    {
			// エネルギの放射が指定より大きかったら放射キュー
			// に入れる : 全てを一度見るだけなので，既にキュー
			// に入っているということはない．
			Assert.out(tobj.isInRadiationQueue() == false, 
				   "Error!: tobj is already in queue.");
			tobj.setInRadiationQueue();
			this.mRadPatchQueue.enq(tobj);
		    }
		}
	    }
	}
    }

    /**
     * initForRadiosity() 
     *	- initialize HemiCube
     *   Hemi_Cube の初期化を行う．
     */
    private void initializeHemiCube()
    {
	// make HemiCube for Radiosity calc of this PE 
	this.mHemiCube = 
	    new HemiCube((this.mPE.peekViewData()).getHemres(), 
			 this.mPE.peekID());
	// ray pointer array for communicate HC <-> PE
	// ray の受け渡しの配列の作成    
	this.mRadiosityRays = new Ray[this.mHemiCube.getGenRayNum()];
    
	if(Mpi2Option.getInstance().getVerboseMode() == true)
	{
	    String s = Mpi2Option.toHead(VERBOSEHEAD, this.mPE.getMyRank()) + 
		"init radpatches ( " + this.mRadPatchQueue.length() + 
		" ), HCGenRays/once = " + this.mHemiCube.getGenRayNum();
	    System.out.println(s);
	}
    }

    /**
     * radiosity の処理 複数本の delta-radiosity ray を処理する
     * 交差判定、次空間計算等を行い、送信する ray を NetworkInterface に送る
     */
    private void processingRadiosities()
    {
	/*
	 * この関数が呼ばれる時には以下の条件を満たしている
	 *     ・キューに一本はレイが存在している			
	 */
	Assert.out(!(this.mPE.peekRayQueue().isEmpty()), 
		   "Error!: there must be ray : processingRadiosities()");
	/*
	 * 以下の状況の時にレイを一本取り出してその処理を行なう。
	 * 以下の条件はこの関数にエントリした時には満たされているので 
	 * do{}while;型のループとなる。 
	 * 	   ・レイがキューに存在する
	 * 	   ・レイを処理した数が getProcRadioAtOnce 以下
	 *	   ・バッファが空いている				 
	 */
	int i = 0;
	do {
	    Assert.out(this.mPE.peekRayQueue().length() > 0, 
		       "Error!: There must be ray : processingRadiosities().");
	    Ray ray = (Ray)(this.mPE.peekRayQueue().deq());
	    this.processingOneRadiosity(ray); 
	    i++;
	}while((!(this.mPE.peekRayQueue().isEmpty())) && 
	       (i < this.mPE.peekSysInfo().getProcRadioAtOnce()));
	// FIXME Statistics.addRadioProcsAtOnce(i);
    }

    /**
     * processing one delta-radiosity ray packet
     * 	これは processingOneRayTrace() とほぼ同じだが Hit した ray の
     * 	処理が異なる  (Diff の行が異なる)
     *      一つのレイを処理し、次空間があるならば NI へと渡す
     *	Ray Processing Block の作業を行う
     */
    private void processingOneRadiosity(Ray ray)
    {
	// ray の存在する空間位置が不正でないかを調べる。不正ならば exit
// 	Assert.out(this.mPE.isDealSpace(ray.peekSpcPos()) == true, 
// 		   "Error!: ray is in invalid subspace. " + 
// 		   "processingOneRadiosity");
 	if(this.mPE.isDealSpace(ray.peekSpcPos()) == false)
	{
	    String s = "DEBUG Error!: rank " + this.mPE.getMyRank() + 
		", PEID " + this.mPE.peekID() + ", ray is in invalid subspace."
		    + " processingOneRadiosity";
	    System.out.println(s + ray);
	    Mpi2CommFactory.getInstance().exit(1);
	}
	
	Vector objArray;
	final int objNumInThisSpc;
	if(this.mRadSpaceMapRef.containsKey(ray.peekSpcPos()) == true)
	{
	    objArray = (Vector)(this.mRadSpaceMapRef.get(ray.peekSpcPos()));
	    objNumInThisSpc = objArray.size();
	}else
	{
	    // この空間には物体が存在しない．
	    objArray = null;
	    objNumInThisSpc = 0;
	}

	double distanceBetweenRayNObject = Double.MAX_VALUE;

	// 交差判定
	Vector3 hitPoint	= new Vector3();	// 交差点
	ObjectPrimitive hitObj 	= null; 		// 交差物体
	for(int i = 0; i < objNumInThisSpc; i++)
	{
	    ObjectPrimitive obj = (ObjectPrimitive)(objArray.get(i));
	    if(obj != ray.peekRadObjRef()) // 放射したその面とは交差判定しない
	    {
		double tmpDist = obj.getDistance(ray, hitPoint);
		Assert.out(tmpDist >= 0.0, "Error!: distance must be >= 0");

		// 球のような物体の場合にはこの部分空間に存在しない部分で 
		// hit する可能性がある．その場合を除く
		switch(obj.who())
		{
		case ObjectPrimitive.OBJ_TRIANGLE:	
		    break;
		case ObjectPrimitive.OBJ_SPHERE:
		    if(this.mPE.checkHitPosIsInThisSpcPos(hitPoint, 
							  ray.peekSpcPos())
		       == false)
		    {
			// この空間ではヒットしていない
			tmpDist = Double.MAX_VALUE; 
		    }
		    break;
		case ObjectPrimitive.OBJ_NULL:		
		default:
		    throw new RuntimeException("Internal Error! : this obj " + 
					       "cannot intersect calc" + obj);
		}
		if(distanceBetweenRayNObject > tmpDist)
		{
		    // tmpDist の方が近いので交差したobjを更新し、
		    // 交点をセットする
		    distanceBetweenRayNObject = tmpDist;
		    hitObj = obj;
		    ray.setHitPos(new Vector3(hitPoint)); // newする必要はない?
		}
	    }
	}
    
	// パッチ輝度計算、二次レイ発生、次空間探索、送信
	if(hitObj != null) // 交差面がある
	{
	    // 物体に ray が交差した
	    final Material mat = 
		(this.mPE.peekMatArray())[hitObj.getMaterialID()];
	    if(hitObj.who() == ObjectPrimitive.OBJ_TRIANGLE)
	    {
		/*
		 * triangle のみが radiosity を受ける
		 *	Diffパッチの輝度計算
		 *	輝度を加算し，ray 中の deltaRadiosity をこの 
		 * 	patch の面積で割り，単位面積あたりの delta
		 * 	radiosity とする 
		 */
		this.calcNsetIntensityRadio(ray, mat, hitObj);
	    }

	    /*
	     * 更に反射屈折を行うか
	     * ここでは ray tracing 法のように輝度を Frame Buffer へ送
	     * 信する必要がないので ray が発生したかどうかは不問に付す 
	     *
	     * 発生した ray は PE のキューに入れることとする
	     */
	    CalcRadioSRG.newSecondaryRadiosityRays(ray, hitObj, this.mPE);
	    // delete ray;   やってきたレイは消滅 deleteRay!
	}else
	{
	    // 交差しなかったので次空間へ送信を試みる 
	    boolean isSendRay = this.mPE.moveRayToNextSubspace(ray);
	    if(isSendRay == true)	// ray 送信
	    {
		Assert.out(ray != null, "Error! : Ray is null");
		this.mPE.setRayToNI(ray); // NIのバッファへためこむ  送信
	    }else
	    {
		// レイ消滅 deleteRay!
		// delete ray;
	    }
	}	
    }

    /**
     * do radiosity
     */
    public void doRadiosity()
    {
	ProfileMon pfm = ProfileMon.getInstance();
	pfm.startStage(ProfileMon.ProfSt_radiosity);
    
	// 初期化
	this.initForRadiosity();

	// 最初に未放射だったエネルギの総計を求めておく
	double totalInitNonRadEnergy = 
	    RadioRenderer.getTotalUnshotRadiosityEnergy(this.mRadSpaceMapRef);
	
	// System.out.println("DEBUG : total rad = " + totalInitNonRadEnergy);
    
	while(this.isRadiosityStageOver() == false)
	{
	    // まずは他から受信する
	    this.mPE.recvRays();		

	    /*
	     * 送信する : 概念的にはここで送信であるが，実は Network
	     * Interface に enqueue された時点でキュー長の比較が行なわ
	     * れ，送信を発行すべきならば送信はその時に発行されている．
	     * this.sendRays();	
	     */
	
	    // processing
	    if(this.mPE.peekRayQueue().hasElements())
	    { 
		// DELETEME System.out.println("DEBUG : proc ray");	
		// 処理するべき ray 有り
		Assert.out(this.mPE.isIdle() == false, 
			   "Error!: here must be busy.");

		pfm.occurEv(ProfileMon.ProfEv_ProcRadRays);

		// // this.toBusy();
		this.processingRadiosities(); // buffer に空きがある分処理する
		this.mPE.globalLoadBalance(); // rayの総量でrayの発生制限を行う
	    }
	    else if(this.mRadPatchQueue.hasElements())
	    {
		/*
		 * キューにレイなし && 放射パッチあり 
		 *		-> 最大放射パッチから放射する
		 * 放射パッチが存在するので busy にする．
		 * 	この toBusy は不要かもしれないが放射の処理は比
		 *	較的少ないので overhead にはならないだろう   
		 */
		this.mPE.toBusy(); 
	    
		/*
		 * レイを発生する前に host から ray の発生の停止要求がある
		 * かを調べる 
		 */
		this.getAndPerformHostCommand();
		if(this.isStopGenNewRay() == false) // host から停止要請はない
		{
		    pfm.occurEv(ProfileMon.ProfEv_genHemiCube);
		    this.radiateRadFromMaxRadPatch(); // ray 発生
		}
	    }
	    else if(this.mRadNIUnitRef.isSendRayBufAllEmpty() == false)
	    {
		// System.out.println("DEBUG : flush");	
		// レイ無し && 放射無し && バッファにレイが残っている
		Assert.out(this.mPE.isIdle() == false, 
			   "Error!: here must be busy.");

		// 送信可能な場合には送信発行
		this.mRadNIUnitRef.flushSendRaysBuffer();
		pfm.occurEv(ProfileMon.ProfEv_SendingWait);
		
	    }else if((this.mRadNIUnitRef.isNoSendRay() == true) &&
		     (this.mRadNIUnitRef.isNoRecvRay() == true))  
	    {
		// 送信中ではない && 受信中ではない
		// DELETEME System.out.println("DEBUG : may idle");	

		// 輝度と PEStat の flush
		this.mRadNIUnitRef.flushPEStatSendBuff();   // PEStat の flush
		if(this.mRadNIUnitRef.isSendingPEStat() == false)
		{
		    /*
		     * 受信レイなし && 処理するべき自己 ray 無し && 
		     * 送信終了     && 受信終了 (ray && PEStat)
		     * 		== 全ての仕事が終了
		     */
		    if(this.mPE.isIdle() == false)
		    {
			this.mPE.toIdle();
			pfm.occurEv(ProfileMon.ProfEv_Idle);		    
		    }else
		    {
			// idle 状態のまま
			// host のコマンドがあれば受ける
			this.getAndPerformHostCommand();
			if(this.isRadiosityStageOver() == true)
			{
			    // 終了コマンドを受けた場合にはキューは空
			    Assert.out(this.mPE.peekRayQueue().isEmpty() &&
				       this.mRadPatchQueue.isEmpty(),
				       "Error!: queues must be empty");
			}
		    }
		}
		Mpi2System.pollingSleep();
	    }else
	    {
		this.mPE.toBusy();
	    }
	}

	// 閾値以下の未放射エネルギの処理
	this.finalizeForRadiosity(totalInitNonRadEnergy);

	pfm.endStage(ProfileMon.ProfSt_radiosity);
    }

    /**
     * finalize for radisity
     */
    private void finalizeForRadiosity(double totalInitNonRadEnergy)
    {
	// 放射したエネルギと未放射エネルギの出力
	double unshotRadEnergy = 
	    RadioRenderer.getTotalUnshotRadiosityEnergy(this.mRadSpaceMapRef);

	String s = Mpi2Option.toHead(NORMALHEAD, this.mPE.peekID()) + 
	    "unshot radenergy (start end) = " + totalInitNonRadEnergy
	    + " " + unshotRadEnergy;
	System.out.println(s);

	// 未放射ラジオシティ値を flush しラジオシティ値に加える
	for(Iterator i = this.mRadSpaceMapRef.values().iterator(); 
	    i.hasNext(); ) 
	{
	    Vector objlist = (Vector)(i.next());
	    for(int j = 0; j < objlist.size(); j++)
	    {
		ObjectPrimitive obj = (ObjectPrimitive)(objlist.elementAt(j));

		switch(obj.who())
		{
		case ObjectPrimitive.OBJ_TRIANGLE:
		    ((TriangleObject)obj).flushUnshotRadiosityNInitVerInt();
		    break;	    
		case ObjectPrimitive.OBJ_SPHERE:
		case ObjectPrimitive.OBJ_NULL:
		default:
		    break;
		}
	    }
	}
	String head = Mpi2Option.toHead(VERBOSEHEAD, this.mPE.getMyRank());
	if(this.mPE.peekSysInfo().isRadiosityCalc() == false)
	{
	    // radiosity calculation is not executed
	    if(Mpi2Option.getInstance().getVerboseMode() == true)
	    {
		String verbosemes = head +
		    "flushed each patch emittion (skipping radiosity stage).";
		System.out.println(verbosemes);
	    }
	}else
	{
	    // delete this.mHemiCube;	    
	    this.mHemiCube = null;
	    if(Mpi2Option.getInstance().getVerboseMode() == true)
	    {
		String verbosemes = head + 
		    " destroing hemicube. finalize radiosity stage.";
		System.out.println(verbosemes);
	    }
	}

	// 終了
	FileInfo fi = FileInfo.getInstance();
	if(fi.isRadioStageResultOut() == true)
	{
	    this.outRadiosityMpi2Data(fi.getRadioStageOutFileName(), true);
	}
    }

    /**
     * Host からのコマンドを受信し解釈する．
     */
    private void getAndPerformHostCommand()
    {
	if(this.mPE.probeHostCommand() == false)
	{
	    return;		// コマンドの受信はなかった
	}
	
	// Host のコマンドを受けた
	Assert.out(this.mPE.peekHostCommRecvQueue().length() > 0, 
		   "Error!: queue len must be > 0 " + 
		   "RadioRenderer.getAndPerformHostCommand");
	HostCommand hcomm = 
	    (HostCommand)this.mPE.peekHostCommRecvQueue().deq();

	switch(hcomm.getCommand())
	{
	case End_Radiosity: 	
	    // radiosity stage 終了
	    this.mIsRadiosityStageOver = true;
	    break;

	case Suspend_GenNewRays: 
	    // 大域負荷分散: stop generate new rays
	    this.mIsStopGenNewRay = true;
	    break;
	    
	case Resume_GenNewRays:
	    // 大域負荷分散: resume generate new rays
	    this.mIsStopGenNewRay = false;
	    break;
	    
	case Send_Patch_Descriptions:
	    // ホストからのパッチ情報送信要求
	    {
		// 計算の途中でパッチ情報を host に送信する
		// this.sendOutRadioResultToHost();
		Mpi2Option.getInstance().
		    verboseOut("NIN sendOutRadioResultToHost at " + 
			       "RadioRenderer.getAndPerformHostCommand");
	    }
	    break;

	    // 以下は radiosity ステージでは送られてこないはず
	case End_PrepareShading: 	// prepare shading stage 終了
	case End_Raytrace: 		// ray-tracing stage 終了
	case Issue_BarrierSync: 	// synchronization
	default:
	    String s = "Internal Error! : PE. r " + this.mPE.getMyRank() + 
		"PE.getAndPerformHostCommand received illegal host command";
	    throw new RuntimeException(s);
	}
	    
	if(Mpi2Option.getInstance().getDebugMode())
	{
	    System.out.println(Mpi2Option.toHead(DEBUGHEAD, 
						 this.mPE.getMyRank()) 
			       + "get " + hcomm);
	}
    }
	

    /*------------------------------------------------------------
     * CalcRadioFRG
     *	calculation of First Rays Generation Unit for radiosity 
     *-----------------------------------------------------------*/
    /**
     * get unshot radiosity energy from an object list
     *	渡された object list の Map に入っている patch の未放射エネル
     *  ギを返す 
     */
    private static double getTotalUnshotRadiosityEnergy(HashMap objlistmap)
    {
	double unshotEnergy = 0.0;
	for(Iterator i = objlistmap.values().iterator(); i.hasNext();)
	{
	    Vector objlist = (Vector)(i.next());
	    for(int j = 0; j < objlist.size(); j++)
	    {
		ObjectPrimitive obj = (ObjectPrimitive)(objlist.elementAt(j));

		// TriangleObject のみが放射可能
		if(obj instanceof TriangleObject)
		{
		    unshotEnergy += ((TriangleObject)obj).getNonRadEnergy();
		}
	    }
	}
	return(unshotEnergy);
    }

    /**
     * 放射パッチの候補の中から最大のエネルギのものを選択し、放射させる
     */
    private void radiateRadFromMaxRadPatch()
    {
	double maxEnergy 	       	= 0.0;
	TriangleObject maxEnergyPatch	= null;
	int deleteIdx 		       	= -1;
	
	VectorQueue patchqueue = (VectorQueue)this.mRadPatchQueue;
	
	for(int i = 0; i < patchqueue.length(); i++)
	{
	    TriangleObject tripatch = (TriangleObject)(patchqueue.peek(i));
	    if(tripatch.getNonRadEnergy() > maxEnergy)
	    {
		maxEnergy = tripatch.getNonRadEnergy();
		maxEnergyPatch = tripatch;
		deleteIdx = i;
	    }
	}
	
	// この関数はパッチキュー長さ > 0 で呼ぶから必ず放射パッチが存在する
	Assert.out((maxEnergyPatch != null), 
		   "Error!: max energy patch must be there."); 
	
	this.mRadPatchQueue.del(deleteIdx);
	
	double nonRadEnergyOfThePatch = maxEnergyPatch.getNonRadEnergy();
	
	this.mHemiCube.genRays(maxEnergyPatch, 
			       this.mPE.peekSysInfo().isDistributedRadiation(),
			       this.mRadiosityRays);
	
	int raynum = this.mHemiCube.getGenRayNum();
	for(int i = 0; i < raynum; i++)
	{
	    (this.mPE.peekRayQueue()).enq(this.mRadiosityRays[i]);
	    /*
	     * 過去の version では実体を渡したので必要だったが、今は 
	     * pointer がコピーされていれば良い 
	     * delete this.mpRadiosityRays[i];
	     * しかし、ポインタは既にリストにコピーされ、実体を指すポイン
	     * タが 2 つになったので片方を切っておく 
	     */
	    this.mRadiosityRays[i] = null;
	}

	/*------------------------------
	 * Hemi Cube の生成をしるす
	 *------------------------------*/
	(this.mPE.peekStatistics()).incHCGenNum();
	if(FileInfo.getInstance().isRadioOutAtStep() == true)
	{
	    this.outRadiosityIntermediateResult();
	}

	// Profiling Monitor
	double unshotRadEnergy = -1.0;
	if(FileInfo.getInstance().isTraceOutWithUnshotRadiosity())
	{
	    unshotRadEnergy = 
		RadioRenderer.
		getTotalUnshotRadiosityEnergy(this.mRadSpaceMapRef);
	}
	ProfileMon.getInstance().outHemiCubeInfo(patchqueue.length(), 
						 unshotRadEnergy);

	if(Mpi2Option.getInstance().getVerboseMode())
	{
	    String s = Mpi2Option.toHead(VERBOSEHEAD, this.mPE.getMyRank()) + 
		"make HC : radpat nonrad enegy " + nonRadEnergyOfThePatch +
		", rest nonradpatches " + this.mRadPatchQueue.length();
	    System.out.println(s);
	    if(Mpi2Option.getInstance().getDebugMode())
	    {
		if(unshotRadEnergy < 0.0)
		{
		    unshotRadEnergy = 
			RadioRenderer.
			getTotalUnshotRadiosityEnergy(this.mRadSpaceMapRef);
		}
		String ds = Mpi2Option.toHead(DEBUGHEAD, this.mPE.getMyRank())
		    + "rest of total energy of this PE " + unshotRadEnergy;
		System.out.println(ds);
	    }
	}
    }

    /** 
     * ラジオシティの中間結果の出力
     */
    private void outRadiosityIntermediateResult()
    {
	FileInfo fi = FileInfo.getInstance();

	if(fi.isInLoop() == true)
	{
	    int output_counter = 0;
	    if(fi.getLoopVarValue() == 
	       (this.mPE.peekStatistics()).getHCGenNum())
	    {
		String outfbase = "";
		// output radiosity file 
		switch(fi.getRadioIntermFileType())
		{
		case 0:		// shot radiosity
		    outfbase = fi.getRadioIntermediateBaseName() + ".shot." + 
			PrintUsing.getDigitString(3, output_counter);
		    this.outRadiosityMpi2Data(outfbase, true);
		    break;
		case 1:		// unshot radiosity
		    outfbase = fi.getRadioIntermediateBaseName() + ".unshot."+ 
			PrintUsing.getDigitString(3, output_counter);
		    this.outRadiosityMpi2Data(outfbase, false);
		    break;
		case 2:		// both
		    outfbase = fi.getRadioIntermediateBaseName() + ".shot." + 
			PrintUsing.getDigitString(3, output_counter);
		    this.outRadiosityMpi2Data(outfbase, true);
		    outfbase = fi.getRadioIntermediateBaseName() + ".unshot."+ 
			PrintUsing.getDigitString(3, output_counter);
		    this.outRadiosityMpi2Data(outfbase, false);
		    break;
		default:
		    throw new RuntimeException("Internal Error! : illegal "+
					       "intermediate radio out type");
		}
		output_counter++;
		// next loop
		fi.nextLoopVar();
	    }
	}
    }    

    /**
     * output radiosity intermediate result
     * 本来は一度 host に計算情報を集める必要があるだろうが，現在は
     * それぞれの PE が rank を filename に入れてそれぞれ出力する．
     * これは改善する必要があるだろう．FIXHERE
     * @param isShotRad == true  の時には計算済みの radiosity を出力する
     *		       	   false の時には未放射 radiosity を出力する
     */
    public void outRadiosityMpi2Data(String outBaseFname, boolean isShotRad)
    {
	// basename + "r"ranknum + "mpi2.geo"
	String outfilename = outBaseFname + "r" + 
	    PrintUsing.getDigitString(3, this.mPE.getMyRank()) + 
	    ".mpi2.geo";
	PrintWriter printer = null;
        try {
	    printer = new PrintWriter(new FileOutputStream(outfilename));
        }
        catch(IOException e)
        {
            throw new RuntimeException("Can not open printer stream at "+
				       "RadioRenderer.outRadiosityMpi2Data."
				       + e);
        }

	// write Hemi-cube num 
	printer.println("# create HC num is " + 
			this.mPE.peekStatistics().getHCGenNum());
	this.writeGeometryFile(printer, isShotRad);
	printer.close();
	    
	// gzip output file
	Mpi2Option.getInstance().
	    verboseOut("NIN : Can not exec gzip now");
	// system(combuf);
	// System.out.println(NORMALHEAD + 
	// " radiosity intermediate result out. [" + buf + "]");
    }	    
    
    /**
     * write geometry file
     * 	@param isShotRad : true  の場合には shot のみを出力する
     *		           false の場合には unshot のみを出力する
     */
    private void writeGeometryFile(PrintWriter p, boolean isShotRad)
    {
	int outputType = ((isShotRad == true) ? 
			  WITH_SHOT_RADOUT : WITH_UNSHOT_RADOUT);
	Material nowmat = null;
	for(Iterator i = this.mRadSpaceMapRef.values().iterator(); 
	    i.hasNext();) 
	{
	    Vector objlist = (Vector)(i.next());
	    for(int j = 0; j < objlist.size(); j++)
	    {
		ObjectPrimitive obj = (ObjectPrimitive)(objlist.elementAt(j));
		if(!(obj instanceof NullObject))
		{
		    if(nowmat != 
		       (this.mPE.peekMatArray())[obj.getMaterialID()])
		    {
			// change material
			nowmat =(this.mPE.peekMatArray())[obj.getMaterialID()];
			p.println("m " + nowmat.peekName());
		    }
		    p.println(obj.getMgfForm(outputType));
		}
	    }
	}
    }

    /*------------------------------------------------------------
     * CalcRadioIC
     *	calculation of Intensity Calcuration unit for radiosity 
     *-----------------------------------------------------------*/
    /**
     * radiosity intensity の計算
     *
     * PE.mRadPatchQueue へのアクセスがあるのでこのファイルに統合した．
     * あるいは，RadioRenderer にメソッドを設けてそれを ICUnit からア
     * クセスするということも考えられるだろうが，輝度計算はこれだけな
     * のでよかろう．
     */
    private void calcNsetIntensityRadio(final Ray ray, 
					final Material mat,
					ObjectPrimitive hitobj)
    {
	Assert.out(hitobj.who() == ObjectPrimitive.OBJ_TRIANGLE, 
		   "Error!: now, only TriangleObject can increase its"
		   + "radiosity.");

	TriangleObject hitPatch = (TriangleObject)hitobj;

	double  dAj   = hitPatch.getArea(); // 受けたパッチの面積
	Vector3 Rho_j = new Vector3(mat.peekDiffRef()); // 反射率
	// dBi*Fie*Ai(ref.Ray.h)
	Vector3 dBiFieAi  = new Vector3(ray.peekColor()); 
    
	// ラジオシティ増加量 dRad = 
	// 反射率 * ラジオシティ * フォームファクタ * 放射パッチ面積 / 受け面積
	// Vector3 dRad = (Rho_j * dBiFieAi) / dAj; 
	Rho_j.mulVecHere(dBiFieAi);	// Rho_j = Rho_j * dBiFieAi
	Rho_j.divScalerHere(dAj);
	Vector3 dRad = new Vector3(Rho_j); 

	hitPatch.addUnshotRadiosity(dRad); // ラジオシティ値の増加

	// HemiCube.initialize にて既にパッチから放射分は引かれている
	// 放射可 && FRGのキューにない場合に lock してキューに入れる
	if((hitPatch.getNonRadEnergy() > 
	    this.mPE.peekViewData().getMinRadEnergy()) && 
	   (hitPatch.isInRadiationQueue() == false)) 
	{
	    hitPatch.setInRadiationQueue(); 	// lock する
	    this.mRadPatchQueue.enq(hitPatch);	// 放射パッチキューに入る

	    if(Mpi2Option.getInstance().getVerboseMode())
	    {
		String s = Mpi2Option.toHead(VERBOSEHEAD, 
					     this.mPE.getMyRank()) + 
		    "enqueue radiation patch : patch non rad enetgy = " + 
		    hitPatch.getNonRadEnergy();
		System.out.println(s);
	    }
	}
    }
}
