/*
 * $Id: RecvBuffer.java,v 1.6.4.1 2000/06/15 20:41:23 yamauchi Exp $
 *
 *     (M\pi)^2 : SendBuffer 
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * RecvBuffer
 *    一つの方向からの一つの受信バッファを manage する．
 *    SendBuffer と対になっている．
 *
 *    受信用バッファが空いており，レイが受信可能な場合に受信コマンドを
 *    発行し，受信バッファをロックする．キューに Obj をコピーしたら解
 *    放し，次の受信に備える．
 *   
 * original source RecvBuffer.h
 *
 * 1999年8月19日(木)
 *
 * EncodedData 用に変更 1999年9月22日(水)
 */

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

import mpi.*;
import jp.ac.tohoku.is.archi.mpi2.util.*;
import jp.ac.tohoku.is.archi.mpi2.common.*;
import jp.ac.tohoku.is.archi.mpi2.sysparam.Mpi2Option;

/**
 * RecvBuffer : 一つの受信用バッファ
 */
public final class RecvBuffer {
    private int       	mBufRecvObjsPerOnce; // 一度に受信する最大 Obj 数
    private int       	mRecvObjTag; 	     // この Buf が受信する物体の TAG
    private EncodedData mEncodedRecvBuf;     // 受信バッファ実体
    private ObjectDecoder mObjDec; 	     // 受信物体の Decorder
    private boolean   	mIsLock;	     // バッファが lock 中かどうか示す
    private int      	mRecvObjNum; 	     // 受信した Object の数
    private int      	mSizeOfObj; 	     // 受信する物体の encode サイズ
    private Request   	mReqHandle;	     // 受信リクエストハンドル
    private Queue     	mObjQueueRef; 	     //受信したObjを入れるqueueへの参照
    private int       	mSrcRank;	     //最後に受信した時の送信元のランク
    private CommunBufStatistics mCBStat;     // 統計情報


    /**
     * constructor
     */
    public RecvBuffer()
    {
	this.mBufRecvObjsPerOnce	= -1;
	this.mEncodedRecvBuf		= null;
	this.mObjDec			= null;
	this.mIsLock			= false;
	this.mRecvObjNum		= -1; 
	this.mSizeOfObj			= -1;
	// this.mReqHandle;
	// this.mObjQueueRef;
	this.mSrcRank	= -1;
	this.mCBStat	= new CommunBufStatistics();
    }

    /**
     * initialize : 何回も呼べる
     *  @param maxrecvObjsParOnce : 一度に受信する物体数の最大値
     *  @param objQueueRef : 
     *		受信した Objs へのポインタを入れるキューへの参照．キュー
     *		の実体はそのキューからものを取り出すために上位に確保す
     *		るのが通常   
     *  @param recvObjTAG : 
     *		この RecvBuffer が受信する物体の識別用 TAG．この TAG 
     *		を持つ物体のみをこの Buffer は受信する．  
     *  @param objdec : 
     *		Serialize された物体をデコードするためのデコーダ
     */
    public synchronized 
    void init(int maxrecvObjesParOnce, Queue 	objQueueRef, 
	      int recvObjTAG,	       ObjectDecoder objdec)
    {
	if(maxrecvObjesParOnce < 1)
	{
	    throw new RuntimeException("Error! : bufferLength.RecvBuffer.init"+
				       "must be > 0");
	}
	
	// init
	this.mBufRecvObjsPerOnce	= maxrecvObjesParOnce;
	this.mRecvObjTag		= recvObjTAG;
	// this.mEncodedRecvBuf
	this.mObjDec			= objdec;
	this.mIsLock 			= false;
	this.mRecvObjNum		= -1;
	this.mSizeOfObj			= objdec.sizeInDouble();
	this.mReqHandle			= null;
	this.mObjQueueRef 		= objQueueRef;
	this.mSrcRank	  		= -1;
	
	// init statistics 
	this.mCBStat.init();
    }
    
    /**
     * 受信中か
     *   @return 受信中ならば true
     */
    public boolean isLocking() 
    { 
	return(this.mIsLock);	
    }
    
    /**
     * 受信の完了を試みる．
     *   受信中から完了に移行した時に true を返す
     */
    public synchronized boolean completeRecv()
    {
	// 受信中にしか呼ばない
	if(this.isLocking() == false)
	{
	    return(false);
	}
	
	// 受信完了か?
	Assert.out(this.mReqHandle != null, 
		   "Error! : no recv is issued : completeRecv");
	if((Mpi2CommFactory.getInstance()).isRecvComplete(this.mReqHandle)
	   == true)
	{
	    // 受信する obj の数は正
	    Assert.out(this.mRecvObjNum > 0, 
		       "Error! : recv objnum must be > 0. completeRecv");
	    
	    Assert.out(this.mEncodedRecvBuf != null,
		       "Error! : mEncodedRecvBuf is null");

	    // 受信完了した．受信バッファからレイを取り出す
	    Decoder dec = this.mEncodedRecvBuf.getDecoder();

	    for(int i = 0; i < this.mRecvObjNum; i++)
	    {
		// 受信バッファから取り出した obj を新たに new する 
		// newObj (newRay, newIntensity, ... etc.)! 
		// T *pNewObj = new T (this.mBuff[i]); 
		// Java の場合には既に new されているはず
		
		// PE の procRaysQueue に enqueue する
		this.mObjQueueRef.enq(dec.decode(this.mObjDec)); 
	    }

	    // 受信完了のフラグをセットし、このバッファを未使用とする．
	    this.mIsLock = false;
	    this.mRecvObjNum = -1;
	    return(true);
	}
	else
	{
	    return(false);
	}
    }

    /**
     * 受信要求は発行できるか
     *  @return 受信要求を発行した
     */
    public synchronized boolean issueRecvIfItCan(int recvTags)
    {
	if(this.isLocking() == true)
	{
	    return(false);
	}

	Mpi2Communicator comm  = Mpi2CommFactory.getInstance();
	// DataNum は MPI.BYTE か MPI.DOUBLE で数えた受信数 
	int [] srcRankNDataNums = new int[2]; 
	
	// どこからか送信されているか
	if(comm.canRecv(srcRankNDataNums, recvTags, MPI.DOUBLE)  == true) 
	{	
	    // 送信されている．受信要求発行
	    this.mSrcRank    = srcRankNDataNums[0];
	    this.mRecvObjNum = srcRankNDataNums[1] / this.mSizeOfObj;

	    if(srcRankNDataNums[1] % this.mSizeOfObj != 0)
	    {
		Mpi2Option.getInstance().
		    debugOut("DEBUG : RecvBuffer: can recv srcrank = " + 
			     this.mSrcRank + 
			     ", datnum " + srcRankNDataNums[1] + 
			     ", sizeof Obj " + this.mSizeOfObj + 
			     ", objnum " + this.mRecvObjNum    +
			     ", tag = " + recvTags);
	    }

	    Assert.out(srcRankNDataNums[1] % this.mSizeOfObj == 0,
		       "Error! : datanums % sizeof(obj) must be 0");
	    
	    this.mEncodedRecvBuf = 
		Marshal.createEncodedData(this.mObjDec, this.mRecvObjNum);

	    Assert.out(this.mEncodedRecvBuf.type() == MPI.DOUBLE,
		       "Error ! : Data Type is fixed to MPI.DOUBLE here.");
	    
	    this.mReqHandle = comm.IrecvObj(this.mSrcRank,
					    this.mEncodedRecvBuf, 
					    recvTags);

	    this.mIsLock    = true; 	// 送信中になった lock
	    
	    // 受信バッファの利用の統計を取る 
	    this.mCBStat.reportNSendRecvObjs(this.mRecvObjNum); 
	    
	    // Profile Monitor
	    ProfileMon.getInstance().recv(this.mSrcRank, 
					  this.mRecvObjNum, recvTags);

	    return(true);
	}
	else
	{
	    return(false);
	}
    }

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

    /**
     * 受信中か
     *  @return 受信中ならば true
     */
    public synchronized boolean updateRecving(int recvobjTags)
    {
	if(this.isLocking() == true)
	{
	    // 受信の完了を試みる．ここで受信が完了して lock は外れる
	    // かもしれないし外れないかもしれない．
	    this.completeRecv();
	}

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

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

    /**
     * 最後に受信した時の src rank
     */
    public int getLastSrcRank() 
    {
	return(this.mSrcRank);
    }

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

    /**
     * 受信したものがあるかあるいは受信中か (簡易関数)
     */
    public synchronized boolean isRecvObj()
    {
	if(this.isLocking() == true)
	{
	    return(true);
	}
	if(this.mObjQueueRef.length() > 0)
	{
	    return(true);
	}
	return(false);
    }

    /**
     * 統計情報の取得
     */
    public CommunBufStatistics peekCBStatistics()
    {
	return(this.mCBStat);
    }
}
