/*
 * $Id: ShadeCalculator.java,v 1.7 2000/06/15 13:55:51 yamauchi Exp $
 *     (M\pi)^2 : PE (Processing Element)
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source prepareShading.cc
 *
 *	Gouroud Shading の実装
 * 
 * 1999年9月 7日(火)
 */

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

import  jp.ac.tohoku.is.archi.mpi2.common.*;
import  jp.ac.tohoku.is.archi.mpi2.dda.*;
import  jp.ac.tohoku.is.archi.mpi2.host.*;
import  jp.ac.tohoku.is.archi.mpi2.net.*;
import  jp.ac.tohoku.is.archi.mpi2.obj.*;
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;


/**
 * Prepare Shading の実装
 */
public final class ShadeCalculator 
implements Mpi2Enum, Mpi2Constants, HostConstants, Config
{ 		
    /*
     * PE への参照
     */
    private PE mPE;

    /*
     * この stage は終了かどうか
     */
    private boolean mIsPrepareShadingOver;

    /*
     * 受信頂点輝度のキュー
     */
    private Queue mVerIntQueue; 

    /**
     * constructor
     */
    public ShadeCalculator(PE pe)
    {
	Assert.out(pe != null, "init with null PE at ShadeCalculator");
	this.mPE = pe;
	this.mIsPrepareShadingOver = false;
	// PROF this.mVerIntQueue = new VectorQueue();
	this.mVerIntQueue = new RingBufferQueue();
    }

    private boolean isPrepareShadingStageOver()
    {
	return(this.mIsPrepareShadingOver);
    }

    /**
     * Shading の準備
     *	shading のための計算と情報の交換
     */
    public void doPrepareShading()
    {
	switch(this.mPE.peekSysInfo().getShadingScheme())
	{
	case FlatShading:
	    // flat shading ではすることはない
	    break;
	case GouroudShading:
	    this.initPrepareShadingStage(); // stage の処理 start
	    this.prepareGouroudShading();
	    this.finalizePrepareShadingStage();
	    break;
	default:
	    throw new RuntimeException("Error!: I don't know such shading " +
				       "scheme. " + this.mPE.peekSysInfo().
				       getShadingScheme());
	}
    }

    /**
     * 通信バッファなどの初期化
     */
    private void initPrepareShadingStage()
    {
	// このステージはこれから初める(終了していない)
	this.mIsPrepareShadingOver = false;
	this.mPE.initBusyIdleStat();

	// 輝度頂点送受信バッファを含む初期化
	NetIntUnit niUnit = this.mPE.peekNIUnit();
	int verIntSendThresh = this.mPE.peekSysInfo().getSendVerIntAtOnce();
	niUnit.initVerIntBuff(verIntSendThresh, this.mVerIntQueue);
    }
    
    /**
     * GouroudShading のための情報送信
     */
    private void prepareGouroudShading()
    {
	/*
	 * NIN
	 * 	pm_change_area(PA_prepshade_e);
	 * 	pm_change_stat(PC_prec_intensity_e);
	 */

	// Cell 長さ、頂点の位置の許容誤差を求める
	final double cellLen   = this.mPE.peekSpaceParam().getCellLen();
	final double tolerance = cellLen / 64.0; // 判定基準

	// PE の持つ物体情報を空間ごとにスキャンする
	final Vector3 defSpcMinPos = 
	    this.mPE.peekSpaceParam().peekDefSpaceMin();
	if(!(defSpcMinPos.equals(Vector3.ZERO)))
	{
	    System.out.println("Warning! : 今の実装ではdefine spaceの最小値が "
			       + "(0.0,0.0,0.0)でないとまずいかもしれない。");
	}

	IntVec3 spcpos = new IntVec3();
	final int subdivnum = this.mPE.peekSpaceParam().getSubDivNum();
	for(int spz = 0; spz < subdivnum; spz++)
	{
	    for(int spy = 0; spy < subdivnum; spy++)
	    {
		for(int spx = 0; spx < subdivnum; spx++)
		{
		    // 空間番号
		    spcpos.set(spx, spy, spz);
		    this.averageSendIntensity(spcpos, defSpcMinPos,
					      cellLen, tolerance);
		}
	    }
	}

	if(REDUCE_VERBOSE_INFO)
	{
	    if(Mpi2Option.getInstance().getVerboseMode())
	    {
		System.out.println(Mpi2Option.toHead(VERBOSEHEAD, 
						     this.mPE.getMyRank()) + 
				   "send Intensity-Vertex.. done.");
	    }
	}

	/*
	 * この PE の担当する物体の頂点輝度はとりあえずキューには入れた．
	 * あとは送信の完了と受信の完了，そしてステージの終了待ち 
	 */
	NetIntUnit niUnit = this.mPE.peekNIUnit();
	while(this.isPrepareShadingStageOver() == false)
	{
	    // 送信バッファの flush
	    niUnit.flushVerIntSendBuff();
		
	    // 受信
	    niUnit.recvVerInt();
	    
	    if((niUnit.isSendingVerInt() == true) ||
	       (niUnit.isRecvingVerInt() == true))
	    {
		// 送信中あるいは受信中
		this.mPE.toBusy(); // host へ動作中伝達	    
		niUnit.recvVerInt();
	    }
	    else
	    {
		// 送信・受信ともになくなった
		this.mPE.toIdle(); // host へ終了伝達
	    }
	    this.getAndPerformHostCommand(); // host からの連絡待ち
	    Mpi2System.pollingSleep();
	}
	
	if(REDUCE_VERBOSE_INFO)
	{
	    if(Mpi2Option.getInstance().getVerboseMode())
	    {
		System.out.println(Mpi2Option.toHead(VERBOSEHEAD, 
						     this.mPE.getMyRank()) + 
				   "get all Intensity-Vertex.. done.");
	    }
	}
    }

    /**
     * 各空間ごとに同一頂点の輝度を伝播
     * 	それぞれの空間内のみ
     *------------------------------
     * ひとつの空間の分
     *	IMPROVEME 相互作用の形に書ければいいのだが、現在は送り手受け手
     */
    private void aveVerIntOneSpcLocal(Vector objVec)
    {
	int len = objVec.size();
	VertexIntensity [] senderIvc = new VertexIntensity[3];
	for(int i = 0; i < senderIvc.length; i++)
	{
	    senderIvc[i] = new VertexIntensity();
	}

	for(int i = 0; i < len; i++)
	{
	    // 輝度送り元
	    ObjectPrimitive senderObj = (ObjectPrimitive)(objVec.elementAt(i));
	    // 三角形のみ処理
	    if(senderObj.who() == ObjectPrimitive.OBJ_TRIANGLE)
	    {
		TriangleObject tri = (TriangleObject)senderObj;
		tri.getRadVer(senderIvc);
		for(int j = 0; j < len; j++)
		{
		    if(i != j)	// 自分自身でない者へ送信
		    {
			// 輝度受け側
			ObjectPrimitive recvObj =
			    (ObjectPrimitive)(objVec.elementAt(j));
			if(recvObj.who() == ObjectPrimitive.OBJ_TRIANGLE)
			{
			    TriangleObject recvTri = (TriangleObject)recvObj;
			    // sv   ...sender vertex idx
			    for(int sv = 0; sv < 3; sv++)
			    {
				// addIntensityToVertex は porigonID と頂
				// 点の位置が同一なら輝度を加える  
				recvTri.addIntensityToVertex(senderIvc[sv]);
			    }
			}
		    }
		}
	    }
	}
    }


    /** 
     * この空間の輝度を平均化して他空間に送信する
     */
    private void averageSendIntensity(final IntVec3 spcpos, 
				      final Vector3 defSpcMinPos,
				      final double  cellLen,
				      final double  tolerance)
    {
	IntVec3 peid = this.mPE.peekNIUnit().getNewPEIDFromSpcPos(spcpos);
	if(this.mPE.peekID().equals(peid))
	{
	    // 物体数が 0 ならこの空間で送信処理を行う必要はない
	    Map objmap = this.mPE.peekSpaceMap();
	    if(objmap.containsKey(spcpos) == false)
	    {
		// この空間には物体が存在しない．
		return;
	    }

	    Vector objArray = (Vector)(objmap.get(spcpos));
	    int objNumInThisSpc = objArray.size();
			
	    Assert.out(objArray.size() > 0, 
		       "Error!: There must be object(s):averageSendIntensity");
	    /*
	     * 1.この空間内の輝度値を平均する 
	     * 空間内に 1 つしか物体がなければ処理する必要はない 
	     */
	    if(objArray.size() > 1)
	    {
		this.aveVerIntOneSpcLocal(objArray);
	    }
	    
	    /*
	     * 2.この空間内の輝度を他の空間へと伝搬させる
	     *   この空間の min, max を求める
	     */
	    /*
	     * Vector3 spmin = defSpcMinPos + cellLen * iv2v(spcpos); 
	     */
	    Vector3 spmin = new Vector3(spcpos);
	    spmin.mulScalerHere(cellLen);
	    spmin.plusHere(defSpcMinPos);
	    /*
	     * Vector3 spmax = spmin + cellLen * Vector3(1.0, 1.0, 1.0);
	     */
	    Vector3 spmax = new Vector3(1.0, 1.0, 1.0);
	    spmax.mulScalerHere(cellLen);
	    spmax.plusHere(spmin);

	    //   空間内の物体を取り出し送信する
	    this.sendVerIntOfThisSpc(objArray, spcpos, 
				     spmin, spmax, tolerance);
	}
    }

	
    /**
     * 一つの空間内の輝度情報を他へ送信する。ある1つの空間の物体情報は 
     * objArray が指している。
     */
    private void sendVerIntOfThisSpc(Vector objVec, 	IntVec3 srcspc, 
				     Vector3 spmin,     Vector3 spmax,
				     double tolerance)
    {
	int objnum = objVec.size();
	for(int i = 0; i < objVec.size(); i++)
	{
	    ObjectPrimitive obj = (ObjectPrimitive)(objVec.elementAt(i));

	    /*
	     * 送信候補になれるのは radiosity で用いることが可能な 
	     * primitive (現在は、三角パッチのみ)
	     */
	    if(obj.who() == ObjectPrimitive.OBJ_TRIANGLE)
	    {
		TriangleObject tri = (TriangleObject)obj;

		// 輝度値を取り出す．
		VertexIntensity [] retVerInts3 = new VertexIntensity[3];
		retVerInts3[0] = new VertexIntensity();
		retVerInts3[1] = new VertexIntensity();
		retVerInts3[2] = new VertexIntensity();
		tri.getRadVer(retVerInts3);
		for(int ver = 0; ver < 3; ver++) // 三角形の頂点は3つ
		{
		    /*
		     * この物体の一つの輝度値が送信すべきものかどうか
		     * 判定し、送信する。nonblocking 送信を行う。
		     */
		    this.testOneIntNsendVerInt(retVerInts3[ver], srcspc, 
					       spmin, spmax, tolerance);
		}
	    }
	}
    }

    /**
     * この輝度情報が送信すべきものかどうか判定し、送信する
     *   この関数は最高に addhoc だ．adhoc 関数と命名しても良い位だ．
     *   しかし，今日は気分的に表を作るのがめんどうな日なのだ．
     *   1999年9月 6日(月) 山内 斉
     */
    private void testOneIntNsendVerInt(VertexIntensity v, IntVec3 srcspc, 
				       Vector3 spmin,     Vector3 spmax,
				       double   tolerance)
    {
	// verint は変更の可能性あり
	VertexIntensity verint = new VertexIntensity(v);

	Assert.out(tolerance > 0.0, "Error!: tolerance must be > 0.0");
	
	for(int i = 0; i < 3; i++) // vector i 番目 (x, y, z)
	{
	    /*
	     * 頂点は空間内部 
	     */
	    if(InDEBUG)
	    {
		if((spmax.get(i) - (verint.peekVertexPos()).get(i)) <
		   (-tolerance))
		{
		    String s = 
			"Error! : vertex is out of range:Large"	+ "\n" + 
			"min	" + spmin 		     	+ "\n" + 
			"ver	" + (verint.peekVertexPos())	+ "\n" + 
			"max	" + spmax		     	+ "\n" + 
			"out axis is [" + i + "], val = " 	+ 
			(spmax.get(i)-(verint.peekVertexPos()).get(i))+ "\n" + 
			"tolerance " + (-tolerance);
		    throw new RuntimeException(s);
		}
		if(((verint.peekVertexPos()).get(i) - spmin.get(i)) <
		   (-tolerance))
		{
		    String s = 
			"Error! : vertex is out of range:Small"	+ "\n" + 
			"min	" + spmin 		     	+ "\n" + 
			"ver	" + verint.peekVertexPos()	+ "\n" + 
			"max	" + spmax		     	+ "\n" + 
			"out axis is [" + i + "], val = " 	+ 
			((verint.peekVertexPos()).get(i)-spmin.get(i))+ "\n" +
			"tolerance " + (-tolerance);
		    throw new RuntimeException(s);
		}
	    }
	    // まず送信は各 X,Y,Z の +- 方向へ
	    if((spmax.get(i) - (verint.peekVertexPos()).get(i)) < tolerance)
	    {
		// plus 方向に送信候補として追加(定義空間内にあるなら)
		verint.setDestSpcPos(srcspc.plusNew(AdjDir.get(i * 2)));
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if((verint.peekVertexPos().get(i) - spmin.get(i)) < 
		     tolerance)
	    {
		// minus 方向に送信候補として追加(定義空間内にあるなら)
		verint.setDestSpcPos(srcspc.plusNew(AdjDir.get(i * 2 + 1)));
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else
	    {
		// 送信の候補にはならない
	    }
	}
	// 次に複合方向へ (X+Y+, X+Y-, X-Y+, X-Y- : XZ も同様)
	Vector3 vertexpos = verint.peekVertexPos();
	double xp = spmax.get(Vector3.X) - vertexpos.get(Vector3.X); 
	double xm = vertexpos.get(Vector3.X) - spmax.get(Vector3.X); 
	double yp = spmax.get(Vector3.Y) - vertexpos.get(Vector3.Y);
	double ym = vertexpos.get(Vector3.Y) - spmax.get(Vector3.Y);
	double zp = spmax.get(Vector3.Z) - vertexpos.get(Vector3.Z);
	double zm = vertexpos.get(Vector3.Z) - spmax.get(Vector3.Z);
	// XY, XZ
	if(xp < tolerance) 
	{
	    //----- XPlus
	    if(yp < tolerance)
	    {	
		// Y+

		// srcspc + AdjDir.get(DIR_XPLUS) + AdjDir.get(DIR_YPLUS);
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XPLUS));
		dir.plusHere(AdjDir.get(DIR_3D.YPLUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    /*
		     * まず X+ へ送信しその後 Y+ 方向へ送信する(現在は
		     * 使わない) 
		     * 将来的に Mesh の計算機上ではこのように明示的に2回送
		     * 信する方が良いだろうが現在は MPI の rank に頼って送
		     * 信する (IMPROVEME)
		     */
		    // verint.mRestDir = DIR_3D.YPLUS; // X+ . Y+
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if(ym < tolerance)
	    {	
		// Y-

		// srcspc + AdjDir.get(DIR_3D.XPLUS) + AdjDir.get(DIR_3D.YMINUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XPLUS));
		dir.plusHere(AdjDir.get(DIR_3D.YMINUS));
		
		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.YMINUS; // X+ . Y-
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }
	    if(zp < tolerance)
	    {	
		// Z+

		// srcspc + AdjDir.get(DIR_3D.XPLUS) + AdjDir.get(DIR_3D.ZPLUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XPLUS));
		dir.plusNew(AdjDir.get(DIR_3D.ZPLUS));
		
		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZPLUS; // X+ . Z+
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if(zm < tolerance)
	    {	
		// Z-
		//srcspc + AdjDir.get(DIR_3D.XPLUS) + AdjDir.get(DIR_3D.ZMINUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XPLUS));
		dir.plusHere(AdjDir.get(DIR_3D.ZMINUS));
		
		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZMINUS; // X+ . Z-
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }
	}else if(xm < tolerance)
	{
	    //----- X Minus
	    if(yp < tolerance)
	    {			
		// Y+

		//srcspc + AdjDir.get(DIR_3D.XMINUS) + AdjDir.get(DIR_3D.YPLUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XMINUS));
		dir.plusHere(AdjDir.get(DIR_3D.YPLUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.YPLUS; // X- . Y+
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if(ym < tolerance)
	    {			
		// Y-

		//srcspc + AdjDir.get(DIR_3D.XMINUS)+ AdjDir.get(DIR_3D.YMINUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XMINUS));
		dir.plusHere(AdjDir.get(DIR_3D.YMINUS));
		
		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.YMINUS; // X- . Y-
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }
	    if(zp < tolerance)
	    {		
		// Z+
		
		//srcspc + AdjDir.get(DIR_3D.XMINUS) + AdjDir.get(DIR_3D.ZPLUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XMINUS));
		dir.plusHere(AdjDir.get(DIR_3D.ZPLUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZPLUS; // X- . Z+
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if(zm < tolerance)
	    {	
		// Z-

		//srcspc + AdjDir.get(DIR_3D.XMINUS)+ AdjDir.get(DIR_3D.ZMINUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.XMINUS)); 
		dir.plusHere(AdjDir.get(DIR_3D.ZMINUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZMINUS; // X- . Z-
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }
	}
	//-------------------- YZ
	if(yp < tolerance) 
	{
	    //----- YPlus
	    if(zp < tolerance)
	    {			
		// Z+
		// srcspc + AdjDir.get(DIR_3D.YPLUS) + AdjDir.get(DIR_3D.ZPLUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.YPLUS));
		dir.plusHere(AdjDir.get(DIR_3D.ZPLUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZPLUS; // Y+ . Z+
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if(zm < tolerance)
	    {	
		// Z-

		//srcspc + AdjDir.get(DIR_3D.YPLUS) + AdjDir.get(DIR_3D.ZMINUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.YPLUS));
		dir.plusHere(AdjDir.get(DIR_3D.ZMINUS));
		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZMINUS; // Y+ . Z-
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }
	}else if(ym < tolerance)
	{
	    //----- Y Minus
	    if(zp < tolerance)
	    {	
		// Z+

		//srcspc + AdjDir.get(DIR_3D.YMINUS) + AdjDir.get(DIR_3D.ZPLUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.YMINUS));
		dir.plusHere(AdjDir.get(DIR_3D.ZPLUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZPLUS; // Y- . Z+
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }else if(zm < tolerance)
	    {	
		// Z-

		//srcspc + AdjDir.get(DIR_3D.YMINUS)+ AdjDir.get(DIR_3D.ZMINUS)
		IntVec3 dir = srcspc.plusNew(AdjDir.get(DIR_3D.YMINUS));
		dir.plusHere(AdjDir.get(DIR_3D.ZMINUS));

		verint.setDestSpcPos(dir);
		if(this.checkInSpc(verint.peekDestSpcPos()) == true)
		{
		    // verint.mRestDir = DIR_3D.ZMINUS; // Y- . Z-
		    this.sendVerIntToOtherSpcNrecv(verint);
		}
	    }
	}
    }
    
    /**
     * その空間指標は定義空間内か
     */
    private boolean checkInSpc(final IntVec3 spcpos)
    {
	final IntVec3 maxIdx = 
	    this.mPE.peekSpaceParam().peekMaxDefSpcIdxPlusOne();
	int x = spcpos.get(IntVec3.X);
	if((x < 0) || (x >= (maxIdx.get(IntVec3.X))))
	{
	    return(false);
	}
	int y = spcpos.get(IntVec3.Y);
	if((y < 0) || (y >= (maxIdx.get(IntVec3.Y))))
	{
	    return(false);
	}
	int z = spcpos.get(IntVec3.Z);
	if((z < 0) || (z >= (maxIdx.get(IntVec3.Z))))
	{
	    return(false);
	}
	return(true);
    }


    /**
     * 輝度情報の送信
     */
    private void sendVerIntToOtherSpcNrecv(VertexIntensity verint)
    {
	IntVec3 destPos   = verint.peekDestSpcPos();
	NetIntUnit niUnit = this.mPE.peekNIUnit();
	IntVec3 destPEId  = niUnit.getNewPEIDFromSpcPos(destPos);

	if(destPEId.equals(this.mPE.peekID()))
	{
	    // 次空間は自分の担当するものだった
	    this.addIntensity(verint);
	}else
	{
	    // 次空間は自分の担当するものではなかった -> 送信
	    
	    // PEID -> rank
	    int destRank = this.mPE.getRank(destPEId);

	    // 送信するものは一度 new する
	    VertexIntensity sendVerInt = new VertexIntensity(verint);

	    // rank の PE へ送信
	    niUnit.sendVerInt(sendVerInt, destRank);

	    // 受信テスト
	    niUnit.recvVerInt();
	
	    // 受信が完了していたら受信キューの内容を取り出す
	    while(this.mVerIntQueue.length() > 0)
	    {
		VertexIntensity v = (VertexIntensity)(this.mVerIntQueue.deq());
		this.addIntensity(v);
		// delete pV;		// 受信したものは new されている
	    }
	}
    }

    /**
     * addIntensity
     *	輝度を加えられるか調べ、可能なら加える
     *	加えた頂点の数を覚えておく
     */
    private void addIntensity(VertexIntensity verint)
    {
	Map objmap = this.mPE.peekSpaceMap();
	if(objmap.containsKey(verint.peekDestSpcPos()) == true)
	{
	    Vector objVec = (Vector)(objmap.get(verint.peekDestSpcPos()));
	    int destObjNum = objVec.size();

	    for(int i = 0; i < destObjNum; i++)
	    {
		ObjectPrimitive obj = (ObjectPrimitive)objVec.elementAt(i);
		// 何度加えても良い(いくつものポリゴンが集まった頂点の
		// 可能性がある)  
		// 受信候補は現在パッチのみ
		if(obj.who() == ObjectPrimitive.OBJ_TRIANGLE) 
		{
		    TriangleObject tri = (TriangleObject)obj;
		    // ポリゴン ID と vertex 番号を比較し，同じならば加える
		    tri.addIntensityToVertex(verint);
		}
	    }
	}
    }

    /**
     * GouroudShading を行うための予備計算
     *   計算に必要な情報の送信は終了している
     *	通信して加算した輝度を平均する
     */
    private void finalizePrepareShadingStage()
    {
	Map objMap = this.mPE.peekSpaceMap();

	for(Iterator i = objMap.values().iterator(); i.hasNext(); ) 	
	{
	    Vector objVec = (Vector)(i.next());
	    for(int j = 0; j < objVec.size(); j++)
	    {
		ObjectPrimitive obj = (ObjectPrimitive)(objVec.elementAt(j));
		if(obj.who() == ObjectPrimitive.OBJ_TRIANGLE)
		{
		    TriangleObject tri = (TriangleObject)(objVec.elementAt(j));
		    tri.calcVerIntAveVal();
		}
	    }
	}
    }

    /**
     * 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_PrepareShading: 	// prepare shading stage 終了
	    this.mIsPrepareShadingOver = true;
	    break;

	    // 以下は shading stage では発生しないはず
	case End_Radiosity: 	
	case End_Raytrace:
	case Suspend_GenNewRays: 
	case Resume_GenNewRays:
	case Send_Patch_Descriptions:
	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);
	}
    }
}
