//------------------------------------------------------------
// $Id: RecvBuffer.h,v 1.1.1.1 1999/11/25 06:26:09 yamauchi Exp $
//------------------------------------------------------------
// RecvBuffer
//    一つの方向からの一つの受信バッファを manage する．
//    SendBuffer と対になっている．
//
//    受信用バッファが空いており，レイが受信可能な場合に受信コマンドを
//    発行し，受信バッファをロックする．キューに Obj をコピーしたら解
//    放し，次の受信に備える．
//   
//	Copyright (C) 1999 YAMAUCHI Hitoshi 山内 斉
//
//   FIXME use testsrc/RecvRaysBuffer.cc for test 
//------------------------------------------------------------
#ifndef _RecvBuffer_h
#define _RecvBuffer_h 1

#include <assert.h>
#include <stdio.h>
#include "mpi2.h"
#include "req_handle_t.h"
#include "mySLQueue.h"
#include "mpi2communication.h"
#include "Statistics.h"
#include "ProfileMon.h"

//------------------------------------------------------------
// RecvBuffer : 一つの受信用バッファ
//------------------------------------------------------------
template < class T >
class RecvBuffer {
private:
    RecvBuffer(const RecvBuffer < T > & rhs);
    const RecvBuffer < T > & operator=(const RecvBuffer < T > & rhs);

protected:
    int  mBufSize;		// 受信用バッファのサイズ
    T   *mpBuff;		// Obj の受信バッファ実体
    YorN mIsLock;		// バッファが lock 中かどうか示す
    int  mRecvNum;		// 受信する ray の数
    reqHandle_st mReqHandle;	// 受信リクエストハンドル
    mySLQueue < T * > *mpObjQueue; // 受信した Obj を入れる queue への参照
    int  mSrcRank;		   // 最後に受信した時の送信元のランク

public:				
    // 統計情報
    CommunBufStatistics mCBStat;

protected:
    void freebuf(void)
    {
	if(this->mpBuff != NULL)
	{
	    delete [] this->mpBuff;
	}
	this->mpBuff  	= NULL;
    }

public:
    RecvBuffer(void)
    {
	this->mBufSize	= -1;
	this->mpBuff	= NULL;
	this->mIsLock	= NO;
	this->mRecvNum	= -1;
	// this->mReqHandle;
	this->mSrcRank	= -1;
    }
    ~RecvBuffer(void)
    {
	this->freebuf();
    }

    //------------------------------
    // initialize : 何回も呼べる
    //  @param bufferLength : 受信バッファの長さ
    //  @param pObjQueueRef : 受信した Objs へのポインタを入れるキュー
    //	    	への参照．キューの実体はそのキューからものを取り出すた
    //		めに上位に確保するのが通常  
    //------------------------------    
    void init(int bufferLength, mySLQueue < T * > *pObjQueueRef)
    {
	// delete buff
	if(this->mpBuff != NULL)
	{
	    this->freebuf();
	}
	
	// check arg
	if(bufferLength < 1)
	{
	    cout << "Error! : bufferLength::RecvBuffer::init must be > 0"
	    << endl;
	    exit(1);
	}
	
	if(pObjQueueRef == NULL)
	{
	    cout << "Error! : pObjQueueRef::RecvBuffer::init must be != NULL"
	    << endl;
	    exit(1);
	}
	
	// init
	this->mBufSize	= bufferLength;
	this->mpBuff 	= new T [this->mBufSize];
	this->mIsLock 	= NO;
	this->mRecvNum	= -1;
	// this->mReqHandle;
	this->mpObjQueue 	= pObjQueueRef;

	// init statistics 
	this->mCBStat.init();
    }
    
    //------------------------------
    // 受信中か
    //   @return 受信中ならば YES
    //------------------------------
    YorN isLocking(void) const { return(this->mIsLock);	}
    
    //------------------------------
    // 受信の完了を試みる．
    //   受信中から完了に移行した時に YES を返す
    //------------------------------
    YorN completeRecv(void)
    {
	// 受信中にしか呼ばない
	if(this->isLocking() == NO)
	{
	    return(NO);
	}
	
	// 受信完了か?
	if(commMpi2_isRecvComplete(this->mReqHandle) == YES)
	{
	    assert(this->mRecvNum > 0); // 受信する obj の数は正
	    
	    // 受信完了した．受信バッファからレイを取り出す
	    int i;
	    for(i = 0; i < this->mRecvNum; i++)
	    {
		// 受信バッファから取り出した obj を新たに new する 
		// newObj (newRay, newIntensity, ... etc.)! 
		T *pNewObj = new T (this->mpBuff[i]);
		
		// PE の procRaysQueue に enqueue する
		this->mpObjQueue->enq(pNewObj); 
	    }
	    // 受信完了のフラグをセットし、このバッファを未使用とする．
	    this->mIsLock = NO;
	    this->mRecvNum = -1;
	    
	    return(YES);
	}
	else
	{
	    return(NO);
	}
    }

    //------------------------------
    // 受信要求は発行できるか
    //  @return 受信要求を発行した
    //------------------------------
    YorN issueRecvIfItCan(void)
    {
	if(this->isLocking() == YES)
	{
	    return(NO);
	}

	int nObjsBytes;
	
	// どこからか送信されているか
	if(commMpi2_canRecv(&(this->mSrcRank), T::getTAG(), &nObjsBytes)
	   == YES) 
	{	
	    assert(nObjsBytes % sizeof( T ) == 0);
	    int nObjs = nObjsBytes / sizeof( T );

	    // 送信されている．受信要求発行
	    this->mReqHandle = 
	    	commMpi2_IrecvObj(this->mSrcRank, this->mpBuff, nObjs);
	    this->mIsLock    = YES; 	// 送信中になった lock
	    this->mRecvNum   = nObjs;	// 受信中の物体数を記録する
	    
	    // 受信バッファの利用の統計を取る
	    this->mCBStat.reportNSendRecvObjs(nObjs); 

	    // Profile Monitor
	    ProfileMon::getInstance()->recv(this->mSrcRank, 
					    nObjs, T::getTAG());
	    
	    return(YES);
	}
	else
	{
	    return(NO);
	}
    }

    //------------------------------
    // 受信バッファ長さ
    //  @return 現在の受信バッファ長 (固定長で初期化される)
    //------------------------------
    int  getFixedBufLen(void) const	{ return(this->mBufSize);	}

    //------------------------------
    // 受信中か
    //  @return 受信中ならば YES
    //------------------------------
    YorN updateRecving(void)
    {
	if(this->isLocking() == YES)
	{
	    // 受信の完了を試みる．ここで受信が完了して lock は外れる
	    // かもしれないし外れないかもしれない．
	    this->completeRecv();
	}

	// 一回目の lock は受信完了で外れたかもしれない
	if(this->isLocking() == NO)
	{
	    // ロックがかかっていなければ受信発行を試みる．ここでロッ
	    // クされるかもしれないし，されないかもしれない．
	    this->issueRecvIfItCan();
	}

	// 結局，上の 2 つを経て最終的にバッファがロックされていれば受
	// 信中である．
	return(this->isLocking()); 
    }

    //------------------------------
    // 最後に受信した時の src rank
    //------------------------------
    int getLastSrcRank(void) const 
    {
	return(this->mSrcRank);
    }

public:
    //------------------------------
    // 簡易関数 : 受信テストを行い，受信発行を試みる
    //------------------------------
    void recv(void)
    {
	// 受信終了をテスト
	this->completeRecv(); 
	// 次の受信は発行できるか
	this->issueRecvIfItCan();
    }

    // 受信したものがあるかあるいは受信中か
    YorN isRecvObj(void)
    {
	if(this->isLocking() == YES)
	{
	    return(YES);
	}
	if(this->mpObjQueue->length() > 0)
	{
	    return(YES);
	}
	return(NO);
    }
};

#endif				// _RecvBuffer_h
