/*
 * $Id: Host.java,v 1.30.4.3 2000/06/19 17:46:17 yamauchi Exp $
 *     (M\pi)^2 : Host Computer
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source host.h
 *
 *	host は rank = (size - 1) の者が担当する。
 * 
 * 1999年8月 3日(火)
 */

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

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.sysparam.*;
import  jp.ac.tohoku.is.archi.mpi2.raytrace.*;
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;

/**
 * Host Computer
 */
public final class Host 
implements Mpi2Constants, Mpi2Enum, HostConstants
{
    /*
     * singleton 
     */
    private static Host mHost = null;
    public  static Host getInstance()
    {
	if(mHost == null)
	{
	    mHost = new Host();
	}
	return(mHost);
    }

    /*
     * member
     */
    private int	       	mRank;		// 自分の rank
    private SysInfo    	mSysInfo; 	// system info.
    private ViewData   	mViewData;	// view info.
    private SpaceParam 	mSpcParam;	// space division info.
    private Map         mMaterialMap;	// material map
    // private Material [] mMaterialArray;	// material array
    private Space2PE   	mSpace2PE; 	// space -> PE mapping

    private Array3D 	mObjNumInSpcInfoArray; // 各空間の物体の数の情報
    private Array3D 	mObjExistInSpcInfoArray; // 各空間の物体の存在情報

    private FrameBuffer mGlobalFrameBuffer;

    private PEStat []	mPEStats; 	    // PE の稼働状況
    private int 	mReqNumOfPEStopRays; // ray 発生停止を要求しているPE数
    
    // network interface unit for host
    private Queue  mIntensityQueue;	// 輝度用ポインタ保持キュー
    private RecvBuffer mIntensityRecvBuff; // FIXME 複数持たせる

    // PE からの状態 (PEStat) を受信する．PE へ命令(HostComm)を送信する． 
    private Queue mPEStatRecvQueue;	// PEStat ptr 保持キュー
    private RecvBuffer mRecvPEStatBuf;
    private SendBuffer mSendHostCommBuf;

    private HostStatistics mHStat;	

    /*
      protected:			// for PRC method
      char *mpDistanceDataFileName;	
    */

    
    /**
     * check space information
     */
    private void checkSpcData()
    {
	IntVec3 penum = this.mSysInfo.peekPEDim();
	int subdivnum = this.mSpcParam.getSubDivNum();
	// 分配可能か (大きさ)
	if((subdivnum < penum.get(IntVec3.X)) || 
	   (subdivnum < penum.get(IntVec3.Y)) ||
	   (subdivnum < penum.get(IntVec3.Z)))
	{
	    String s = "Error! : space subdivision number is smaller than "+
		"number of PEs.\n" + 
		"Numver of PEs = " + penum + ", number of space " +
		"subdivision " + subdivnum;
	    throw new RuntimeException(s);
	}
	// 分配可能か (割り切れるか)
	if(((subdivnum % penum.get(IntVec3.X)) != 0) || 
	   ((subdivnum % penum.get(IntVec3.Y)) != 0) ||
	   ((subdivnum % penum.get(IntVec3.Z)) != 0))
	{
	    String s = "Error! : space subdivision number can not divede " + 
		"by number of PEs.=n" + 
		"Numver of PEs = " + penum + ", number of space " +
		"subdivision " + subdivnum;
	    throw new RuntimeException(s);
	}
	
	// 定義空間は分割空間と同じかそれ以下でなくてはいけない
	// partmin <= def min, max  <= partmax
	Vector3 defmin  = this.mSpcParam.peekDefSpaceMin();
	Vector3 defmax  = this.mSpcParam.peekDefSpaceMax();
	Vector3 partmin = this.mSpcParam.peekPartVolumeMin();    
	Vector3 partmax = this.mSpcParam.peekPartVolumeMax();    
	for(int i = 0; i < 3; i++)
	{
	    if((partmin.get(i) > defmin.get(i))  || 
	       (defmax.get(i)  > partmax.get(i)))
	    {
		String s = "Error! : illegal define space and part volume. " + 
		    "There must be partmin <= def min, max  <= partmax\n" +
		    "define space(" + defmin + ")-(" + defmax + ")\n" +
		    "partition volume (" + partmin + ")-(" + partmax
		    + ")";
		throw new RuntimeException(s);		
	    }
	}
	// 定義空間の分割は分割空間と同じでなくてはいけない(min はそろえる)
	if(!(defmin.equals(partmin)))
	{
	    String s = "Error! : partition volume minimam must be same to " + 
		"define space\n" +
		"define space     min (" + defmin  + ")\n" + 
		"partition volume min (" + partmin + ")";
	    throw new RuntimeException(s);			    
	}
	partmax.minusHere(partmin);
	defmax.minusHere(defmin);
	double partOneSize, defsize;
	for(int i = 0; i < 3; i++)
	{
	    // 1つの副空間の大きさ
	    partOneSize = partmax.get(i) / subdivnum; 
	    defsize     = defmax.get(i);
	    boolean canDev = false;
	    for(int sd = 1; sd <= subdivnum; sd++)
	    {
		if(Math.abs((defsize / partOneSize) - (double)(sd)) < 
		   (partOneSize * 1e-4))
		{
		    // (partOneSize * 1e-4) は副空間の大きさに対して 1e-4 
		    // 倍という十分小さい数  
		    canDev = true;
		    break;
		}
	    }
	    if(canDev == false)
	    {
		String s = "Error! : each subdefine " + 
		    "space is same as subparttition space.\n" + 
		    "define space(" + defmin + ")-(" + defmax + ")\n" +
		    "partition volume (" + partmin + ")-(" + partmax
		    + ")";
	    }
	}
    }

    /**
     * set spc2PE
     *	空間と PE のマッピングを生成する
     *  @ref NetintUnit.java  private void initSpc2PE()
     */
    private void mapSpacePE()
    {
	// 分配のために空間と PE のマッピングを作成する
	switch(this.mSysInfo.getAllocationScheme())
	{
	case Block_Alloc:
	{
	    Space2PEBlock s2p = new Space2PEBlock();
	    s2p.init(this.mSysInfo.peekPEDim(), 
		     this.mSpcParam.getSubDivNum(), HostID);
	    this.mSpace2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("readData : allocated " + 
						"Space2PEBlock");
	}
        break;
	
	case Distributed_Alloc:
	{
	    Space2PEDistributed s2p = new Space2PEDistributed();
	    s2p.init(this.mSysInfo.peekPEDim(), 
		     this.mSpcParam.getSubDivNum(), HostID);
	    this.mSpace2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("readData : allocated " + 
						"Space2PEDistributed");
	}
	break;
	
	case Skewed_Alloc:
	{
	    Space2PESkewedDistributed s2p = new Space2PESkewedDistributed();
	    s2p.init(this.mSysInfo.peekPEDim(), this.mSpcParam.getSubDivNum(),
		     HostID);
	    this.mSpace2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("readData allocated " + 
						"Space2PESkewedDistributed");
	}
	break;
	
	case Random_Alloc:
	{
	    Space2PERandom s2p = new Space2PERandom();
	    s2p.init(this.mSysInfo.peekPEDim(), 
		     this.mSpcParam.getSubDivNum(),
		     HostID);
	    this.mSpace2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("readData : allocated " + 
						"Space2PERandom");
	}
	break;
	
	default:
	    this.mSpace2PE = null;
	    throw new RuntimeException("Error! : no such allocation scheme " + 
				       "Host.mapSpacePE");
	}
    }
    
    /**
     * get host rank
     */
    private int  getRank() 	
    {
	return(this.mRank);	
    }

    /**
     * read sysinfo and deliver to system
     */
    private void readSysinfo(String sysinfoFname)
    {
	this.mSysInfo = new SysInfo();
	try {
	    this.mSysInfo.readFromFile(sysinfoFname);
	}catch(IOException e)
	{
	    System.out.println("Error! : fail to read the sysinfo file.\n"+e);
	    Mpi2CommFactory.getInstance().exit(1);
	}
	if(Mpi2Option.getInstance().getVerboseMode())
	{
	    final String head = Mpi2Option.toHead(VERBOSEHEAD, HostID);
	    System.out.println(head + "----- read sysinfo succeeded.");
	    System.out.println(this.mSysInfo.toStringWithHead(head));
	}
	IntVec3 pe = this.mSysInfo.peekPEDim();
	int penum  = this.mSysInfo.getTotalPENum();
	System.out.println(NORMALHEAD + " host.systemdim " + 
			   Space2PE.getSystemDimension(pe) + " totalPEnum " +
			   penum + " PEtopology " + pe);
	if(this.getRank() != penum)
	{
	    System.out.println(NORMALHEAD + " host.Error! rank(" + 
			       this.getRank() + ") and PEnum(" + penum + 
			       ") is not match. abort.");
	    Mpi2CommFactory.getInstance().exit(1);	
	}
    }

    /**
     * read space information 
     */
    private void readSpcData(String spcFname)
    {
	this.mSpcParam = new SpaceParam();
	try {
	    this.mSpcParam.readFromFile(spcFname);
	}catch(IOException e)
	{
	    System.out.println("Error! : fail to read the spcparam file.\n"+e);
	    Mpi2CommFactory.getInstance().exit(1);
	}
	if(Mpi2Option.getInstance().getVerboseMode())
	{
	    final String head = Mpi2Option.toHead(VERBOSEHEAD, HostID);
	    System.out.println(head + "----- read space param succeeded.");
	    System.out.println(this.mSpcParam.toStringWithHead(head));
	}
    }

    /**
     * read viewdata  
     */
    private void readViewdata(String viewFname)
    {
	this.mViewData = new ViewData();
	try {
	    this.mViewData.readFromFile(viewFname);
	}catch(IOException e)
	{
	    System.out.println("Error! : fail to read the viewdada file.\n"+e);
	    Mpi2CommFactory.getInstance().exit(1);
	}
	if(Mpi2Option.getInstance().getVerboseMode())
	{
	    final String head = Mpi2Option.toHead(VERBOSEHEAD, HostID);
	    System.out.println(head + "----- read viewdata succeeded.");
	    System.out.println(this.mViewData.toStringWithHead(head));
	}	    
    }

    /**
     * read and deliver material data
     *------------------------------------------------------------
     * read material data
     *   material の情報を読み、名前で sort して host のメンバとして保持す
     *   る 
     */
    private void readMaterialData(String matFname)
    {
	this.mMaterialMap = new HashMap();
	try {
	    MGFReader reader =
		new MGFReader(new InputStreamReader(new 
						    FileInputStream(matFname)),
			      this.mMaterialMap);
	    ObjectPrimitive obj = null;
	    if((obj = reader.readItem()) != null) 
	    {
		System.out.println("Warning! : Material file contains " + 
				   "objects:"+ obj);
	    }
	}catch(IOException e)
	{
	    System.out.println("Error! : readMaterialData : " + e);	    
	}

	// sort material set
	// SortedSet s = new TreeSet();  No need to sort

	// set material ID 
	int matID = 0;
	for(Iterator i = this.mMaterialMap.values().iterator(); i.hasNext(); ) 
	{
	    Material m = (Material)(i.next());
	    m.setID(matID);
	    matID++;
	    // System.out.println(m);
	}

	// set to array
	// this.mMaterialArray = new Material[s.size()];
	// int idx = 0;
	// 	for(Iterator i = s.iterator(); i.hasNext(); ) 
	// 	{
	// 	    this.mMaterialArray[idx] = (Material)(i.next());
	// 	    idx++;
	// 	}
	
	if(Mpi2Option.getInstance().getShowDataMode())
	{
	    System.out.println(VERBOSEHEAD + 
			       "[========== Readed Data ==========]");
	    System.out.println(VERBOSEHEAD + "materials");

	    for(Iterator i = this.mMaterialMap.values().iterator(); 
		i.hasNext(); ) 
	    {
		Material m = (Material)(i.next());
		System.out.println(m);
	    }
	}
    }
    
    /**
     * deliver material data : IMPROVE まとめ送りが可能
     */
    private void deliverMaterial()
    {
	Assert.out(!(FileInfo.getInstance().getMatFileName().
		     equals(FileInfo.INIT_FILENAME)), 
		   "Error!: no material filename but called deliver material");

	for(Iterator i = this.mMaterialMap.values().iterator(); i.hasNext(); ) 
	{
	    Material [] mat = new Material[1];
	    mat[0] = new Material((Material)(i.next()));
	    Mpi2CommFactory.getInstance().broadMaterial(mat);
	}
    }

    /**
     * null material の送信 : 単に new すると nullmaterial になる
     * しかし，Java では全てが Object なので null を送っても問題な
     * さそう．とりあえずは C++ と同じ実装でいくか．1999年8月12日(木)
     */
    private void broadNullMaterial()
    {
	Material [] mat = new Material[1];
	mat[0] = new Material();
	Mpi2CommFactory.getInstance().broadMaterial(mat);
    }

    /**
     * read and deliver material data
     */
    private void readAndDeliverMaterialData()
    {
	// read material and geometry, and deliver to PEs
	FileInfo fif = FileInfo.getInstance();
	if(!(fif.getMatFileName().equals(FileInfo.INIT_FILENAME)))
	{
	    this.readMaterialData(FileInfo.getInstance().getMatFileName());
	    this.deliverMaterial();
	    Mpi2Option.getInstance().verboseOut("broadcasting material... "+
						"done.");
	}
	this.broadNullMaterial();
    }

    /**
     * read geometry data and deliver them to PEs 
     *	読み込みながら送信する
     *	現在は一つづつ送信する : IMPROVE まとめ送信
     *	物体データを1つでも読み込めたら true を返す
     */
    private void readAndDeliverGeometryData()
    {
	// 部分空間物体数管理表が既に存在する
	Assert.out(this.mObjNumInSpcInfoArray == null, "Error!: Already "+
		   "there is ObjInSpcInfoArray. Host.readAndDeliverGeoData()");

	// 部分空間物体数管理表の作成
	int [] maxIdxP1 =
	    (this.mSpcParam.peekMaxDefSpcIdxPlusOne()).getNewAsArray();
	this.mObjNumInSpcInfoArray = new Array3D();
	this.mObjNumInSpcInfoArray.initBySizeWithBaseZero(maxIdxP1);
	int [] idx = new int[3];
	for(idx[0] = 0; idx[0] < maxIdxP1[0]; idx[0]++)
	{
	    for(idx[1] = 0; idx[1] < maxIdxP1[1]; idx[1]++)
	    {
		for(idx[2] = 0; idx[2] < maxIdxP1[2]; idx[2]++)
		{
		    this.mObjNumInSpcInfoArray.set(idx, new ObjNumInASpc());
		}
	    }
	}

	// geometry file
	FileInfo fif = FileInfo.getInstance();
	String geoFileName = fif.getGeoFileName();
	Assert.out(!(geoFileName.equals(FileInfo.INIT_FILENAME)), 
		   "Error!: no geometry file is indicated.");

	// 読み込み
	MGFReader geoReader = null;
	try {
	    // これまでの material Map を渡すべき
	    geoReader = 
		new MGFReader(
		    new InputStreamReader(new FileInputStream(geoFileName), 
					  "ASCII"),
		    this.mMaterialMap);
	}catch(IOException e)
	{
	    System.out.println("Error! : read geometry data : " + e);	    
	}

	// 空間位置確認のための情報
	int   	spcDevNum  = this.mSpcParam.getSubDivNum();
	Vector3 spcPartMin = new Vector3(this.mSpcParam.peekPartVolumeMin());
	Vector3 spcPartMax = new Vector3(this.mSpcParam.peekPartVolumeMax());
	// subSpcSize = (spcPartMax - spcPartMin) / spcDevNum
	Vector3 subSpcSize = 
	    (spcPartMax.minusNew(spcPartMin)).divScalerNew(spcDevNum);

	int numOfReadObject = 0; 	// 読み込んだ物体数
	ObjectPrimitive obj = null; 	
	try {
	    while((obj = geoReader.readItem()) != null) 
	    {
		// count up numver of objects
		numOfReadObject++;	

		// output read data
		if(Mpi2Option.getInstance().getShowDataMode())
		{
		    System.out.println(obj.toStringWithHead(VERBOSEHEAD));
		}
		
		// 空間中の位置確認
		if(!(obj.isAtValidSpcPos(spcPartMin, subSpcSize, spcDevNum)))
		{
		    System.out.println("Error! : inconsistency occur between "+
				       "spcpos and object at line " + 
				       geoReader.getLineNumber());
		}
		
		ObjNumInASpc onias = (ObjNumInASpc)(this.mObjNumInSpcInfoArray.
						    get(obj.peekSpcPos()));
		onias.incGeoNum();
		this.mObjNumInSpcInfoArray.set(obj.peekSpcPos(), onias);

		// deliver object
		IntVec3 PEid = 
		    this.mSpace2PE.getNewPEIDfromSpcPos(obj.peekSpcPos());

		Mpi2CommFactory.getInstance().sendObject(this.getRank(PEid), 
							 obj);

		if(Mpi2Option.getInstance().getVerboseMode())
		{
		    if(numOfReadObject == 1)
		    {
			System.out.print("## Send Objs: `o'/1000 objs.\n##");
		    }
		    if(numOfReadObject % 1000 == 0)		    
		    {
			System.out.print("o");
		    }
		}
	    }
	    Mpi2Option.getInstance().verboseOut("...done.");	    
	}catch(Exception e)
	{
	    throw new RuntimeException("fail to read geometry file: obj [" +
				       numOfReadObject + "] " + e);
	}

	// send null ObjectPrimitive for terminate receiving objects.
	//   PE の Rank は 0 から size - 1 まで
	final int maxPERank = SysStat.getInstance().getCommSize() - 1;
	for(int rank = 0; rank < maxPERank; rank++)
	{
	    Mpi2CommFactory.getInstance().sendObject(rank, new NullObject());
	}

	if(Mpi2Option.getInstance().getVerboseMode())
	{
	    System.out.println(Mpi2Option.toHead(VERBOSEHEAD, HostID) + 
			       "total number of geometry data = " + 
			       numOfReadObject);
	}
	Assert.out(numOfReadObject >= 0, "Error!: I can read no object.");

	/*
	  if(numOfReadObject > 0)
	  {
	  return(true);		
	  }else
	  {
	  return(false);	    
	  }
	  */
    }

    /**
     * read volume data and deliver them to PEs 
     *	読み込みながら送信する
     */
    private void readAndDeliverVolumeData()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN : Host readAndDeliverVolumeData");

	/*------------------------------------------------------------
	 * volume data の読み込み (file が存在した時のみ)
	 *------------------------------------------------------------
	 int numOfTotalReadVols = 0;
	 if(FileInfo.get().getVolumeInFileListName() != FileInfo.INIT_FILENAME)
	 {				
	 // ref.: volumeReader.cc
	 // この形のように object in space array を渡す形が良い
	 char *pVolInFList = FileInfo.get().getVolumeInFileListName();
	 numOfTotalReadVols = readAllVolumeData(pVolInFList, this.mSpcParam,
	 (this.mObjInSpcInfoArray));
	 }
	 this.broadNullVolume(numOfTotalReadVols); //読み込み終了を各 PE へ示す
	 */
    }
    
    /**
     * 物体存在表の配布
     * 	geometry と volume data を読み込み中に部分空間中の Object 存在 
     * 	情報を作成し，それをこの関数で配布する．
     */
    private void deliverObjInSubSpaceInfo()
    {
	switch(this.mSysInfo.getRayPropSkipMethod())
	{
	case NoSkip_rayprop: // skip なしでも物体情報の統計のためにdeliverする
	case SobokuSkip_rayprop:
	    this.deliverObjInSpaceInfoSoboku();
	    break;
	case ProximityClouds_rayprop:
	    Mpi2Option.getInstance().
		verboseOut("NIN : deliverObjInSubSpaceInfo");	    

	    Mpi2CommFactory.getInstance().exit(1);
	    // NIN  this.PRC_deliverSkipInfoMap(FileInfo::get(). 
	    // 		 getDistanceDataFileName());
	    break;
	default:
	    System.out.println("Internal Error!: Host.deliverObjInSpaceInfo");
	    Mpi2CommFactory.getInstance().exit(1);
	    break;
	}

	// 表の内容を出力する
	if(Mpi2Option.getInstance().getShowNumobjInSpace()) 
	{
	    SearchNextSpcDDA.
		printObjInSpcMapCommon(NORMALHEAD + " host:: ",
				       this.mObjExistInSpcInfoArray);
	}
    }

    /**
     * 物体存在情報の配布
     *	素朴な skip バージョン
     */
    private void deliverObjInSpaceInfoSoboku()
    {
	Assert.out((this.mSysInfo.getRayPropSkipMethod()== NoSkip_rayprop) || 
		   (this.mSysInfo.getRayPropSkipMethod()== SobokuSkip_rayprop),
		   "Internal Error! : deliverObjInSpaceInfoSoboku");

	this.mObjExistInSpcInfoArray = new Array3D();
	int [] size = new int[3];
	size[0] = this.mObjNumInSpcInfoArray.getSize(0);
	size[1] = this.mObjNumInSpcInfoArray.getSize(1);
	size[2] = this.mObjNumInSpcInfoArray.getSize(2);
	this.mObjExistInSpcInfoArray.initBySizeWithBaseZero(size);
	
	for(int idx=0; idx < this.mObjNumInSpcInfoArray.getTotalSize(); idx++)
	{
	    // 物体の数の情報を存在の情報だけに変換する
	    ObjNumInASpc onias = (ObjNumInASpc)
		(this.mObjNumInSpcInfoArray.peekAsOneDimArray(idx));
	    ObjExistanceInASpc oeias = onias.newObjExistanceInASpc();
	    
	    // 存在情報をセット
	    this.mObjExistInSpcInfoArray.setAsOneDimArray(idx, oeias);
	}   

	// 存在情報を送信
	Array3D [] a3d = new Array3D[1];
	a3d[0] = this.mObjExistInSpcInfoArray;
	Mpi2CommFactory.getInstance().broadObjectInSpaceMap(a3d); 
    }

    /**
     * out put result image
     */
    public void outResult()
    {
	this.saveResult(FileInfo.getInstance().getSaveResultFileName());
    }

    /**
     * save result
     */
    public void saveResult(String outfname)
    {
	if(Mpi2Option.getInstance().getVerboseMode() == true)
	{
	    System.out.println(VERBOSEHEAD + " host: save file is " +
			       outfname);
	}
	this.mGlobalFrameBuffer.saveData(outfname);
    }

    /* 
       void broadNullObject();
       void saveResult(char *outFile);
     */

    /**
     * 全ての PE が稼働中のようにセットする
     */
    private void initPEStatAllMoving()
    {
	Assert.out(SysStat.getInstance().getCommSize() > 1, 
		   "Error!: size must be > 1");
	if(this.mPEStats == null)
	{
	    this.mPEStats = 
		new PEStat[SysStat.getInstance().getCommSize() - 1];
	}

	// PEはrank [0, size - 1]
	for(int i = 0; i < (SysStat.getInstance().getCommSize() - 1); i++)
	{	
	    this.mPEStats[i] = new PEStat();
	    this.mPEStats[i].setBusy(i);
	}
    }


    /**
     * PE 状態の記録の更新
     */
    private void updatePEstat(PEStat pestat)
    {
	int perank = pestat.getSrcRank();
	Assert.out((0 <= perank) && 
		   (perank < SysStat.getInstance().getCommSize() - 1),
		   "Error!: illegal src rank. updatePEstat");
	this.mPEStats[perank].set(pestat);
	
	if(Mpi2Option.getInstance().getDebugMode())
	{
	    int i; 
	    System.out.print(DEBUGHEAD + " host :: ");
	    for(i = 0; i < SysStat.getInstance().getCommSize() - 1; i++)
	    {
		System.out.print(i + " " + this.mPEStats[i] + " ");
	    }
	    System.out.println("");
	}
    }


    /**
     * analyze PE information and action 
     */
    private void analyzePEInfo(PEStat pestat)
    {
	if(pestat.getStat() != PEStat.PESt_ELSE)
	{
	    /*
	     * PE の status 報告
	     *    system 動作状況を更新する
	     */
	    this.updatePEstat(pestat);
	}else
	{
	    // PE からの要求
	    if(pestat.getRequest() == PEStat.PEReq_StopGenRays)
	    {
		if(this.mReqNumOfPEStopRays == 0) // 初の停止要求
		{
		    // システムにレイ発生停止要求
		    HostCommand hcom = 
			new HostCommand(Suspend_GenNewRays);
		    this.broadHostCommand(hcom);
		    outHostComForRayGenMessage("suspend", pestat.getSrcRank());
		}
		this.mReqNumOfPEStopRays++;
// 		System.out.println("Debug: from " + pestat.getSrcRank() +
// 				   " get stop ray request.");;
	    }
	    else if(pestat.getRequest() == PEStat.PEReq_ResumeGenRays)
	    {
		if(this.mReqNumOfPEStopRays == 1) // 最後の再開要求
		{
		    // システムにレイ発生再開要求
		    HostCommand hcom = new HostCommand(Resume_GenNewRays);
		    this.broadHostCommand(hcom);
		    outHostComForRayGenMessage("resume",  pestat.getSrcRank());
		}
		this.mReqNumOfPEStopRays--;
// 		System.out.println("Debug: from " + pestat.getSrcRank() + 
// 				   " resum stop ray request.");
	    }
	}
    }

    /**
     * movingPE  
     *	@return 1つでも動作中の PE があれば true
     */
    private boolean movingPE()
    {
	/*
	 * PE の rank は [0, size - 1]
	 */
	for(int i = 0; i < (SysStat.getInstance().getCommSize() - 1); i++) 
	{	
	    if(this.mPEStats[i].getStat() == PEStat.PESt_BUSY)
	    {
		return(true);
	    }
	}
	return(false);    
    }


    /**
     * Host command を全 PE へ送信
     *	(MPI には non blocking broadcast がない)
     */
    private void broadHostCommand(HostCommand hostcomm)
    {
	for(int i = 0; i < (SysStat.getInstance().getCommSize() - 1); i++)
	{
	    if(Mpi2Option.getInstance().getDebugMode() == true)
	    {
		System.out.println(Mpi2Option.toHead(DEBUGHEAD, this.getRank())
				   + " broad host command " + this.getRank() +
				   " to " + i + "\t" + hostcomm);
	    }
	    // コピーを作成し，送付
	    HostCommand hc_copy = new HostCommand(hostcomm);
	    this.mSendHostCommBuf.enqNewedObjwithMaySend(hc_copy, i);
	}
    }

    /*
    void printObjInSpcMap() ;
    */
    
    /**
     * consistency check
     *	data file の一貫性について
     *	気がついたことを check することにする
     */
    private void consistencyCheck()
    {
	// warning error
	if(this.mSysInfo.getFirstRaysGenerationMethod() == AllScan_frgm)
	{
	    if(this.mViewData.getDistributedRayNum() > 1)
	    {
// 		String s = 
// 		    "Warning! : When First Ray Generation is `ALLScan' mode."+
// 		    "\t rays are generated but not distributed on a pixel." +
// 		    "`ALLScan' mode 時には 1 pixel に複数のrayを発っした場合"+
// 		    "並列動作においては正しい画像が得られません．\n"+
// 		    "ある pixel のランダムな位置に ray が発射された場合に，"+
// 		    "その ray がその担当空間のものかを決定する際，全部を scan"+
// 		    "して取捨選択するアルゴリズムでは，各 PE で同一の"+
// 		    " pixel にはまったく同じ ray のサンプリングが必要ですが，"+
// 		    "その実現には隣接空間を担当する PE 間での交渉等が必要と"+
// 		    "なります．これは ALLScan 特有の問題で他の分散初期光発生"+
// 		    "では自然に解決できるので，Allscan の場合だけ余計なこと"+
// 		    "をするのは比較に対して公平でないとの判断からそのままに"+
// 		    "してあります．";
// 		System.out.println(s);
	    }
	}
	
	// exit error
	if(this.mSysInfo.isRayTracingCalc() == false)
	{
	    if(this.mViewData.getDistributedRayNum() > 1)
	    {
		System.out.println("Error! : ray tracing mode is off.");
		System.out.println("\tDistributedRayNum must be 1 instead of"+
				   this.mViewData.getDistributedRayNum());
		Mpi2CommFactory.getInstance().exit(1);
	    }
	}
    }
    
/*
    protected:
    // volume rendering related functions (ref. in volumeReadHoost.cc)
    int  readAndDeliverVolDataAll();
    boolean readAndDeliverVolOneData(char *pFile, int volumeID);
    void broadNullVolume(int maxNumOfDeliverdVolume);

protected:
    // for PRC method : データ表配布 deliverSkipInfoPRC.cc で実装
    //tokunaga changed	
    void PRC_deliverSkipInfoMap(char  *pDistanceFname);    
    */

    /**
     * Host と PE 間の通信
     */
    private void initNetworkInterfaceUnit()
    {
	/*
	 * PEStat 受信バッファ， HostComm 送信バッファの初期化
	 * 現在は一つでもあれば送信する．
	 * ここで期待するのは送受信が Nonblocking ということのみでまとめ送
	 * りの効果はない．
	 */
	final int recvIntensityThresh = this.mSysInfo.getSendInt2FBAtOnce();

	// intensity
	// PROF this.mIntensityQueue 	= new VectorQueue();
	this.mIntensityQueue 	= new RingBufferQueue();
	this.mIntensityRecvBuff	= new RecvBuffer();
	this.mIntensityRecvBuff.init(recvIntensityThresh, this.mIntensityQueue,
				     Intensity.getTAG(),  
				     Intensity.getObjectDecoder());
	
	// PE からの状態 (PEStat) の受信バッファ
	final int recvPEStatThresh = 1; 
	// PROF this.mPEStatRecvQueue	= new VectorQueue();	
	this.mPEStatRecvQueue	= new RingBufferQueue();	
	this.mRecvPEStatBuf	= new RecvBuffer();
	this.mRecvPEStatBuf.init(recvPEStatThresh, this.mPEStatRecvQueue,
				 PEStat.getTAG(),  
				 PEStat.getObjectDecoder());

	// PE へ命令(HostComm)を送信するバッファ
	final int HostCommSendThreshold = 1; 
	this.mSendHostCommBuf	= new SendBuffer();
	this.mSendHostCommBuf.initBuff(HostCommSendThreshold,
				       HostCommand.getTAG());
    }

    
    /*------------------------------------------------------------
     * public functions
     *-----------------------------------------------------------*/
    public Host()
    {
	this.mRank 		= -1;
	this.mSysInfo		= null; 
	this.mViewData		= null;
	this.mSpcParam	= null;
	// this.mMaterialArray	= null;
	this.mSpace2PE		= null;
	this.mObjNumInSpcInfoArray	= null;
	this.mGlobalFrameBuffer	= null;

	this.mPEStats		= null;
	this.mReqNumOfPEStopRays= 0;
	this.mIntensityQueue	= null;
	this.mIntensityRecvBuff	= null;

	this.mPEStatRecvQueue	= null;
	this.mRecvPEStatBuf	= null;
	this.mSendHostCommBuf	= null;
	this.mHStat		= new HostStatistics();	
	// 統計情報の初期化
        this.mHStat.init();

	// for Proximity Clouds
	// this.mpDistanceDataFileName	= null;
    }

    /**
     * initialize
     *  @param hostrank 
     */
    public void init(int hostrank)
    {
	// 0 も可能であるが、その場合には PE がいない
	Assert.out(hostrank > 0, "Error!: There is no PE.");
	this.mRank	= hostrank; 

	FileInfo fif = FileInfo.getInstance();
	try {
	    fif.isOKforMpi2();
	}catch(RuntimeException e)
	{
	    System.out.println("Error! : host.init : " + 
			       " FileInfo has lack of consistency\n" + e);
	    Mpi2CommFactory.getInstance().exit(1);	
	}

	//---------- open check 
	final String emes = "Error! : can't open ";
	boolean isOK = true;
	File f = new File(fif.getSpcFileName());
	if(!f.canRead())
	{
	    isOK = false;
	    System.out.println(emes + "spc file [" + fif.getSpcFileName()+"]");
	}
	
	f = new File(fif.getSysInfoFileName());
	if(!f.canRead())
	{
	    isOK = false;
	    System.out.println(emes + "sysinfo file [" + 
			       fif.getSysInfoFileName() + "]");
	}
	
	f = new File(fif.getViewDataFileName());
	if(!f.canRead())    
	{
	    isOK = false;
	    System.out.println(emes + "viewdata file [" + 
			       fif.getViewDataFileName() + "]");
	}
	
	f = new File(fif.getSaveResultFileName());
	if(f.exists())    
	{
	    isOK = false;
	    System.out.println("Error! : there are save file [" + 
			       fif.getSaveResultFileName() + "]");
	}
	
	if(!(fif.getMatFileName().equals(FileInfo.INIT_FILENAME)))
	{
	    f = new File(fif.getMatFileName());
	    if(!f.canRead())
	    {
		isOK = false;
		System.out.println(emes+"mat file ["+fif.getMatFileName()+"]");
	    }
	    f = new File(fif.getGeoFileName());
	    if(!f.canRead())
	    {
		isOK = false;
		System.out.println(emes+"geo file ["+fif.getGeoFileName()+"]");
	    }
	}
	
	// volume infile を指定している場合
	if(!(fif.getVolumeInFileListName().equals(FileInfo.INIT_FILENAME)))
	{
	    f = new File(fif.getVolumeInFileListName());
	    if(!f.canRead())
	    {
		isOK = false;	// 指定しているにもかかわらず開けなかった
		System.out.println(emes + "volume infile [" + 
				   fif.getVolumeInFileListName() + "]");
	    }
	}

	// for PRC method
	if(!(fif.getDistanceDataFileName().equals(FileInfo.INIT_FILENAME)))
	{
	    f = new File(fif.getDistanceDataFileName());
	    if(!f.canRead())
	    {
		isOK = false;	// 指定しているにもかかわらず開けなかった
		System.out.println(emes + "map file  [" +	
				   fif.getDistanceDataFileName() + "]");
	    }
	}
	
	if(isOK == false)
	{
	    System.out.println("Error! : fail to open file(s) at Host.init()");
	    Mpi2CommFactory.getInstance().exit(1);
	}
	
	if(Mpi2Option.getInstance().getVerboseMode())
	{
	    System.out.println(fif.toStringWithHead(VERBOSEHEAD + " host::"));
	}
    }
    
    /**
     * read data
     *	・sysinfo, material は全員に broadcast される
     *	・object は読み込みごとに host から送信される。
     *	・各 PE では object を受信し、数えて収納する。
     *	・どの {material, object} でもない null{material, Object} により、
     *	  分配終了を示す 
     */
    void readAndDeliverAllData()
    {
	Mpi2Option opt = Mpi2Option.getInstance();

	// read sysinfo, viewdata, spacedata
	FileInfo fif = FileInfo.getInstance();
	this.readSysinfo(fif.getSysInfoFileName());
	this.readViewdata(fif.getViewDataFileName());
	this.readSpcData(fif.getSpcFileName());

	// consistent check
	this.consistencyCheck();

	// broadcast sysinfo, viewdata, spacedata
	SysInfo    [] sys 	= new SysInfo[1];
	sys[0] 			= this.mSysInfo;
	ViewData   [] view 	= new ViewData[1];
	view[0]			= this.mViewData;
	SpaceParam [] spcparam 	= new SpaceParam[1];
	spcparam[0] 		= this.mSpcParam;

	Mpi2CommFactory.getInstance().broadSysinfo(sys, view, spcparam);
	opt.verboseOut("host:: deliver sysinfo, viewdata, spcparam done.");

	// sysinfo, spcdata から SubSpaces <-> PE のマップを生成する。
	this.mapSpacePE();

	// check spcdata
	this.checkSpcData();

	Mpi2CommFactory.getInstance().barrier();	

    
	// read material/geometry and deliver to PEs
	this.readAndDeliverMaterialData();
	this.readAndDeliverGeometryData();
	this.readAndDeliverVolumeData();
	this.deliverObjInSubSpaceInfo(); // 部分空間中に存在する物体情報配布

	opt.verboseOut("host:: readData ok!");

	// Frame Buffer
	final int scrX = this.mViewData.getScreenSizeX();
	final int scrY = this.mViewData.getScreenSizeY();
	this.mGlobalFrameBuffer = new FrameBuffer();
	this.mGlobalFrameBuffer.init(scrX, scrY);
	if(FileInfo.getInstance().isImmediateImageOut() == true)
	{
	    this.mGlobalFrameBuffer.setImmidiateOut();
	    // output GFB size 
	    System.out.println("GFBSIZE " + scrX + " " + scrY);
	}
	opt.verboseOut("host:: framebuffer initialized.");
    }

    /**
     * initialize radiosity stage for HOST
     *	基本は ray-tracing と同じ
     */
    private void initRadiosityStage()
    {
        this.mReqNumOfPEStopRays = 0;
        this.initPEStatAllMoving();

	// Network の初期化
	this.initNetworkInterfaceUnit();
    }

    /**
     * calculate radiosity
     *	基本は ray-tracing と同じ
     */
    public void doRadiosity()
    {
	if(this.mSysInfo.isRadiosityCalc() == false)
	{
	    // skip radiosity stage
	    Mpi2Option.getInstance().verboseOut("skip radiosity stage.");
	    return;
	}

	// not skip the radiosity stage
	ElapsedTimer.outHostRawTime("start radiosity stage.");
	this.initRadiosityStage();
	
	int endRetryNum = 0;
	// 通信遅延のために再び busy になるかもしれないのでリトライする。
	while(endRetryNum < MAX_RETRY_NUM_CALC)
	{
	    while((this.movingPE() == true) ||               // 動作中 PE 有り
		  (this.mRecvPEStatBuf.isRecvObj() == true)) // あるいは受信中
	    {
		// PEStat の受信 : 受信可能な限り
		this.mRecvPEStatBuf.recv(PEStat.getTAG());	
		while(this.mPEStatRecvQueue.length() > 0)
		{
		    this.mRecvPEStatBuf.recv(PEStat.getTAG());	
		    PEStat pestat = (PEStat)this.mPEStatRecvQueue.deq();
		    this.analyzePEInfo(pestat);
		    
		    this.mHStat.incrGetPEStatsNum(); // 統計情報収集
		}

		// 受信はなくなった
		Mpi2System.pollingSleep();// wait for suppress probing load
		endRetryNum = 0; 	// reset number of retry.
	    }

	    // 受信もなく，動作中 PE もなくなった．
	    if(Mpi2Option.getInstance().getVerboseMode() == true)
	    {
		System.out.println(VERBOSEHEAD + " host. finish radiosity, " + 
				   "but wait. retry " + RETRY_WAIT_TIME + 
				   " usec: retrynum " + endRetryNum);
	    }
	    endRetryNum++;
	    this.mRecvPEStatBuf.recv(PEStat.getTAG()); // 受信を試みる
	    Mpi2System.retrySleep(); // wait for suppress probing load	
	}

	// finalize radiosity stage
	ElapsedTimer.outHostRawTime("end radiosity stage.");
	HostCommand hcomm = new HostCommand(End_Radiosity);
	this.broadHostCommand(hcomm);

	// 統計情報出力
	System.out.println(NORMALHEAD + " " + this.mHStat);
    }

    /**
     * 全 PE から object の情報を get してファイルに書く
     */
    public void getAndOutRadioResult()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN getAndOutRadioResult");
	/**
	Assert.out(FileInfo.get().isRadioStageResultOut() == true, "Error!:");
	int FNAMEBUFSIZE = 128;
	char fnamebuf[FNAMEBUFSIZE];

    // write 可能なまだ存在していない FILE * を求める (ref. fileUtil.cc)
    FileInfo *pFi = FileInfo.get();
    Assert.out(pFi.isRadioStageResultOut() == true, "Error!:");
    FILE *pfp = 
    	getUniqueWriteFP((char*)(pFi.getRadioStageOutFileName()),
			 fnamebuf, FNAMEBUFSIZE);
    
    // 各 PE からデータを受け取る
    int PEnum = (SysStat.get().getCommSize() - 1);

    for(int i = 0; i < PEnum; i++)
    {
	HostCommClass *pHcom = 
		new HostCommClass(HOSTCOM_Send_Patch_Descriptions_e);
	this.mSendHostCommBuf.enqNewedObjwithMaySend(pHcom, i);
	outputObjectsFromOnePE(i, this.getRank(), (this.mMatArray), pfp);
    }
    fclose(pfp);

    System.out.println(NORMALHEAD + " host. output radiosity intermediate result. [" 
    	 + fnamebuf + "]" + endl;

    // gzip output file
    char combuf[FNAMEBUFSIZE];
    sprintf(combuf, "gzip %s", fnamebuf);
    system(combuf);

    System.out.println( + NORMALHEAD + " host . compressed [" + fnamebuf + "] done. " 
         + getNowTime() + endl;
	 }
	 */
    }


    /**
     * prepare shading 
     *	基本は ray-tracing と同じ
     */
    public void doPrepareShading()
    {
	if(this.mSysInfo.getShadingScheme() == FlatShading)
	{
	    // flat shading
	    Mpi2Option.getInstance().
		verboseOut("flat shading. skip the prepare shading pass.");
	    return;
	}

	// not flat shading ... start prepare shading.
	ElapsedTimer.outHostRawTime("start the prepare shading pass.");
	this.initPEStatAllMoving();
    
	// ここでは idle の後にまた busy になることがないので retry は不要
	while((this.movingPE() == true) || 		     // 動作中 PE 有
	      (this.mRecvPEStatBuf.isRecvObj() == true)) // あるいは受信中
	{
	    // PEStat の受信 : 受信可能な限り
	    this.mRecvPEStatBuf.recv(PEStat.getTAG());	
	    while(this.mPEStatRecvQueue.length() > 0)
	    {
		Assert.out(this.mPEStatRecvQueue.length() > 0,
			   "Error!:");
		PEStat pestat = (PEStat)this.mPEStatRecvQueue.deq();
		this.updatePEstat(pestat); // system 動作状況の更新
		// delete pPEstat;		  // 受信時に new されている
		
		this.mHStat.incrGetPEStatsNum();
	    }

	    Mpi2System.retrySleep(); // wait for suppress probing load
	}
	ElapsedTimer.outHostRawTime("done prepare shading pass... " + 
				    "send hostcommand to all PE.");
	this.broadHostCommand(new HostCommand(End_PrepareShading));
    }

    /**
     * initialize ray-tracing stage for HOST
     */
    void initRayTraceStage()
    {
	this.initRadiosityStage(); // 初期化部分はラジオシティ法と同じ
    }


    /**
     * calculate ray-tracing
     */
    public void doRayTracing()
    {
	ElapsedTimer.outHostRawTime("start the ray tracing stage.");
	this.initRayTraceStage();
	this.mHStat.init();

	int endRetryNum = 0;	// 通信遅延のためにリトライする。その回数
	while(endRetryNum < MAX_RETRY_NUM_CALC)
	{
	    while((this.movingPE()	               == true) || //動作中PE有
		  (this.mIntensityRecvBuff.isRecvObj() == true) || //輝度受信中
		  (this.mRecvPEStatBuf.isRecvObj() == true))   // PEStat 受信中
	    {
		// 輝度情報受信キューに輝度がある間とり出す
		this.mIntensityRecvBuff.recv(Intensity.getTAG()); // 輝度受信
		while(this.mIntensityQueue.length() > 0)
		{
		    Assert.out(this.mIntensityQueue.length() > 0, 
			       "Error!: ");
		    Intensity ival = (Intensity)this.mIntensityQueue.deq();
		    this.addIntensity(ival);
		    // delete pInt;
		}
		
		// PEStat 受信キューに PEStat がある
		this.mRecvPEStatBuf.recv(PEStat.getTAG());
		while(this.mPEStatRecvQueue.length() > 0)
		{
		    this.mRecvPEStatBuf.recv(PEStat.getTAG());
		    // system 動作状況の更新
		    
		    Assert.out(this.mPEStatRecvQueue.length() > 0, 
			       "Error!:");
		    PEStat pestat = (PEStat)this.mPEStatRecvQueue.deq();
		    this.analyzePEInfo(pestat);
		    // delete pPEstat;	// 受信時に new されている
		    
		    this.mHStat.incrGetPEStatsNum(); // 統計情報収集
		}
		endRetryNum = 0;	// reset number of retry.
		Mpi2System.retrySleep(); // wait for suppress probing load
	    }
	    if(Mpi2Option.getInstance().getVerboseMode() == true)
	    {
		System.out.println(VERBOSEHEAD + " host. finish ray trace, " + 
				   " but wait. retry " + RETRY_WAIT_TIME +
				   " usec: retrynum "  + endRetryNum);
	    }
	    Mpi2System.retrySleep();
	    endRetryNum++;
	}
	
	// finalize ray-tracing stage
	ElapsedTimer.outHostRawTime("end the ray tracing stage.");
	this.broadHostCommand(new HostCommand(End_Raytrace));

	// 統計情報出力
	System.out.println(NORMALHEAD + " " + this.mHStat);
    }    
    
    /**
     * barrier 同期
     */
    public void barrierSync()
    {
	Mpi2CommFactory.getInstance().barrier();
    }
    
    /**
     * 輝度情報を加算する
     */
    public void addIntensity(Intensity intensity)
    {
	this.mGlobalFrameBuffer.addIntensity(intensity);
    }
    /*
    public IntVec3 getPEIDfromSpcPos(IntVec3 spcpos)
    {
	return(this.mSpace2PE.getPEIDfromSpcPos(spcpos));
    }
    */
    /*------------------------------
     * get Rank (PEClass. にもある) PE の id から serial ID を得る
     * serial id = (IDz * PEx * PEy) + (IDy * PEx) + IDx
     *	         = PEx * (IDz * PEy + IDy) + IDx
     */
    public int getRank(IntVec3 PEid) 
    {
	int rank = 
	    this.mSysInfo.peekPEDim().get(IntVec3.X) * 
	    (PEid.get(IntVec3.Z) * this.mSysInfo.peekPEDim().get(IntVec3.Y)
	     + PEid.get(IntVec3.Y)) + PEid.get(IntVec3.X);
	return(rank);
    }


    /*
     * output host command for ray generation message
     */
    private static void outHostComForRayGenMessage(String com, int reqPErank)
    {
	if(Mpi2Option.getInstance().getVerboseMode() == true)
	{
	    System.out.print(VERBOSEHEAD + " host.broad " + com  +
			     " ray gen. req PE r = " + reqPErank + " time ");
	    ElapsedTimer.outHostRawTime("");
	}
    }
}
