/*
 * $Id: NetIntUnit.java,v 1.15.4.1 2000/06/15 20:41:23 yamauchi Exp $
 *     (M\pi)^2 : Network Interface Unit
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source NetIntUnit.h NetIntUnit.cc
 *
 *	Network Interface Unit の実装
 * 
 * 1999年8月 9日(月)
 *
 *-- 1999年9月28日(火) 先週の金曜にバッグを自転車の前の籠に入れておいた
 *-- ら，後ろから来たバイクの 2 人組のやつにすっと持ち上げられた．とっさ
 *-- にバッグをつかんで追いかけたが，逃げられた．腹の立つことである．ひ
 *-- きずりおろして腕の一本でも折れなかったのが残念でならぬ．
 *--
 *-- 後の話だが，同じ所で犯罪を繰り返していたらしく，捕まっていた．訴え
 *-- てさらに余罪を追及させるべきだったかもしれない．社会人の名前は出て
 *-- いたが，残りの専門学校生という方の名前はなかった．
 */

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

import  java.util.*;

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

/**
 * NetIntUnit : Network Interface Unit の実装
 */
public final class NetIntUnit 
implements Mpi2Enum, Mpi2Constants 
{
    /*
     * 全 PE 数
     */
    private int mTotalPENum;		 
    /* 
     * 送信キュー長がこれを越えたら ray を送信
     */
    private int mSendRayThreshold;	 

    /* 
     * 輝度送信キュー長がこの長さを越えたら送信
     */
    private int	mSendIntensityThreshold; 

    /*
     * この NetworkInterface の自分の rank (check)
     */
    private int mThisRank;		 

    /*
     * host の rank
     */
    private int mHostRank;		 

    /*
     * 空間と PE の対応情報の実体
     */
    private Space2PE 	mSpc2PE;

    /**
     * ray の送受信用バッファ (キューイングあり): i 番目の送信用バッファ 
     * は rank == i 用 
     */
    public SendBuffer [] mSendRaysBuf; // 送信 buffer : 実行時に数を決定する
    public RecvBuffer [] mRecvRaysBuf; // 受信 buffer : 数は固定

    /**
     * ホストへの輝度送信用 buf (これは送信用が一つのみ，受信は host のみ)
     */
    public SendBuffer mSendIntensitiesBuf;

    /**
     * シェーディング時の頂点輝度送受信用バッファ
     */
    public SendBuffer [] mSendVerIntBuf; // [i] は destrank == i 用
    public RecvBuffer mRecvVerIntBuf;

    /**
     * ホストへの状態の送信用バッファ
     */
    public SendBuffer mSendPEStatBuf;

    /**
     * ホストからの命令の受信バッファ
     */
    public RecvBuffer mRecvHostCommBuf;
    
    /** 
     * constructor
     */
    public NetIntUnit()
    {
	this.mTotalPENum 		= -1;
	this.mSendRayThreshold 		= -1;
	this.mSendIntensityThreshold  	= -1;
	this.mThisRank			= -1;
	this.mHostRank			= -1;
	this.mSpc2PE 			= null;
	this.mSendRaysBuf		= null;
	this.mSendIntensitiesBuf	= null;
	this.mSendVerIntBuf		= null;
	this.mRecvVerIntBuf		= null;
	this.mSendPEStatBuf		= null;
	this.mRecvHostCommBuf		= null;
    }


    /**
     * Ray SendRecv Buffer
     * 	initialize ray buffer : 何度 call しても可
     *	@param totalPENum		全 PE 数
     *	@param raySendThreshold 	ray 数の送信閾値 
     *	@param intensitySendThreshold  	輝度情報の FB へのまとめ送り数
     *	@param thisRank			この PE の rank (実際は不要:チェック用)
     *	@param hostRank			host のランク : Pixel 輝度送信用
     *	@param procRaysQueue		PEで処理するrayを保持するキューの参照
     *	@param hostCommRaysQueue	Host からのコマンドを受けるキューの参照
     */
    public void initRayBuff(int 	totalPENum, 	  
			    int 	raySendThreshold,    
			    int 	intensitySendThreshold,
			    int 	thisRank,	  
			    int 	hostRank,	  
			    Queue 	procRaysQueue,
			    Queue	hostCommRaysQueue)
    {
	// check args
	if((totalPENum <= 0) 	     	 || (raySendThreshold <= 0) || 
	   (intensitySendThreshold <= 0) || (thisRank < 0)  	    ||
	   (thisRank >= totalPENum)	 || (hostRank < 0))
	{
	    String s = "Error! : illegal args at NetIntUnit.initRayBuff" +
		"(totalPENum, raySendThreshold, intensitySendThreshold, " + 
		"thisRank, hostRank) = (" + totalPENum + ", " + 
		raySendThreshold + ", "   + intensitySendThreshold + ", " + 
		thisRank + ", " + hostRank + ")";
	    throw new RuntimeException(s);
	}
	if((procRaysQueue == null) || (hostCommRaysQueue == null))
	{
	    throw new RuntimeException("Error! : illegal queue (null) at " +
				       "NetIntUnit.initRayBuff");
	}
	if((procRaysQueue.length() != 0) || (hostCommRaysQueue.length() != 0))
	{
	    throw new RuntimeException("Error! : illegal queue (there is " + 
				       "contents) NetIntUnit.initRayBuff");
	}
	
	// init PE num and threshold
	this.mTotalPENum 	      	= totalPENum;	
	this.mSendRayThreshold  	= raySendThreshold;
	this.mSendIntensityThreshold	= intensitySendThreshold;
	this.mThisRank 			= thisRank;
	this.mHostRank 			= hostRank;
    
	// ray 送信バッファの初期化 
	this.mSendRaysBuf = new SendBuffer[this.mTotalPENum];
	for(int peRank = 0; peRank < this.mTotalPENum; peRank++)
	{
	    this.mSendRaysBuf[peRank] = new SendBuffer();
	    this.mSendRaysBuf[peRank].initBuff(this.mSendRayThreshold,
					       Ray.getTAG());
	}

	// ray 受信バッファの初期化
	this.mRecvRaysBuf = new RecvBuffer[MAX_RECV_BUFFNUM];
	for(int i = 0; i < MAX_RECV_BUFFNUM; i++)
	{
	    // 送信のバッファと受信のバッファの領域の大きさは同じ．
	    // (この大きさ(this.mSendRayThreshold)の量のrayのやりとりがある．)
	    this.mRecvRaysBuf[i] = new RecvBuffer();
	    this.mRecvRaysBuf[i].init(this.mSendRayThreshold, procRaysQueue,
				      Ray.getTAG(), Ray.getObjectDecoder());
	}

	// host への輝度送信用バッファ
	this.mSendIntensitiesBuf = new SendBuffer();
	this.mSendIntensitiesBuf.initBuff(this.mSendIntensityThreshold,
					  Intensity.getTAG());

	// host への状態送信用バッファ : 
	this.mSendPEStatBuf	= new SendBuffer();
	this.mSendPEStatBuf.initBuff(1, // キューイングはなし
				     PEStat.getTAG());
	

	// host からの命令受信バッファ
	this.mRecvHostCommBuf	= new RecvBuffer();
	this.mRecvHostCommBuf.init(this.mSendRayThreshold, hostCommRaysQueue,
				   HostCommand.getTAG(),   
				   HostCommand.getObjectDecoder());
    }


    /**
     * initSpc2PE
     *	 空間と PE のマッピング関係の初期化
     *	child of initInternalUnits()
     *  @ref Host.java private void mapSpacePE()
     */
    // public void initSpc2PE(SysInfo sys, SpaceParam spcparam, IntVec3 PEID)
    public void initSpc2PE(int allocationScheme, IntVec3 PEdim, 
			   int subdivNum, 	 IntVec3 PEID)
    {
	switch(allocationScheme)
	{
	case Block_Alloc:
	{
	    Space2PEBlock s2p = new Space2PEBlock();
	    s2p.init(PEdim, subdivNum, PEID);
	    this.mSpc2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("PE : " + PEID + " init " + 
						"BlockAlloc");
	}
        break;
	
	case Distributed_Alloc:
	{
	    Space2PEDistributed s2p = new Space2PEDistributed();
	    s2p.init(PEdim, subdivNum, PEID);
	    this.mSpc2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("PE : " + PEID + " init " + 
						"DistributedAlloc");
	}
	break;
	
	case Skewed_Alloc:
	{
	    Space2PESkewedDistributed s2p = new Space2PESkewedDistributed();
	    s2p.init(PEdim, subdivNum, PEID);
	    this.mSpc2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("PE : " + PEID + " init " + 
						"SkewedDistributedAlloc");
	}
	break;
	
	case Random_Alloc:
	{
	    Space2PERandom s2p = new Space2PERandom();
	    s2p.init(PEdim, subdivNum, PEID);
	    this.mSpc2PE = s2p;
	    Mpi2Option.getInstance().verboseOut("PE : " + PEID + " init " + 
						"RandomAlloc");
	}
	break;
	
	default:
	    this.mSpc2PE = null;
	    throw new RuntimeException("Error! : no such allocation scheme " + 
				       "NetIntUnit.initSpc2PE");
	}
    }


    /*------------------------------
     * send
     * 	キューに入れ，その方向のキューが十分長い場合には送信
     */
    public boolean sendRays(Ray r, int destRank)
    {
	return(this.mSendRaysBuf[destRank].enqNewedObjwithMaySend(r, 
								  destRank));
    }

    /*
    // 全ての方向を見てある方向のキューが十分長い場合には送信
    boolean sendRaysIfExceedQueueLen();
    */

    /**
     * キューに 1 本でも ray が存在すれば送信発行を試み，buffer が lock さ
     * れていない限り送信要求を発行する． 
     *   @return 送信要求を発行したら true
     */
    public boolean flushSendRaysBuffer() 
    {
	// 送信
	boolean isIssueSend = false;
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    if(this.mSendRaysBuf[i].flushdone(i) == true)
	    {
		// 自分自身に送信する場合には通信路を使わないで直接送る
		Assert.out(this.mThisRank != i, 
			   "Error!: no send to myself with communication.");
		isIssueSend = true; // 送信要求を発行した
	    }
	}
	return(isIssueSend);
    }

    /*
    // 送信要求で未完のものが完了したのかチェックする
    void completeSendRaysRequest();
    */

    /**
     * Ray について送信中のものが何も無い場合に true を返す
     */
    public boolean isNoSendRay()
    {
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    // 送信中のものが存在するときには即 false
	    if(this.mSendRaysBuf[i].isLockRealBuffer() == true)
	    {
		this.mSendRaysBuf[i].flushdone(i);
		return(false);
	    }
	}
	return(true);
    }

    /**
     * 全ての送信バッファが empty なら true
     */
    public boolean isSendRayBufAllEmpty()
    {
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    if(this.mSendRaysBuf[i].getSizeOfQueue() > 0)
	    {
		return(false);
	    }
	}
	return(true);
    }

    
    /*------------------------------------------------------------
     * 受信
     *-----------------------------------------------------------*/
    /**
     * 受信コマンド発行
     */
    public boolean recvRays()
    {
	// 受信を完了させる
	// 受信中のバッファを検索し、受信終了ならその処理を行う
	for(int recvBuffNum = 0; recvBuffNum < MAX_RECV_BUFFNUM; recvBuffNum++)
	{
	    // 受信終了をテストし、終了なら受信．もし受信完了ならば 
	    // unlock されるがそれは状況による．
	    this.mRecvRaysBuf[recvBuffNum].completeRecv(); 
	}

	// 受信要求の発行
	boolean issueRecvReq = false;
	for(int i = 0; i < MAX_RECV_BUFFNUM; i++)
	{
	    if(this.mRecvRaysBuf[i].issueRecvIfItCan(Ray.getTAG()) == true)
	    {
		issueRecvReq = true;
	    }else
	    {	
		// もう受信する ray は存在しないので loop から抜ける
		break;		
	    }
	}
	return(issueRecvReq);
    }

    /**
     * Ray を受信中のバッファが 1 つでもあれば false を返す
     * 受信中のものがなければ true
     */
     public boolean isNoRecvRay()
    {
	for(int i = 0; i < MAX_RECV_BUFFNUM; i++)
	{
	    // 送信中のものが存在するときには即 false
	    if(this.mRecvRaysBuf[i].isLocking() == true)
	    {
		this.mRecvRaysBuf[i].recv(Ray.getTAG());
		return(false);	// 受信中
	    }
	}
	return(true);
    }

    /*
    int  getNRecvs();	// 受信中のバッファ数を返す
     */

    /*------------------------------------------------------------
     * Intenisity : pixel 送信のみ (host が受信)
     *-----------------------------------------------------------*/
    /**
     * キューに設定した値を越えて保持された場合，送信要求が発行可能な
     * らば発行 
     */
    public void sendIntensity(Intensity intensity)
    {
	this.mSendIntensitiesBuf.enqNewedObjwithMaySend(intensity, 
							this.mHostRank);
    }

    /**
     * flush queue
     */
    public void flushIntensitySendBuf()
    {
	this.mSendIntensitiesBuf.flushdone(this.mHostRank);
    }

    /**
     * 送信中か
     */
    public boolean isSendingIntensity()
    {
	return(!this.mSendIntensitiesBuf.isNoMoreWork());
    }
    
    /*------------------------------------------------------------
     * 輝度頂点バッファの送受信
     *-----------------------------------------------------------*/
    /**
     * initialize intensity vertex sendrecv buffer : 何度 call しても可
     *
     *    @param verIntSendThreshold 	輝度頂点数の送信閾値 
     *    @param verIntRecvQueue    	Shading 時に受信した輝度頂点のキュー
     */
    public void initVerIntBuff(int verIntSendThreshold, Queue verIntRecvQueue)
    {
	// check internal status 
	// (既にrayの計算で初期化されていなくてはいけない)
	if((this.mTotalPENum < 0) || (this.mThisRank < 0))
	{
	    throw new RuntimeException("Error! : not init yet. " + 
				       "NetIntUnit.initVerInttexBuff");
	}

	// check args 
	if(verIntSendThreshold <= 0)
	{
	    String s = "Error! : illegal args at NetIntUnit.initVerInttexBuff "
		+ "initVerInttexBuff = " + verIntSendThreshold;
	    throw new RuntimeException(s);
	}
	if((verIntRecvQueue == null) || (verIntRecvQueue.length() != 0))
	{
	    String s ="Error! : illegal queue at NetIntUnit.initVerInttexBuff";
	    throw new RuntimeException(s);	
	}

	/*
	 * 輝度頂点送信・受信バッファの初期化
	 */
	this.mSendVerIntBuf = new SendBuffer[this.mTotalPENum];
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    this.mSendVerIntBuf[i] = new SendBuffer();	    
	    this.mSendVerIntBuf[i].initBuff(verIntSendThreshold, 
					    VertexIntensity.getTAG());
	}
	this.mRecvVerIntBuf = new RecvBuffer();
	this.mRecvVerIntBuf.init(verIntSendThreshold, verIntRecvQueue,
				 VertexIntensity.getTAG(),
				 VertexIntensity.getObjectDecoder());
    }

    public void sendVerInt(VertexIntensity verint, int destrank)
    {
	Assert.out(destrank != this.mThisRank, 
		   "Error! : not use sendbuff for sending myself:sendVerInt");
	this.mSendVerIntBuf[destrank].enqNewedObjwithMaySend(verint, destrank);
    }

    public void flushVerIntSendBuff()
    {
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    this.mSendVerIntBuf[i].flushdone(i);
	}
    }

    // 送信中のものが何か存在する
    public boolean isSendingVerInt()
    {
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    // 送信中のものが存在するときには true
	    if(this.mSendVerIntBuf[i].isLockRealBuffer() == true)
	    {
		return(true);
	    }
	}
	return(false);
    }

    public void recvVerInt()
    {
	this.mRecvVerIntBuf.recv(VertexIntensity.getTAG());
    }

    // 受信中のものが何か存在する
    public boolean isRecvingVerInt()
    {
	// lock 中なら受信中
	return(this.mRecvVerIntBuf.isLocking());
    }


    /*------------------------------------------------------------
     * PE 情報をホストに送信
     *-----------------------------------------------------------*/
    /*
    void initPEStatHostCommBuff(Vector hostCommRecvQueue);
    */
    public void sendPEStatToHost(PEStat pestat)
    {
	int hostRank = SysStat.getInstance().getHostRank();
 	if(this.mSendPEStatBuf.enqNewedObjwithMaySend(pestat, hostRank)
	   == true)
	{
	    // System.out.println("SendPEStat");
	}
    }
    /**
     * 一つでも残っていれば送信発行を試みる.
     */
    public void flushPEStatSendBuff()
    {
	// 送信先は現在 Host に固定
	this.mSendPEStatBuf.flushdone(this.mHostRank);	
    }
    /**
     * PEStat の送信中のものが存在する
     */
    public boolean isSendingPEStat()
    {
	// !仕事はない == 送信中 : 仕事があるというのは SendBuffer では送信中
	return(!(this.mSendPEStatBuf.isNoMoreWork()));
    }

    /**
     * host にこの PE の状態を伝える
     *	IsThisPEBusy == true  の時にはこの PE は稼働を開始した
     *	IsThisPEBusy == false の時にはこの PE は idle になった
     */
    public void sendHostToThisPEIsBusy(boolean isThisPEBusy, int peRank)
    {
	PEStat pestat = new PEStat();
	if(isThisPEBusy == true)
	{
	    pestat.setBusy(peRank);
	}else
	{
	    pestat.setIdle(peRank);	
	}
	this.sendPEStatToHost(pestat);
    }

    /**
     * ホストからのコマンドを受信
     */
    public void recvHostComm()
    {
	this.mRecvHostCommBuf.recv(HostCommand.getTAG());
    }

    /*
    boolean isRecvingHostComm(); // 受信中のものが何か存在する
     */

    /**
     * 統計情報の出力
     *	stage を skip した場合には統計情報はもともとないので出力しないこと 
     */
    public void printStat(String mes, String stageName)
    {
	// radiosity の平均レイ処理数 FIXME これは必要かな?
	// System.out.println(pMes + "stat procradios: " + 
	// 			   this.mNumOfProcRadiosities);

	/*
	 * 送信 buffer の統計
	 * 最大キュー長の出力 : 各 rank について全てと最後に最大長の平均長
	 */
	System.out.print(mes + stageName + " SendBuff.MaxQueueLength ");
	int maxqueuetotal = 0; 

	for(int i = 0; i < this.mTotalPENum; i++)	
	{
	    CommunBufStatistics sendstat = 
		this.mSendRaysBuf[i].peekCBStatistics();
	    System.out.print(sendstat.getMaxQueueLen() + " ");
	    maxqueuetotal += sendstat.getMaxQueueLen();
	}
	double ave = ((double)maxqueuetotal) / ((double)this.mTotalPENum);
	System.out.println(" : ave " + ave);
						    
	// 一回あたりの送信でいくつの obj が送信されたか
	System.out.print(mes + stageName + " SendBuff.NumberOfObjParSend ");
	double opstotal = 0.0;
	for(int i = 0; i < this.mTotalPENum; i++)
	{
	    CommunBufStatistics sendstat = 
		this.mSendRaysBuf[i].peekCBStatistics();

	    double n_objsend = sendstat.getNObjParSend();
	    opstotal += n_objsend;
	    
	    if(n_objsend == 0.0)
	    {
		System.out.print("(NotUsed) "); // 利用されなかった
	    }else
	    {
		System.out.print(n_objsend + " ");
	    }
	}
	System.out.println(" : ave " + 
			   (((double)opstotal) / ((double)this.mTotalPENum)));
	
	/*
	 * 受信 buffer の統計
	 * 一回あたりの受信でいくつの obj が送信されたか
	 * しかし，MPI は送受信を保証するのだからこちらは不要か FIXME
	 */
	System.out.print(mes + stageName + " RecvBuff.NumberOfObjParSend ");
	opstotal = 0.0;
	for(int i = 0; i < MAX_RECV_BUFFNUM; i++)
	{
	    double n_objrecv = 
		this.mRecvRaysBuf[i].peekCBStatistics().getNObjParSend();
	    opstotal += n_objrecv;
	    
	    if(n_objrecv == 0.0)
	    {
		System.out.print("(NotUsed) "); // 利用されなかった
	    }else
	    {
		System.out.print(n_objrecv + " ");
	    }
	}
	System.out.println(" : ave " + 
			   (((double)opstotal) / ((double)MAX_RECV_BUFFNUM)));
    }

    /**
     * getPEID from spcpos
     */ 
    public IntVec3 getNewPEIDFromSpcPos(IntVec3 spcpos)
    {
	return(this.mSpc2PE.getNewPEIDfromSpcPos(spcpos));
    }
}

