/*
 * $Id: SysInfo.java,v 1.11 1999/12/29 21:23:32 yamauchi Exp $
 *     (M\pi)^2 : FileInfo (file information) 
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	Copyright 1999 MAEDA Atusi
 *	@author YAMAUCHI Hitoshi 山内 斉
 *
 * 	SysInfo  : システムの情報 (PE 数など)
 *
 * original source sysinfo.h
 * 
 * 1999年7月31日(土)
 */

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

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.Serializable;
// import java.util.Map;
import jp.ac.tohoku.is.archi.mpi2.jdk12src.Map;

import jp.ac.tohoku.is.archi.mpi2.util.IntVec3;
import jp.ac.tohoku.is.archi.mpi2.util.Assert;
import jp.ac.tohoku.is.archi.mpi2.util.LineParser;
import jp.ac.tohoku.is.archi.mpi2.net.DoubleArrayable;

/**
 * Sysinfo : システム情報
 * 	mAllocScheme が ELSE_Alloc の時は初期化されていない
 */
public final class SysInfo 
implements Mpi2Constants, Mpi2Enum, Serializable // , DoubleArrayable 
{
    private IntVec3 mPEDim;		// PE が x,y,z 方向にいくつあるか
    private int	    mTotalPEnum;	// PE の数
    private int     mAllocScheme;	// subspace allocation scheme
    private boolean mDistributeRadiationHC;	// HemiCube からの放射の方式
    private int     mShadingScheme;	// shading の方法
    private boolean mDoRadiosity;	// Rendering の方法
    private boolean mDoRayTracing;

    // 情報のまとめ送りパラメータ
    private int	    mProcRadiositiesAtOnce; // radiosity rays/packet
    private int	    mSendVerIntAtOnce;	//頂点輝度情報を1packetにいくつ入れるか
    private int	    mProcRaysAtOnce;	// rays / packet
    private int	    mSendInt2FBAtOnce;	// GFB へ送る輝度情報
    // レイの発生の停止、再開のパラメータ
    private int     mSuspendRayGen;	// レイ発生停止のレイの数 (in a PE)
    private int     mResumeRayGen; 	// レイ発生再開のレイの数 (in a PE)
    private int     mRayPropSkipMethod;	// レイの伝搬方法
    private int     mPRCMapGenerationMethod; // PRC 法における Map 生成方式
    private int     mSystemNetworkType;	// システムのネットワーク形態
    // 初期光発生方式
    private int     mFirstRayGenMethod; 
    
    public SysInfo() 
    {
	this.mPEDim			= null;
	this.mTotalPEnum		= -1;
	this.mAllocScheme		= ELSE_Alloc;
	this.mDistributeRadiationHC	= true;
	this.mShadingScheme		= FlatShading; 
	this.mDoRadiosity		= false;
	this.mDoRayTracing		= false;
	this.mProcRadiositiesAtOnce	= -1;
	this.mSendVerIntAtOnce		= -1;
	this.mProcRaysAtOnce		= -1;
	this.mSendInt2FBAtOnce		= -1;
	this.mSuspendRayGen		= 10000;
	this.mResumeRayGen		= 5000;
	this.mRayPropSkipMethod		= ELSE_rayprop;
	this.mPRCMapGenerationMethod	= ELSE_mapgen;
	this.mSystemNetworkType		= ELSE_network;
	this.mFirstRayGenMethod		= ELSE_frgm;
    }

    public SysInfo(SysInfo rhs)
    {
	this.mPEDim 		   	= new IntVec3(rhs.mPEDim);
	this.mTotalPEnum	   	= rhs.mTotalPEnum;
	this.mAllocScheme	   	= rhs.mAllocScheme;
	this.mDistributeRadiationHC	= rhs.mDistributeRadiationHC;
	this.mShadingScheme		= rhs.mShadingScheme;
	this.mDoRadiosity		= rhs.mDoRadiosity;
	this.mDoRayTracing		= rhs.mDoRayTracing;
	this.mProcRadiositiesAtOnce	= rhs.mProcRadiositiesAtOnce;
	this.mSendVerIntAtOnce		= rhs.mSendVerIntAtOnce;
	this.mProcRaysAtOnce		= rhs.mProcRaysAtOnce;
	this.mSendInt2FBAtOnce		= rhs.mSendInt2FBAtOnce;
	this.mSuspendRayGen		= rhs.mSuspendRayGen;
	this.mResumeRayGen		= rhs.mResumeRayGen;
	this.mRayPropSkipMethod	= rhs.mRayPropSkipMethod;
	this.mPRCMapGenerationMethod	= rhs.mPRCMapGenerationMethod;
	this.mSystemNetworkType	= rhs.mSystemNetworkType;
	this.mFirstRayGenMethod	= rhs.mFirstRayGenMethod;
    }

    public void setPEDim(IntVec3 aPEDim)
    {
	for(int i = 0; i < 3; i++)
	{
	    if(aPEDim.get(i) <= 0)
	    {
		throw new RuntimeException("Error!: illegal PE num " +
					   "SysInfo.setPEDim");
		// commMpi2_exit(1);
	    }
	}
	this.mPEDim = aPEDim;
	this.mTotalPEnum = this.mPEDim.get(IntVec3.X) * 
	    this.mPEDim.get(IntVec3.Y) * this.mPEDim.get(IntVec3.Z);
	Assert.out(this.mTotalPEnum > 0, 
		   "Error! : setPEDim PEnum must be > 0");
    }
    public IntVec3 peekPEDim()  	{ return(this.mPEDim); }
    public int     getTotalPENum()  	{ return(this.mTotalPEnum); }

    public boolean setAllocationScheme(int anAllocScheme)
    {
	if(anAllocScheme == ELSE_Alloc)	
	{
	    throw new RuntimeException("Error! : setted unknown allocation " +
				       "scheme.");
	    // return(false);
	}
	if(this.mAllocScheme != ELSE_Alloc)
	{
	    throw new RuntimeException("Warning!: alloc scheme is overrided");
	    // return(false);
	}
	this.mAllocScheme = anAllocScheme;
	return(true);
    }
    public int getAllocationScheme()  	{ return(this.mAllocScheme);    }

    public void setDistributeRadiation(){this.mDistributeRadiationHC = true;}
    public void setContinuousRadiation(){this.mDistributeRadiationHC = false;}
    public boolean isDistributedRadiation()
    {
	return(this.mDistributeRadiationHC);	
    }
    public void setShadingScheme(int s) 	
    { 
	if((s < 0) || (ELSE_Shading <= s))
	{
	    throw new RuntimeException("Error! : Illegal shading scheme");
	    // exit(1);
	}
	this.mShadingScheme = s; 
    }
    public int  getShadingScheme()  	{ return(this.mShadingScheme); }    
    public void setProcRaysAtOnce(int procRadioNumAtOnce, 
				  int sendVerIntsAtOnce,
				  int procRayNumAtOnce, 
				  int SendInt2FBAtOnce)
    {
	if((procRadioNumAtOnce < 1) || (sendVerIntsAtOnce  < 1) || 
	   (procRayNumAtOnce   < 1) || (SendInt2FBAtOnce   < 1))
	{
	    String s = "Error! : illegal proccessing radio/ray num at once ("+
		"radio, int, ray, int2FB) = (" + procRadioNumAtOnce + ", " + 
		sendVerIntsAtOnce + ", " + procRayNumAtOnce + ", "  + 
		SendInt2FBAtOnce  + ")";
	    throw new RuntimeException(s);
	    // commMpi2_exit(1);
	}
	this.mProcRadiositiesAtOnce	= procRadioNumAtOnce;
	this.mSendVerIntAtOnce		= sendVerIntsAtOnce;
	this.mProcRaysAtOnce		= procRayNumAtOnce;
	this.mSendInt2FBAtOnce		= SendInt2FBAtOnce;
    }
    public int  getProcRadioAtOnce()	{return(this.mProcRadiositiesAtOnce);}
    public int  getSendVerIntAtOnce()  	{ return(this.mSendVerIntAtOnce);}
    public int  getProcRayTrAtOnce()  	{ return(this.mProcRaysAtOnce);  }
    public int  getSendInt2FBAtOnce()  	{ return(this.mSendInt2FBAtOnce);}
    public void setSuspendRayNums(int suspendRayNum, int resumeRayNum)
    {
	// suspend の最低数は suspend > resume > 0 より (2 > 1 > 0)
	if((suspendRayNum <= 2) || (resumeRayNum <= 1))
	{
	    String s = "Error! : illegal {suspend|resume} number of rays.\n" +
		"Number of suspend rays(>2) = "  + suspendRayNum + 
		", Number of resume rays(>1) = " + resumeRayNum;
	    throw new RuntimeException(s);
	    // commMpi2_exit(1);
	}
	if(suspendRayNum <= resumeRayNum)
	{
	    String s = "Error! : illegal suspend, resume number of rays. " + 
		"It must be suspend > resume.\n" +
		" Number of suspend rays  = " + suspendRayNum + 
		", Number of resume rays = " + resumeRayNum;
	    throw new RuntimeException(s);	    
	    // commMpi2_exit(1);
	}
	this.mSuspendRayGen		= suspendRayNum;
	this.mResumeRayGen		= resumeRayNum;
    }
    public int getSuspendRayNums()  	{ return(this.mSuspendRayGen);	}
    public int getResumeRayNums()    	{ return(this.mResumeRayGen);	}
    
    // ray 伝搬時のスキップ方法
    public void setRayPropSkipMethod(int rpnet)
    {
	if((rpnet < 0) || (ELSE_rayprop <= rpnet))
	{
	    String s = "Internal Error!: illegal val at setRayPropSkipMethod "
		+ rpnet; 
	    throw new RuntimeException(s);	    
	    // commMpi2_exit(1);
	}
	this.mRayPropSkipMethod = rpnet;
    }
    public int getRayPropSkipMethod()  { return(this.mRayPropSkipMethod); }
    
    // PRC 法で利用する Map の生成方法
    public void setPRCMapGenerationMethod(int PRCMapGenMethod)
    {
	if((PRCMapGenMethod < 0) || (ELSE_mapgen <= PRCMapGenMethod))
	{
	    String s = "Internal Error!:illegal val at " + 
		"setPRCMapGenerationMethod " + PRCMapGenMethod; 
	    throw new RuntimeException(s);	    
	    // commMpi2_exit(1);
	}
	this.mPRCMapGenerationMethod = PRCMapGenMethod;
    }
    public int  getPRCMapGenerationMethod() 
    {
	return(this.mPRCMapGenerationMethod);
    }

    public void setSystemNetworkType(int nettype)
    {
	if((nettype < 0) || (ELSE_network <= nettype))
	{
	    String s = "Internal Error!: illegal val at setSystemNetoworkType "
		+ nettype; 
	    throw new RuntimeException(s);	    	    
	    // commMpi2_exit(1);
	}
	this.mSystemNetworkType = nettype;
    }
    public int getSystemNetworkType()  { return(this.mSystemNetworkType);}
    

    // add first ray generation method 1998年5月31日(日)
    public void setFirstRayGenerationMethod(int scanMethod)
    {
	if((scanMethod < 0) || (ELSE_frgm <= scanMethod))
	{
	    String s = "Internal Error!: no such first ray generation method.";
	    throw new RuntimeException(s);	    	    
	    // commMpi2_exit(1);	    
	}
	this.mFirstRayGenMethod = scanMethod;
    }
    public int getFirstRaysGenerationMethod()
    {
	return(this.mFirstRayGenMethod);
    }
    
    public void    setRadiosityCalc()	{ this.mDoRadiosity = true;	}
    public boolean isRadiosityCalc() 	{ return(this.mDoRadiosity);	}
    public void    setRayTracingCalc()	{ this.mDoRayTracing = true;	}
    public boolean isRayTracingCalc() 	{ return(this.mDoRayTracing);	}
    
    public String toStringWithHead(String head)  
    {
	String s = head + "sysinfo: PEDim ( " + this.mPEDim + ") = " +
	    this.mTotalPEnum + "\n";
	String [] allocScheme = { 
	    "Block", "Distributed", "Skewed-Distributed", "Random", 
	};
	String [] shadeScheme = { 
	    "Flat", "Gouroud", 
	};
	String [] rayPropskip = { 
	    "NoSkip", "SobokuSkip", "ProximityClouds", 
	};
	String [] PRCMapGenMethod = {
	    "NoPRC", "ReadMap", "GenerateMap", 
	};
	String [] systemNetwork = { 
	    "Torus", "MultiStage", 
	};
	String [] firstRayGenMethod = {
	    "AllScan", "Skip by Scanline", "Distributed Type Scan", 
	};
	
	s += head + "Allocation scheme             = " + 
	    allocScheme[this.mAllocScheme] + "\n";
	s += head + "Distributed radiation from HC = " +
	    ((this.mDistributeRadiationHC == true) ? "Yes" : "NO") + "\n";
	s += head + "Shading Scheme                = " +
	    shadeScheme[this.mShadingScheme] + "\n";
	s += head + "Rendering Scheme              = " +
	    ((this.mDoRadiosity  == true) ? "Radiosity, " : "")+
	    ((this.mDoRayTracing == true) ? "Ray-Tracing" : "") + "\n";
	s += head + "ProcRaysAtOnce(radiosity, " +
	    "intensityinfo, ray-tracing, intensity to GFB) = (" +
	    this.mProcRadiositiesAtOnce + ", " +  
	    this.mSendVerIntAtOnce + ", "  +
	    this.mProcRaysAtOnce + ", " +
	    this.mSendInt2FBAtOnce + ")" + "\n";
	s += head + "SuspendRayGenParam "+
	    "(suspend, resume) = (" + this.mSuspendRayGen + ", " +
	    this.mResumeRayGen + ")" + "\n";
	s += head + "RayPropSkipMethod = " + 
	    rayPropskip[this.mRayPropSkipMethod] + "\n";
	s += head + "PRCMapGenerationMethod = " + 
	    PRCMapGenMethod[this.mPRCMapGenerationMethod] + "\n";
	s += head + "SystemNetwork = " + 
	    systemNetwork[this.mSystemNetworkType] + "\n";
	s += head + "FirstRaysGenMethod = " + 
	    firstRayGenMethod[this.mFirstRayGenMethod] + "\n";
	return(s);
    }

    public boolean isContentsOK()
    {
	boolean isOK = true;
	String err_h = "Error!: There is a lack of contents of a sysinfo: ";
	if(this.mPEDim.get(IntVec3.X) < 0)
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "NumberOfPEs");
	}
	if(this.mAllocScheme == ELSE_Alloc)
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "AllocationScheme");
	}
	// this.mIsImmediateOutImage	default = false
	// this.mDistributeRadiationHC	default = true
	// this.mShadingScheme 	default = FlatShading
	if((this.mDoRadiosity == false) && (this.mDoRayTracing == false))
	{
	    isOK = false;
	    String s = err_h + "radiosity or raytracing must be set" + "\n" +
		"radiosity   = " + 
		((this.mDoRadiosity  == true) ? "YES" : "NO") + "\n" +
		"ray-tracing = " + 
		((this.mDoRayTracing == true) ? "YES" : "NO") + "\n";
	    throw new RuntimeException(s);
	}
	if((this.mProcRadiositiesAtOnce < 1) ||
	   (this.mSendVerIntAtOnce 	 < 1) ||
	   (this.mProcRaysAtOnce 	 < 1) ||
	   (this.mSendInt2FBAtOnce	 < 1))
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "ProcRaysAtOnce");
	}
	if((this.mSuspendRayGen < 1) ||
	   (this.mResumeRayGen  < 1))
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "SuspendRayGenParam");
	}
	else if(this.mResumeRayGen >= this.mSuspendRayGen)
	{
	    isOK = false;
	    String s = "Error! :illegal SuspendRayGenParam: (suspend, resume) "
		+ "= (" + this.mSuspendRayGen + ", " + this.mResumeRayGen +
		")\n" + "It must be resume < suspend" + "\n";
	    throw new RuntimeException(s);
	}	    
	if(this.mRayPropSkipMethod == ELSE_rayprop)
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "RayPropSkipMethod");
	}
	if(this.mPRCMapGenerationMethod == ELSE_mapgen)
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "PRCMapGenerationMethod");
	}
	if(this.mSystemNetworkType == ELSE_network)
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "SystemNetworkType");
	}
	if(this.mFirstRayGenMethod == ELSE_frgm)
	{
	    isOK = false;
	    throw new RuntimeException(err_h + "FirstRaysGenMethod");
	}

	// 初期光分散時には空間 skip 法を適用すること
	if(this.mFirstRayGenMethod != AllScan_frgm)
	{
	    // AllScan でない場合には制限あり : No skip は許されない
	    if(this.mRayPropSkipMethod == NoSkip_rayprop)
	    {
	        String s = 
		    "Error! : if first ray generation method is distributed,\n"
		    + "subspace skip method must be applied.\n"+
		    "(Noskip can be used with Allscan only.)\n";
		isOK = false;
		throw new RuntimeException(s);		
	    }
	}
	return(isOK);
    }

  static LineParser.Element[] params = {
    new LineParser.Number("NumberOfPEs", 3),
    new LineParser.Number("ProcRaysAtOnce", 4),
    new LineParser.Number("SuspendRayGenParam", 2),
    new LineParser.Type("AllocationScheme",
			new String[] {"Block", "Distributed",
				      "SkewedDistributed", "Random"}),
    new LineParser.Type("RadiosityDistributionMethod",
			new String[] {"Continuous", "Distribute"}),
    new LineParser.Type("ShadingScheme",
			new String[] {"Gouroud", "Flat"}),
    new LineParser.Type("Rendering",
			new String[] {"ray-tracing", "radiosity", "multi-pass"}),
    new LineParser.Type("RayPropSkipMethod",
			new String[] {"NoSkip", "SobokuSkip",
				      "ProximityClouds"}),
    new LineParser.Type("PRCMapGenerationMethod",
			new String[] {"NoPRC", "ReadMap", "GenerateMap"}),
    new LineParser.Type("SystemNetworkType",
			new String[] {"MultiStage", "Torus"}),
    new LineParser.Type("FirstRayGenMethod",
			new String[] {"AllScan", "ScanlineScan",
			"DistributedScan"})
  };

    public void readFromStream(InputStream in) throws IOException
    {
      LineParser lp = new LineParser(params);
      Map param = lp.parseAll(in);

      double[] numPEs = (double[])param.get("NumberOfPEs");

      // 実は mPEDim を setPEDim で set すると Total などが計算される
      this.setPEDim(new IntVec3((int)numPEs[0], 
				(int)numPEs[1], (int)numPEs[2]));

      double[] procRayAtOnce = (double[])param.get("ProcRaysAtOnce");
      //       mProcRadiositiesAtOnce = (int)procRayAtOnce[0];
      //       mSendVerIntAtOnce = (int)procRayAtOnce[1];
      //       mProcRaysAtOnce = (int)procRayAtOnce[2];
      //       mSendInt2FBAtOnce = (int)procRayAtOnce[3];
      this.setProcRaysAtOnce((int)procRayAtOnce[0], (int)procRayAtOnce[1],
			     (int)procRayAtOnce[2], (int)procRayAtOnce[3]);

      double[] suspendRayGenParam = (double[])param.get("SuspendRayGenParam");
      // mSuspendRayGen = (int)suspendRayGenParam[0];
      // mResumeRayGen = (int)suspendRayGenParam[1];
      this.setSuspendRayNums((int)suspendRayGenParam[0],
			     (int)suspendRayGenParam[1]);
      
      String allocScheme = (String)param.get("AllocationScheme");
      if (allocScheme.equals("Block")) {
	mAllocScheme = Block_Alloc;
      } else if (allocScheme.equals("Distributed")) {
	mAllocScheme = Distributed_Alloc;
      } else if (allocScheme.equals("SkewedDistributed")) {
	mAllocScheme = Skewed_Alloc;
      } else if (allocScheme.equals("Random")) {
	mAllocScheme = Random_Alloc;
      } else {
	// Can't happen.
	throw new RuntimeException("can't happen");
      }

      String rdm = (String)param.get("RadiosityDistributionMethod");
      // ???

      String shading = (String)param.get("ShadingScheme");
      mShadingScheme = shading.equals("Gouroud") ? GouroudShading : FlatShading;
      String rendering = (String)param.get("Rendering");
      mDoRayTracing = (rendering.equals("ray-tracing") ||
		       rendering.equals("multi-pass"));
      mDoRadiosity = (rendering.equals("radiosity") ||
		      rendering.equals("multi-pass"));

      String rayPropSkip = (String)param.get("RayPropSkipMethod");
      if (rayPropSkip.equals("NoSkip")) {
	mRayPropSkipMethod = NoSkip_rayprop;
      } else if (rayPropSkip.equals("SobokuSkip")) {
	mRayPropSkipMethod = SobokuSkip_rayprop;
      } else if (rayPropSkip.equals("ProximityClouds")) {
	mRayPropSkipMethod = ProximityClouds_rayprop;
      }

      String prc = (String)param.get("PRCMapGenerationMethod");
      if (prc.equals("NoPRC")) {
	mPRCMapGenerationMethod = NoPRC_mapgen;
      } else if (prc.equals("ReadMap")) {
	mPRCMapGenerationMethod = ReadMap_mapgen;
      } else if (prc.equals("GenerateMap")) {
	mPRCMapGenerationMethod = GenerateMap_mapgen;
      }

      String netType = (String)param.get("SystemNetworkType");
      mSystemNetworkType = netType.equals("MultiStage") ?
	MultiStage_network : Torus_network;

      String firstRayGen = (String)param.get("FirstRayGenMethod");
      if (firstRayGen.equals("AllScan")) {
	mFirstRayGenMethod = AllScan_frgm;
      } else if (firstRayGen.equals("ScanlineScan")) {
	mFirstRayGenMethod = ScanlineScan_frgm;
      } else if (firstRayGen.equals("DistributedScan")) {
	mFirstRayGenMethod = DistributedScan_frgm;
      }
    }

    public void readFromFile(String fileName) throws IOException
    {
      FileInputStream fin = new FileInputStream(fileName);
      readFromStream(fin);
      fin.close();
    }
}
