/*
 * $Id: Mpi2CommunicatorMpiJavaS1.java,v 1.6.4.1 2000/06/15 20:41:23 yamauchi Exp $
 *     (M\pi)^2 : Interface of Mpi2 Communicator : 
 *		MPI version implementation 
 * 		Using mpiJava
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *
 * original source mpi2communicationMPI.cc
 *
 * MPI による並列版の実装 
 * Object の Serialize は Java の Serialize に任せる方式 
 *	mpiJava + Java Serializer
 *
 * 1999年9月13日(月)
 */

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

/*
 * mpiJava
 */
import  mpi.*;

/*
 * 
 */
import  java.util.*;
import  java.io.*;

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.host.*;

/**
 * (M\pi)^2 が用いる通信関数群の interface
 */
public final class Mpi2CommunicatorMpiJavaS1
implements Mpi2Communicator, Mpi2Enum 
{
    private static final int OFFSET = 0;

    /**
     * get name : どんな種類の通信方式か
     *	Parallel Version (Parallel | OnePE)
     */
    public String getName()
    {
	return("Parallel");
    }

    /**
     * initialize commun (M\pi)^2
     */
    public synchronized String [] init(String [] args)
    {
	try{
	    String [] retargs = MPI.Init(args);
	    int myrank = MPI.COMM_WORLD.Rank();
	    int size   = MPI.COMM_WORLD.Size();
	    int hostrank = size - 1;
	
	    (SysStat.getInstance()).setCommSize(size);
	    (SysStat.getInstance()).setMyRank(myrank);

	    // host のランクは一番最後のものにする 
	    (SysStat.getInstance()).setHostRank(hostrank);

	    return(retargs);
	}catch(MPIException e)
	{
	    System.out.println(e);
	    return(args);
	}
    }
    
    public synchronized void finalizecommun() // finalize method はよくあるので
    {
	try{
	    MPI.Finalize();
	}
	catch(MPIException e)
	{
	    System.out.println(e);
	}
    }
    public synchronized void barrier()
    {
	try{
	    MPI.COMM_WORLD.Barrier(); 
	}
	catch(MPIException e)
	{
	    System.out.println(e);
	}
    }
    public synchronized void exit(int err_num)
    {
	try{
	    MPI.COMM_WORLD.Abort(err_num); 
	}
	catch(MPIException e)
	{
	    System.out.println(e);
	}
    }

    /**
     * get type
     */
    public int getCommunicatorType()
    {
	return(Mpi2Enum.MPI2COMM_mpiJavaS1);
    }

    /**
     * get identification
     */
    public String getDescription()
    {
	return("Mpi2CommunicatorMpiJavaS1 : MPI Parallel version using " + 
	       "mpiJava + java.io.Serialize");
    }


    /**
     * Sysinfo, ViewData, SpaceParam を serialize したサイズ
     */
    private int getSVSSize()
    {
	return(1523);
    }
    /**
     * sysinfo (Broadcast)
     *  PE 用では object, material, sysinfo の receive はいらない。
     *  send と同時に receive する
     */
    public synchronized void 
    broadSysinfo(SysInfo [] sys, ViewData [] view, SpaceParam [] spcparam)
    {
	int root = SysStat.getInstance().getHostRank();
	//     cout << "***** debug :: Broad casting sysinfo .. root rank  = " 
	// 	<< root << endl;
	Assert.out((root >= 0) && 
		   (root < SysStat.getInstance().getCommSize()),
		   "Error! : Mpi2CommunicatorMPI : root rank is illegal.");
	
	if(root == SysStat.getInstance().getMyRank())
	{
	    // 送信側
	    try {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos     = new ObjectOutputStream(baos);
		oos.writeObject(sys[0]);
		oos.writeObject(view[0]);
		oos.writeObject(spcparam[0]);
		oos.flush();
		byte [] serializedobj = baos.toByteArray();
		oos.close();

		// 続いて本体を送信
		MPI.COMM_WORLD.Bcast(serializedobj, OFFSET, 
				     serializedobj.length, MPI.BYTE, root);

		Assert.out(serializedobj.length == getSVSSize(),
			   "Error! : SysInfo, ViewData, SpaceParam size " + 
			   "is changed to " + serializedobj.length);
	    }catch(Exception e)
	    {
		System.out.println("Exception : broadSysinfo send. " + e);
	    }
	}else
	{
	    // 受信側
	    try {
		byte [] buf = new byte [getSVSSize()];
		MPI.COMM_WORLD.Bcast(buf, OFFSET, getSVSSize(), MPI.BYTE,root);
		ByteArrayInputStream bais = new ByteArrayInputStream(buf);
		ObjectInputStream ois     = new ObjectInputStream(bais);
		sys[0] 		= (SysInfo)ois.readObject();
		view[0] 	= (ViewData)ois.readObject();
		spcparam[0] 	= (SpaceParam)ois.readObject();
		ois.close();
		
	    }catch(Exception e)
	    {
		System.out.println("Exception : broadSysinfo recv. " + e);
	    }
	}
    }

    /**
     * 各空間に物体が存在するかの表 : これは固定長ではない
     */
    public synchronized 
    void broadObjectInSpaceMap(Array3D [] objectInSpaceMap)
    {
	int root = SysStat.getInstance().getHostRank();
	Assert.out((root >= 0) && 
		   (root < SysStat.getInstance().getCommSize()),
		   "Error! : Mpi2CommunicatorMPI : root rank is illegal.");
	
	if(root == SysStat.getInstance().getMyRank())
	{
	    // 送信側
	    try {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos     = new ObjectOutputStream(baos);
		oos.writeObject(objectInSpaceMap[0]);
		oos.flush();
		byte [] serializedobj = baos.toByteArray();
		oos.close();

		// まずは serialize した後のサイズを送信
		int [] size = new int[1];
		size[0] = serializedobj.length;
		MPI.COMM_WORLD.Bcast(size, OFFSET, 1, MPI.INT, root);
		
		// 続いて本体を送信
		MPI.COMM_WORLD.Bcast(serializedobj, OFFSET, 
				     serializedobj.length, MPI.BYTE, root);
	    }catch(Exception e)
	    {
		System.out.println("Exception : broadSysinfo send. " + e);
	    }
	}else
	{
	    // 受信側
	    try {
		// まずは受信サイズを受信
		int [] recv_size = new int[1];
		MPI.COMM_WORLD.Bcast(recv_size, OFFSET, 1, MPI.INT, root);

		// 次に本体を受信
		byte [] serializedobj = new byte [recv_size[0]];
		MPI.COMM_WORLD.Bcast(serializedobj, OFFSET, recv_size[0],
				     MPI.BYTE, root);
		ByteArrayInputStream bais = 
		    new ByteArrayInputStream(serializedobj);
		ObjectInputStream ois = new ObjectInputStream(bais);
		objectInSpaceMap[0]   = (Array3D)ois.readObject();
		ois.close();		
	    }catch(Exception e)
	    {
		System.out.println("Exception : broadSysinfo recv. " + e);
	    }
	}
    }

    /**
     * 物体からの距離の表
     */
    // public void broadPRCSkipMap(int bytes, IMPLEMENT_TYPE pDatArray)

    /**
     * send, recv objects
     */
    /**
     * send objects
     * 	@param destRank 	物体プリミティブ送付先の rank
     * 	@param objprim 		送付する物体プリミティブ
     */
    public synchronized 
    void sendObject(int destRank, ObjectPrimitive objprim)
    {
	try {
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    ObjectOutputStream oos     = new ObjectOutputStream(baos);
	    oos.writeObject(objprim);
	    oos.flush();
	    byte [] serializedobj = baos.toByteArray();
	    oos.close();

	    int tag = TAG_NULL_OBJ;
	    switch(objprim.who())
	    {
	    case ObjectPrimitive.OBJ_TRIANGLE:
		tag = TriangleObject.getTAG();
		break;
	    case ObjectPrimitive.OBJ_NULL:
		tag = NullObject.getTAG();
		break;
	    case ObjectPrimitive.OBJ_SPHERE:
		// NIN tag = SphereObject.getTAG();
	    default:
		String s = "Error! : send object has illegal tag. dest = "
		    + destRank;
		throw new RuntimeException(s);
	    }	

	    // size 送信 : material のサイズが変化するのでサイズを送る
	    int [] size = new int[1];
	    size[0] = serializedobj.length;

	    Request reqSize = MPI.COMM_WORLD.Isend(size, OFFSET, 1, MPI.INT, 
						   destRank, TAG_INTEGER);
	    reqSize.Wait();

	    // 本体送信
	    Request reqObj = MPI.COMM_WORLD.Isend(serializedobj, OFFSET, 
						  serializedobj.length,
						  MPI.BYTE, destRank, tag);
	    reqObj.Wait();
	}catch(Exception e)
	{
	    System.out.println("Exception : sendObject send. " + e);
	}
    }	
    
    /**
     * recv objects
     * 	@param destRank 	物体プリミティブ送付元の rank の入る長さ 1 配列
     * 	@param objprim 		受信した物体プリミティブの配列
     */
    // private final static int TRIANGLE_OBJSIZE 	= 100;
    // private final static int NULL_OBJSIZE	= 200;
    // private boolean mIsFirstProbe = true;

    public synchronized void 
    recvObject(int [] srcRank,  ObjectPrimitive [] objprim)
    {
	final int hostrank = (SysStat.getInstance()).getHostRank();

	try{
	    /*
	     * recv size and allocate buffer
	     */
	    Status probeStat = null;
	    while(probeStat == null)
	    {
		probeStat = MPI.COMM_WORLD.Iprobe(MPI.ANY_SOURCE, TAG_INTEGER);
		if(probeStat == null)
		{
		    Mpi2System.pollingSleep();
		}
		/*
		  if(this.mIsFirstProbe == true)
		  {
		  // mpiJava が最初の probe に失敗するので，最初は skip する
		  probeStat = null;
		  this.mIsFirstProbe = false;
		  }
		  */
	    }
	    int [] size = new int[1];
	    Request reqSize = MPI.COMM_WORLD.Irecv(size, OFFSET, 1, MPI.INT,
						   hostrank, TAG_INTEGER);
	    reqSize.Wait();

	    /*
	      switch(probeStat.tag)
	      {
	      case TAG_TRIANGLE_OBJ:
	      size = TRIANGLE_OBJSIZE;
	      break;
	      case TAG_SPHERE_OBJ:
	      // size = SPHERE_OBJSIZE;
	      break;
	      
	      case TAG_NULL_OBJ:
	      size = NULL_OBJSIZE;
	      break;
	      
	      default:
	      String s = "Error! : recv object gets illegal tag. tag = "
	      + probeStat.tag + " " + probeStat.source;
	      throw new RuntimeException(s);
	      }	
	      */
	    
	    byte [] buf = new byte[size[0]];
	    /*
	     * get tag
	     */
	    probeStat = null;
	    while(probeStat == null)
	    {
		probeStat = MPI.COMM_WORLD.Iprobe(hostrank, MPI.ANY_TAG);
		if(probeStat == null)
		{
		    Mpi2System.pollingSleep();
		}
	    }
	    
	    Request reqObj = MPI.COMM_WORLD.Irecv(buf, OFFSET, size[0], 
						  MPI.BYTE, 
						  hostrank, probeStat.tag);
	    reqObj.Wait();
	    
	    ByteArrayInputStream bais = new ByteArrayInputStream(buf);
	    ObjectInputStream ois     = new ObjectInputStream(bais);
	    /*
	     * now I get message
	     */
	    switch(probeStat.tag)
	    {
	    case TAG_TRIANGLE_OBJ:
		// System.out.print("t");
		objprim[0] = (TriangleObject)ois.readObject();
		break;
	    case TAG_NULL_OBJ:
		objprim[0] = new NullObject();
		// System.out.print("n");
		break;
	    case TAG_SPHERE_OBJ:
		// objprim[0] = (SphereObject)ois.readObject();
		// System.out.print("s");
	    default:
		String s = "Error! : recv object has illegal tag. src = "
		    + probeStat.source + ", tag = " + probeStat.tag;
		throw new RuntimeException(s);
	    }
	    ois.close();
	}catch(MPIException e)
	{
	    System.out.println(e);
	}catch(Exception e)
	{
	    System.out.println("Exception : recvObject recv. " + e);
	    e.printStackTrace();
	}
    }


    /**
     * Isend objects
     *	non blocking send
     *	
     *	@param destRank	送信先
     *	@param buff	送信する物体の配列
     *	@param sendnum  配列は固定にとられることが多い．そのため，その
     *			配列のいくつまでが送信すべきものかを示しておく．
     *  @param sendTag	送信したもののタグ
     */
    public synchronized Request 
    IsendObj(int destRank, EncodedData lockedSendBuf, int sendObjTag)
    {
	try {
	    Request reqID = 
		MPI.COMM_WORLD.Isend(lockedSendBuf.getArray(), OFFSET, 
				     lockedSendBuf.length(), 
				     lockedSendBuf.type(),     destRank,
				     sendObjTag);
	    return(reqID);
	}catch(MPIException e)
	{
	    System.out.println(e);	    
	    return(null);
	}
    }
	
    /**
     * Irecv objects
     *	non blocking recv
     *	
     *	@param srcRank	  送信元
     *	@param buff	  送信する物体の配列
     *	@param recvNums[0]受信した物体数
     *	@param recvTags   受信する物体の識別子(タグ)
     */
    public synchronized Request 
    IrecvObj(int srcRank, EncodedData encodedBuf, int recvTags)
    {
	try {
	    Request reqID = 
		MPI.COMM_WORLD.Irecv(encodedBuf.getArray(), OFFSET, 
				     encodedBuf.length(),   encodedBuf.type(),
				     srcRank, recvTags);
	    return(reqID);
	}catch(MPIException e)
	{
	    System.out.println(e);	    
	    return(null);
	}
    }
    
    /*
     * send complete? 
     * (waiting completion of non blocking send)
     */
    public synchronized boolean isSendComplete(Request req_handle)
    {
	try {
	    Status stat = req_handle.Test();
	    if(stat != null)
	    {
		return(true);
	    }else
	    {
		return(false);
	    }
	}catch(MPIException e)
	{
	    System.out.println(e);	    
	    return(false);	    
	}
    }

    /*
     * recv complete? 
     * (waiting completion of non blocking recv)
     */
    public synchronized boolean isRecvComplete(Request req_handle)
    {
	try {
	    Status stat = req_handle.Test();
	    if(stat != null)
	    {
		return(true);
	    }else
	    {
		return(false);
	    }
	}catch(MPIException e)
	{
	    System.out.println(e);	    
	    return(false);
	}
    }

    /**
     * send, recv volume (やがて object として統一すべきか?)
     *	しかし、固定長のものに serialize はしたくないので固定長のもの
     *	はそのまま、serialize すべきものは serialize して統一的に送信
     *	しよう  
     *------------------------------------------------------------
     * send subvolume
     */
    // public void commMpi2_sendVolume(int dstRank, VolumeClass *pVol);

    /**
     * recv subvolume
     */
    // public void commMpi2_recvVolume(int srcRank, int dstRank, 
    // 				       VolumeClass **ppVol);

    /**
     * material
     */
    public synchronized 
    void broadMaterial(Material [] aMaterial)
    {
	final int root = SysStat.getInstance().getHostRank();
	if(root == SysStat.getInstance().getMyRank())
	{
	    // 送信側
	    try {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos     = new ObjectOutputStream(baos);
		oos.writeObject(aMaterial[0]);
		oos.flush();
		byte [] serializedobj = baos.toByteArray();
		oos.close();

		// まずは serialize した後のサイズを送信
		int [] size = new int[1];
		size[0] = serializedobj.length;
		MPI.COMM_WORLD.Bcast(size, OFFSET, 1, MPI.INT, root);
		
		// 次に本体を送信
 		MPI.COMM_WORLD.Bcast(serializedobj, OFFSET, 
 				     serializedobj.length, MPI.BYTE, root);
	    }catch(Exception e)
	    {
		System.out.println("Exception : broadMaterial send. " + e);
	    }
	}else
	{
	    // 受信側
	    try {
		// まずは受信サイズを受信
		int [] recv_size = new int[1];
		MPI.COMM_WORLD.Bcast(recv_size, OFFSET, 1, MPI.INT, root);
		
 		byte [] serializedobj = new byte[(recv_size[0])];
  		MPI.COMM_WORLD.Bcast(serializedobj, OFFSET, recv_size[0],
  				     MPI.BYTE, root);
 		ByteArrayInputStream bais = 
 		    new ByteArrayInputStream(serializedobj);
 		ObjectInputStream ois = new ObjectInputStream(bais);
 		aMaterial[0] = (Material)ois.readObject();
		bais.close();
	    }catch(Exception e)
	    {
		System.out.println("Exception : broadMaterial recv. " + e);
	    }
	}
    }

    /*------------------------------------------------------------
     * probe 
     *-----------------------------------------------------------*/
    /**
     * can recv 
     * 	@param srcRank_and_recvDatNum[] : 返り値
     *		srcRank_and_recvDatNum[0] : 送信元
     *		srcRank_and_recvDatNum[1] : 受信する Data 数
     *	@param recvStaffTAG 		: 受信したいものの TAG
     *	@param dt 			: 受信する際の Datatype
     *		MPI.BYTE か MPI.DOUBLE 
     *	@return recv 発行可能なら true 
     *
     * うーむ，Datatype が残ったのはあまりよくない．
     */
    public synchronized boolean canRecv(int [] srcRank_and_recvDatNum, 
					int  recvStaffTAG,
					Datatype dt)
    {
	try {
	    Status probeStat = null;
	    probeStat = MPI.COMM_WORLD.Iprobe(MPI.ANY_SOURCE, recvStaffTAG);
	    if(probeStat != null)
	    {
		srcRank_and_recvDatNum[0] = probeStat.source;
		srcRank_and_recvDatNum[1] = probeStat.Get_count(dt);
		// srcRank_and_recvDatNum[1] = probeStat.Get_count(MPI.DOUBLE);
		return(true);
	    }else
	    {
		return(false);	    
	    }
	}catch(MPIException e)
	{
	    System.out.println(e);	    
	    return(false);
	}
    }

    /**
     * ray
     */
    // public reqHandle_st commMpi2_sendRays(int destRank, Ray* pRay, int numOfRays);

    // public reqHandle_st commMpi2_recvRays(int srcRank, Ray *pRay, int numOfRays)


    /**
     * send intensity to Frame Buffer
     */
    // public void commMpi2_sendIntensity2FB(IntensityClass *pIntensity, int numOfInt);

    // public void commMpi2_recvIntensity2FB(IntensityClass *pIntensity, int numOfIntensity);


    /**
     * send, recv PEStatus
     */
    // public void commMpi2_sendPEstat2Host(PEStatClass *pPEstat);
    // public void commMpi2_recvPEstat(PEStatClass *pPEstat);

    /**
     * send, recv Intensity value and vertex position
     */
    // public reqHandle_st commMpi2_sendIntVer(int destRank, intVerClass *pIntver);
    // public void commMpi2_recvIntVer(int srcRank,  intVerClass *pIntver);

    /**
     * send, recv object
     *	1 PE の場合,実際には通信しないのでこの関数で直接宛先(通常は 
     *	host)に書いてしまう．  
     */
    // public void sendIntensityStandAlone(IntensityClass *pObj, int numOfObj);
}



