//------------------------------------------------------------
// $Id: NetIntUnit.cc,v 1.2.2.1 2000/06/14 15:00:10 yamauchi Exp $
//------------------------------------------------------------
//	netInt.cc Network Interface Unit implementation
//	These functions are member of PEClass.
//	1997/1/21
//	Copyright (C) 1997--1998 YAMAUCHI Hitoshi
//
//  volume rendering 対応時に Network Interface Unit の PE 内部に直接
//  書いていた部分を分離した 
//  いくつかのメンバは PEClass のメンバである
//------------------------------------------------------------
#include "mpi2communication.h"
#include "mpi2exit.h"
#include "NetIntUnit.h"

//------------------------------------------------------------
// public functions
//------------------------------------------------------------
// constructors
//------------------------------
NetIntClass::
NetIntClass(void)
{
    this->mTotalPENum 			= -1;
    this->mSendRayThreshold 		= -1;
    this->mSendIntensityThreshold  	= -1;
    this->mThisRank			= -1;
    this->mHostRank			= -1;
    this->mpSpc2PE 			= NULL;
    this->mpSendRaysBuf			= NULL;
    this->mpSendVerIntBuf		= NULL;
}

NetIntClass::
~NetIntClass(void)
{
    if(this->mpSpc2PE != NULL)
    {
	delete this->mpSpc2PE;
	delete [] this->mpSendRaysBuf;

    }
    if(this->mpSendVerIntBuf != NULL)
    {
	delete [] this->mpSendVerIntBuf;
    }
}


//------------------------------------------------------------
// initialize network interface 
//------------------------------
// initialize : 何度 call しても可
//    @param totalPENum	      	     全 PE 数
//    @param raySendThreshold 	     ray 数の送信閾値 
//    @param intensitySendThreshold  輝度情報の FB へのまとめ送り数
//    @param thisRank		     この PE のランク(実際は不要:チェック用)
//    @param hostRank		     host のランク : Pixel 輝度送信用
//    @param pProcRaysQueue	     PEで処理するrayを保持するキューの参照
//------------------------------
void NetIntClass::
initRayBuff(int totalPENum, int raySendThreshold, int intensitySendThreshold,
	    int thisRank,   int hostRank,
	    mySLQueue < Ray * > *pProcRaysQueue)
{
    // check args
    if((totalPENum <= 0) 	     ||	(raySendThreshold <= 0) || 
       (intensitySendThreshold <= 0) ||	(thisRank < 0)  	||
       (thisRank >= totalPENum)	     || (hostRank < 0))
    {
	cout << "Error! : illegal args at NetIntClass::initRayBuff" << endl;
	cout << "(totalPENum, raySendThreshold, intensitySendThreshold, "
	     << "thisRank, hostRank) = (" 
	     << totalPENum << ", " << raySendThreshold << ", "
	     << intensitySendThreshold << ", " << thisRank << ", " << 
	     hostRank << ")" << endl;
	commMpi2_exit(1);
    }
    if((pProcRaysQueue == NULL) || (pProcRaysQueue->length() != 0))
    {
	cout << "Error! : illegal queue at NetIntClass::initRayBuff" << endl;
	commMpi2_exit(1);
    }
    
    // init PE num and threshold
    this->mTotalPENum 	      		= totalPENum;	
    this->mSendRayThreshold  		= raySendThreshold;
    this->mSendIntensityThreshold	= intensitySendThreshold;
    this->mThisRank 			= thisRank;
    this->mHostRank 			= hostRank;
    
    // ray 送信バッファの解放，初期化 
    if(this->mpSendRaysBuf != NULL)
    {
	delete [] this->mpSendRaysBuf; // 既に存在していた場合には解放初期化
	this->mpSendRaysBuf = NULL;
    }
    
    this->mpSendRaysBuf = new SendBuffer < Ray > [this->mTotalPENum];
    for(int peRank = 0; peRank < this->mTotalPENum; peRank++)
    {
	this->mpSendRaysBuf[peRank].initBuff(this->mSendRayThreshold);
    }

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

//------------------------------------------------------------
// 送信関係
//------------------------------------------------------------
// 送信コマンド発行
// ある方向について．ある方向のキューに object を入れ，キューが設定し
// た長さを越えたら送信要求の発行を試みる．
//   @return 送信要求を発行したら YES
//------------------------------
YorN NetIntClass::
sendRays(Ray *pRay, int destRank)
{
    assert((0 <= destRank) && (destRank < this->mTotalPENum));
    assert(pRay != NULL);
    
    return(this->mpSendRaysBuf[destRank].
	   	enqNewedObjwithMaySend(pRay, destRank));
}


//------------------------------
// キューに 1 本でも ray が存在すれば送信発行を試み，buffer が lock さ
// れていない限り送信要求を発行する． 
//   @return 送信要求を発行したら YES
//------------------------------
YorN NetIntClass::
flushSendRaysBuff(void)
{
    // 送信
    YorN isIssueSend = NO;
    for(int i = 0; i < this->mTotalPENum; i++)
    {
	if(this->mpSendRaysBuf[i].flushdone(i) == YES)
	{
	    assert(this->mThisRank != i);
	    isIssueSend = YES; // 送信要求を発行した
	}
    }
    return(isIssueSend);
}

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

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

//------------------------------
// 全ての送信バッファが空の場合に YES を返す
//------------------------------
YorN NetIntClass::
isSendRayBufAllEmpty(void) const
{
    for(int i = 0; i < this->mTotalPENum; i++)
    {
 	if(this->mpSendRaysBuf[i].getSizeOfQueue() > 0)
	{
 	    return(NO);
 	}
    }
    return(YES);
}

//------------------------------
// 送信中のものが何も無い場合に YES を返す
// Ray について
//------------------------------
YorN NetIntClass::
isNoSendRay(void) 
{
    for(int i = 0; i < this->mTotalPENum; i++)
    {
 	// 送信中のものが存在するときには即 NO
 	if(this->mpSendRaysBuf[i].isLockRealBuffer() == YES)
 	{
	    this->mpSendRaysBuf[i].flushdone(i);
 	    return(NO);
 	}
    }
    return(YES);
}
    
//------------------------------------------------------------
// receive rays
//------------------------------------------------------------
// 受信中のバッファが 1 つでもあれば NO を返す
// Ray について
//------------------------------
YorN NetIntClass::
isNoRecvRay(void) 
{
    for(int i = 0; i < MAX_RECV_BUFFNUM; i++)
    {
 	// 送信中のものが存在するときには即 NO
 	if(this->mRecvRaysBuf[i].isLocking() == YES)
	{
	    this->mRecvRaysBuf[i].recv();
 	    return(NO);		// 受信中
 	}
    }
    return(YES);
}

//------------------------------
// 受信中のバッファ数
//------------------------------
int NetIntClass::
getNRecvs(void)
{
    int n = 0;
    for(int i = 0; i < MAX_RECV_BUFFNUM; i++)
    {
	if(this->mRecvRaysBuf[i].isLocking() == YES)
	{
	    n++;
	}
    }
    return(n);
}

//------------------------------------------------------------
// 輝度 (Pixel 情報の送信
//------------------------------------------------------------
// キューに設定した値を越えて保持された場合，送信要求が発行可能ならば発行
void NetIntClass::
sendIntensity(IntensityClass *pIntensity)
{
    this->mSendIntensitiesBuf.
    	enqNewedObjwithMaySend(pIntensity, this->mHostRank);
}

//------------------------------
// flush queue of intensity
//------------------------------
void NetIntClass::
flushIntensitySendBuf(void)
{
    this->mSendIntensitiesBuf.flushdone(this->mHostRank);
}

//------------------------------
// まだ輝度は送信中か
//------------------------------
YorN NetIntClass::
isSendingIntensity(void)
{
    if(this->mSendIntensitiesBuf.isNoMoreWork() == YES)
    {
	return(NO);		
    }else
    {
	return(YES);
    }
}


//------------------------------------------------------------
// 輝度頂点バッファの送受信
//------------------------------------------------------------
// initialize intensity vertex sendrecv buffer :  何度 call しても可
//    @param VIntensitySendThreshold 輝度頂点数の送信閾値 
//    @param pVIntensityRecvQueue    Shading 時に受信した輝度頂点のキュー
//------------------------------
void NetIntClass::
initVerIntBuff(int verIntSendThreshold, 
	       mySLQueue < VertexIntensity * > *pVerIntRecvQueue)
{
    // check internal status (既に ray の計算で初期化されていなくてはいけない)
    if((this->mTotalPENum < 0) || (this->mThisRank < 0))
    {
	cout << "Error! : not init yet. NetIntClass::initVerInttexBuff"<< endl;
	commMpi2_exit(1);
    }

    // check args 
    if(verIntSendThreshold <= 0)
    {
	cout << "Error! : illegal args at NetIntClass::initVerInttexBuff " 
	"initVerInttexBuff = " << verIntSendThreshold << endl;
	commMpi2_exit(1);
    }
    if((pVerIntRecvQueue == NULL) || (pVerIntRecvQueue->length() != 0))
    {
	cout << "Error! : illegal queue at NetIntClass::initVerInttexBuff"
		<< endl;
	commMpi2_exit(1);
    }

    // 輝度頂点送信・受信バッファの初期化
    if(this->mpSendVerIntBuf != NULL)
    {
	// shading のステージは現在一回しかないので，現在は解放はないはず．
	cout << "Warning: never reach : NetIntClass::initVerIntBuff" << endl;
	delete [] this->mpSendVerIntBuf; // 既に存在していた場合には解放初期化
	this->mpSendVerIntBuf = NULL;
    }
    this->mpSendVerIntBuf = 
    	new SendBuffer < VertexIntensity > [this->mTotalPENum];
    for(int i = 0; i < this->mTotalPENum; i++)
    {
	this->mpSendVerIntBuf[i].initBuff(verIntSendThreshold);
    }
    this->mRecvVerIntBuf.init(verIntSendThreshold, pVerIntRecvQueue);
}

//------------------------------
// 送信 : まとめ送信
//------------------------------
void NetIntClass::
sendVerInt(VertexIntensity *pVerInt, int destrank)
{
    assert(destrank != this->mThisRank);
    assert((0 <= destrank) && (destrank < this->mTotalPENum));

    this->mpSendVerIntBuf[destrank].enqNewedObjwithMaySend(pVerInt, destrank);
}


//------------------------------
// 一つでも残っていれば送信発行を試みる
//------------------------------
void NetIntClass::
flushVerIntSendBuff(void)
{
    for(int i = 0; i < this->mTotalPENum; i++)
    {
	this->mpSendVerIntBuf[i].flushdone(i);
    }
}

// 送信中のものが何か存在する
YorN NetIntClass::
isSendingVerInt(void)
{
    for(int i = 0; i < this->mTotalPENum; i++)
    {
	if(this->mpSendVerIntBuf[i].isNoMoreWork() == NO)
	{
	    // まだ送信の仕事が存在する
	    return(YES);
	}
    }
    return(NO);
}


// 受信
void NetIntClass::
recvVerInt(void)
{
    this->mRecvVerIntBuf.recv(); 
}

YorN NetIntClass::
isRecvingVerInt(void)
{
    return(this->mRecvVerIntBuf.isLocking());
}


//------------------------------------------------------------
// PEStat のホストへの送信
//------------------------------------------------------------
// initialize PEStat sendrecv buffer :  何度 call しても可
//    @param pPEStatRecvQueue  他 PE (= 現在 Host)から受信したコマンドのキュー
//------------------------------
void NetIntClass::
initPEStatHostCommBuff(mySLQueue < HostCommClass * > *pHostCommRecvQueue)
{
    // check args 
    if((pHostCommRecvQueue == NULL) || (pHostCommRecvQueue->length() != 0))
    {
	cout << "Error! : illegal queue at NetIntClass::initPEStatRecvBuff"
		<< endl;
	commMpi2_exit(1);
    }

    // PEStat, HostComm バッファの初期化
    // 現在は一つでもあれば送信する．
    // ここで期待するのは送受信が Nonblocking ということのみでまとめ送
    // りの効果はない．
    const int PEStatSendThreshold = 1; 
    this->mSendPEStatBuf.initBuff(PEStatSendThreshold);

    const int HostCommRecvBuffLen = 1; 
    this->mRecvHostCommBuf.init(HostCommRecvBuffLen, pHostCommRecvQueue);
}

//------------------------------
// 送信
//------------------------------
void NetIntClass::
sendPEStat(PEStatClass *pPEStat)
{
    const int destRank = this->mHostRank; // 送信先は現在 Host に固定

    assert(destRank != this->mThisRank);
    assert((0 <= destRank) && (destRank < this->mTotalPENum));

    this->mSendPEStatBuf.enqNewedObjwithMaySend(pPEStat, destRank);
}


//------------------------------
// 一つでも残っていれば送信発行を試みる
//------------------------------
void NetIntClass::
flushPEStatSendBuff(void)
{
    const int destRank = this->mHostRank; // 送信先は現在 Host に固定
     
    this->mSendPEStatBuf.flushdone(destRank);
}

// 送信中のものが何か存在する
YorN NetIntClass::
isSendingPEStat(void)
{
    if(this->mSendPEStatBuf.isNoMoreWork() == YES)
    {
	// 仕事はないので送信中ではない
	return(NO);
    }
    else
    {
	return(YES);
    }
}


// HostComm 受信
void NetIntClass::
recvHostComm(void)
{
    this->mRecvHostCommBuf.recv(); 
}

//------------------------------------------------------------
// 統計情報の出力
//------------------------------------------------------------
void NetIntClass::
printStat(const char *pMes, const char *pStageName)
{
    //     // radiosity の平均レイ処理数 FIXME これは必要かな?
    //     cout << pMes << "stat procradios: " << this->mNumOfProcRadiosities;

    //------------------------------
    // 送信 buffer の統計
    //------------------------------
    // 最大キュー長の出力 : 各 rank について全てと最後に最大長の平均長
    cout << pMes << pStageName << " SendBuff::MaxQueueLength ";
    int maxqueuetotal = 0; 
    int i;
    for(i = 0; i < this->mTotalPENum; i++)
    {
	cout << this->mpSendRaysBuf[i].mCBStat.getMaxQueueLen() << " ";
	maxqueuetotal += this->mpSendRaysBuf[i].mCBStat.getMaxQueueLen();
    }
    cout << " : ave " << 
	(((double)maxqueuetotal) / ((double)this->mTotalPENum)) << endl;

    // 一回あたりの送信でいくつの obj が送信されたか
    cout << pMes << pStageName << " SendBuff::NumberOfObjParSend ";
    double opstotal = 0.0;
    for(i = 0; i < this->mTotalPENum; i++)
    {
	double n_objsend = this->mpSendRaysBuf[i].mCBStat.getNObjParSend();
	opstotal += n_objsend;

	if(n_objsend == 0.0)
	{
	    cout << "(NotUsed) "; // 利用されなかった
	}else
	{
	    cout << n_objsend << " ";
	}
    }
    cout << " : ave " << 
    	(((double)opstotal) / ((double)this->mTotalPENum)) << endl;

    //------------------------------
    // 受信 buffer の統計
    //------------------------------
    // 一回あたりの受信でいくつの obj が送信されたか
    // しかし，MPI は送受信を保証するのだからこちらは不要か FIXME
    cout << pMes << pStageName << " RecvBuff::NumberOfObjParSend ";
    opstotal = 0.0;
    for(i = 0; i < MAX_RECV_BUFFNUM; i++)
    {
	double n_objrecv = this->mRecvRaysBuf[i].mCBStat.getNObjParSend();
	opstotal += n_objrecv;

	if(n_objrecv == 0.0)
	{
	    cout << "(NotUsed) "; // 利用されなかった
	}else
	{
	    cout << n_objrecv << " ";
	}
    }
    cout << " : ave " << 
    	(((double)opstotal) / ((double)MAX_RECV_BUFFNUM)) << endl;
}
