//------------------------------------------------------------
// $Id: FileInfo.cc,v 1.3 2000/05/16 19:17:38 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 : file information 
//	1998年12月15日(火) option が繁雑になりすぎた．
// 	FileInfo  : 入出力のファイル情報 from option.h
//	Copyright (C) 1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#include "FileInfo.h"
#include "Option.h"
#include "lineParser.h"
#include "constants.h"
#include "ProfileMon.h"

//------------------------------------------------------------
// print out 
//------------------------------------------------------------
void FileInfo::
print(const char *pHead) const 
{
    cout << pHead << "----- FileInfo::print Filename -----"   	<< endl;
    cout << pHead << "conf    [" << this->mConfigFileName	<< "]" << endl;
    cout << pHead << "spc     [" << this->mSpcFileName		<< "]" << endl;
    cout << pHead << "mat     [" << this->mMatFileName		<< "]" << endl;
    cout << pHead << "geo     [" << this->mGeoFileName		<< "]" << endl;
    cout << pHead << "sysinfo [" << this->mSysInfoFileName	<< "]" << endl;
    cout << pHead << "view    [" << this->mViewDataFileName     << "]" << endl;
    cout << pHead << "save    [" << this->mSaveResultFileName	<< "]" ;
    cout << " : save format   ";
    switch(this->mSaveFileFormat)
    {
    case FI_SAVE_PPM:
	cout << "ppm" << endl;	break;
    case FI_SAVE_GFB:	    
	cout << "gfb" << endl;	break;
    default:
	cout << "Error! (none)" << endl;	
	break;
    }
    cout << pHead << "radout  = " << this->mRadioStageOutFileName << endl;
    if(this->mIsRadioOutAt == YES)
    {
	const char *pInfoType[] = { "Shot", "Unshot", "Both", };
	cout << pHead << "radIout = for(" << this->mRadioOutStartHC << "; " << 
		this->mRadioOutEndHC << "; " <<
		this->mRadioOutIncHCNum << ")" << ", type " << 
		pInfoType[this->mRadioIntermFileType] << endl;
	cout << pHead << "radIout basename ["<< 
		this->mRadioIntermFileBaseName << "]" << endl;
    }else
    {
	cout << pHead << "radIout = NO" << endl;
    }
    cout << pHead << "volList = " << this->mVolumeInFileListName << endl;

    // for PRC method
    cout << pHead << "PRCmap  = " << this->mDistanceDataFileName << endl;

    // for Intensity Immediate out
    cout << pHead << "Intensity Immediate out = " << 
    	((this->mIntensityImmediateOut == YES) ? "YES" : "NO") << endl;
    
    // for Genvol (option 関係を共有できる)
    if(Option::getInstance()->isCompressOut() == YES)
    {
	cout << pHead << "Compress Command = " << 
		this->mCompressCommand << endl;
    }else
    {
	cout << pHead << "No output will be compressed." << endl;
    }
    cout << pHead << "(for only genvol4mpi2) : out volumefilename format = "<< 
    	this->mGenvolOutFileNameFormat << endl;

    // profile monitor
    if(Use_Profile_Monitor == 1)
    {
	if(this->isTraceFileOut() == YES)
	{
	    cout << pHead << "Profile Trace: On, Total Radiosity Calc: " <<
		((this->isTraceOutWithUnshotRadiosity() == YES) ? "On" : "Off")
		 << ", Output file basename [" 
		 << this->getTraceFileBaseName() << "]" << endl;
	}else
	{
	    cout << pHead << "Profile Trace: Off" << endl;
	}
    }else
    {
	cout << pHead << "Compile : Use_Profile_MonitorTrace disabled."<< endl;
    }
    cout << pHead << "Compile : Use_IBM_Program_MarkerArray ";
    if(Use_IBM_Program_MarkerArray == 1)
    {
	cout << "enabled," << 
	    ((this->isProgramMarkerOn() == YES) ? "On" : "Off") << endl;
    }else
    {
	cout << "disabled." << endl;
    }
    cout << pHead << "Compile : Use_IBM_Program_MarkerArray ";
    if(Use_Mpi2_Program_MarkerArray == 1)
    {
	cout << "enabled," << 
	    ((this->isProgramMarkerOn() == YES) ? "On" : "Off") << endl;
    }else
    {
	cout << "disabled." << endl;
    }
}

//------------------------------------------------------------
// ここに保持されているファイル間の一貫性を見る
//	mpi2 のレンダリングを行う場合に一貫しているか
//------------------------------------------------------------
YorN FileInfo::
isOKforMpi2(void) const
{
    const char *pMes = "Error! : FileInfo : lack of ";
    
    YorN isOK  		= YES;	// この FileInfo の保持する内容は妥当か
    YorN isObjDat 	= NO;	// 物体情報は存在するのか
    
    // 空間の定義情報 : 必要
    if(this->mSpcFileName == INIT_FILENAME)
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << pMes << "space file, abort." << endl;
	}
    }
    
    // sysinfo : 必要
    if(this->mSysInfoFileName == INIT_FILENAME)
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << pMes << "sysinfo file, abort." << endl;
	}
    }
    
    // view data : 必要
    if(this->mViewDataFileName == INIT_FILENAME)
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << pMes << "viewdata file, abort." << endl;
	}
    }
    
    // mat file があるが geo file がない あるいは
    // geo file があるが mat file がない場合は error : これらはペア
    if(((this->mMatFileName != INIT_FILENAME) && 
	(this->mGeoFileName == INIT_FILENAME)) ||
       ((this->mMatFileName == INIT_FILENAME) && 
	(this->mGeoFileName != INIT_FILENAME)))
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << pMes << "geo file or material file." << endl;
	    cout << "geo file and material file must be specify at"
	    " the same time." << endl;		 
	}
    }else if(this->mGeoFileName != INIT_FILENAME)
    {
	// 上記の条件でなく，しかも geo file がある場合には必ず 
	// material file は存在する．geo と material が両方存在し
	// ない場合はある．geo, material の組は必須ではない．
	// (volume のみのレンダリングもあり．)
	isObjDat = YES;
    }
    
    // output filename : 必要
    if(this->mSaveResultFileName == INIT_FILENAME)
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << pMes << "output filename is not specified." << endl;
	}
    }
    
    // volume file : 必須ではない．(get, mat のみをレンダリングす
    // る場合あり．)
    if(this->mVolumeInFileListName != INIT_FILENAME)
    {
	isObjDat = YES;    
    }
    
    // radiosity stage の結果出力が指定されている場合にはファイル名指定
    if((this->mIsRadioStageResOut == YES) &&
       (this->mRadioStageOutFileName == INIT_FILENAME))
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)
	{
	    cout << pMes << "radiosity stage output file." << endl;
	}
    }
    
    // 出力指定時にパラメータ初期化確認
    if(this->mIsRadioOutAt == YES)
    {
	// 数値の範囲と basename のチェック
	if((this->mRadioOutStartHC  <  0) ||
	   (this->mRadioOutIncHCNum <= 0) ||
	   (this->mRadioOutStartHC  > this->mRadioOutEndHC) ||
	   (this->mRadioIntermFileBaseName == INIT_FILENAME))
	{
	    isOK = NO;
	    if(Option::getInstance()->getVerboseMode() == YES)
	    {
		cout << pMes << "periodically radio out parameters." << endl;
		cout << "\t In spite of RadiosityIFileOut == On." << endl;
	    }
	}
    }
    
    // 物体情報がまったくない場合にはレンダリングできない
    if(isObjDat == NO)
    {
	isOK = NO;
	if(Option::getInstance()->getVerboseMode() == YES)	    
	{
	    cout << pMes << "object data (geometry or volume)" << endl;
	}
    }
    return(isOK);
}

//------------------------------------------------------------
// config file を読む
//------------------------------------------------------------
// 登録するエントリを LineParser に伝える．
// 全部で，(+ 8 (+ 10 1 1 1) 1 1 1 2) = 25
//------------------------------
static void initLpForConfigFile(LineParser *pLp)
{
    //------------------------------
    // option
    //------------------------------
    const char *pOffOnArgs[] = { 
	"Off", "On",  
    };
    const char *pOptionArg[] = {
	"DebugMode", "VerboseMode", "ShowReadData", "ShowObjNumInSubSpc",
	"CompressOutput", 
	"OutputProfileTrace", "CalcTotalUnshotRadiosityEnergy",	
	"ProgramMarker",
    };
    const int NumOfOptions = 8;	// option は上記の 8 つを認識する
    const int NumOfArgs    = 2;	// 各 option は On, Off の 2 つのいずれか
    LineParserTypeElem lpElemOption[NumOfOptions];
    int i;
    for(i = 0; i < NumOfOptions; i++)
    {
	lpElemOption[i].set(pOptionArg[i], NumOfArgs, pOffOnArgs);
	pLp->addEntry(&(lpElemOption[i])); // 登録
    }
    
    //------------------------------
    // names (mainly for file name)
    //------------------------------
    const char *pFileNameID[] = {
	// input file name
	"SpaceParamFile", 	"SysinfoParamFile",	"ViewFile", 
	"MaterialFile",		"GeometryFile", 	"VolumeInfileListFile",
	"PCMdistanceFile",
	// output file name
	"RadiosityResultFile",	"RadiosityIFileBaseName",
	"SaveOutFile", 
	// compress command
	"CompressCommand",
	// for genvol
	"GenvolOutputFileNameFormat",
	// for Profile monitor
	"ProfileMonBasename",
    };
    const int NumOfFileSorts = 13; // 読み込む可能性のある名前の種類は 13
    LineParserNameElem lpElemFile[NumOfFileSorts];
    for(i = 0; i < NumOfFileSorts; i++)
    {
	lpElemFile[i].set(pFileNameID[i]);
	pLp->addEntry(&(lpElemFile[i])); // 登録
    }
    
    //------------------------------
    // Values and Types
    // radiosity の計算途中の出力関係
    //------------------------------
    // 計算中途の出力を行うか
    LineParserTypeElem lpElemRadioIntermediateOut;
    lpElemRadioIntermediateOut.set("RadiosityIFileOut", 2, pOffOnArgs);    
    pLp->addEntry(&(lpElemRadioIntermediateOut)); // 登録

    // radiosity の出力頻度関係
    LineParserNumberElem lpElemRadoutInterval;
    lpElemRadoutInterval.set("RadiosityIFileOutParam", 3);
    pLp->addEntry(&(lpElemRadoutInterval)); // 登録

    // radiosity の中間結果ファイルの出力形態
    LineParserTypeElem lpElemRadioIFileType;
    const char *pIFileType[] = { 
	"ShotRasiosity", "UnshotRadiosity", "BothRadiosity",
    };
    lpElemRadioIFileType.set("RadiosityIFileType", 3, pIFileType);
    pLp->addEntry(&(lpElemRadioIFileType)); // 登録

    // 最終結果出力形態
    LineParserTypeElem lpElemSaveOutFileType;
    const char *pSaveOutFileType[] = { 
	"ppm", "gfb", 
    };
    lpElemSaveOutFileType.set("SaveOutFileType", 2, pSaveOutFileType);    
    pLp->addEntry(&(lpElemSaveOutFileType)); // 登録

    // intensity の出力を即時に行うか
    LineParserTypeElem lpElemIntensityImmediateOut;
    lpElemIntensityImmediateOut.set("ImmediateImageOut", 2, pOffOnArgs);    
    pLp->addEntry(&(lpElemIntensityImmediateOut)); // 登録
}

//------------------------------
// read config file
// Debug などのフラグ系のオプションはコマンドラインの指定との OR であ
// り，reset されることはない．ここではファイル名のコマンドライン指定
// 部分は考えていない．
//------------------------------
YorN FileInfo::
readConfigFile(void)
{
    FILE *pfp = fopen((const char*)this->mConfigFileName, "r");
    if(pfp == NULL)
    {
	return(NO);
    }

    LineParser lp(26);		// 登録する要素数は 26
    initLpForConfigFile(&lp);	// 登録

    // read file and analize
    YorN isParseOK = YES;
    const int MAX_LINE = 256;	// 一行の長さ
    char buf[MAX_LINE];
    int  line = 0;
    while(fgets(buf, MAX_LINE, pfp) != NULL)
    {
	line++;
	if((buf[0] == '\n') || (buf[0] == '#'))	// skip void line and comment 
	{
	    continue;
	}

	// switch 
	const int Off = 0;
	const int On  = 1;

	// analize
	LineParserElemIF *pMElem = NULL; // matched element
	if(lp.evalOneLine(buf, &pMElem) == YES) 
	{
	    // option
	    if(pMElem->getIdentifier() == "DebugMode")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    Option::getInstance()->setDebugMode();
		}
	    }
	    else if(pMElem->getIdentifier() == "VerboseMode")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    Option::getInstance()->setVerboseMode();
		}
	    }
	    else if(pMElem->getIdentifier() == "ShowReadData")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    Option::getInstance()->setShowData();
		}
	    }
	    else if(pMElem->getIdentifier() == "ShowObjNumInSubSpc")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    Option::getInstance()->setShowNumobjInSpace();
		}
	    }
	    else if(pMElem->getIdentifier() == "CompressOutput")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    Option::getInstance()->setCompressOut();
		}else
		{
		    Option::getInstance()->setNotCompress();		    
		}
	    }else if(pMElem->getIdentifier() == "OutputProfileTrace")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    this->setTraceFileOut();
		}
	    }else if(pMElem->getIdentifier()=="CalcTotalUnshotRadiosityEnergy")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    this->setTraceOutWithUnshotRadiosity();
		}
	    }else if(pMElem->getIdentifier()=="ProgramMarker")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    this->setProgramMarkerOn();
		}
	    }
	    // name (file name)
	    else if(pMElem->getIdentifier() == "SpaceParamFile")
	    {
		this->mSpcFileName = ((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "SysinfoParamFile")
	    {
		this->mSysInfoFileName =
			((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "ViewFile")
	    {
		this->mViewDataFileName = 
			((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "MaterialFile")
	    {
		this->mMatFileName = ((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "GeometryFile")
	    {
		this->mGeoFileName = ((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "VolumeInfileListFile")
	    {
		this->mVolumeInFileListName = 
			((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "RadiosityResultFile")
	    {
		this->mRadioStageOutFileName = 
			((LineParserNameElem *)pMElem)->getName();
		this->mIsRadioStageResOut = YES;
	    }
	    else if(pMElem->getIdentifier() == "RadiosityIFileOut")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == On) || (res == Off));
		if(res == On)
		{
		    this->mIsRadioOutAt = YES;
		}else
		{
		    this->mIsRadioOutAt = NO;		    
		}
	    }
	    else if(pMElem->getIdentifier() == "RadiosityIFileBaseName")
	    {
		this->mRadioIntermFileBaseName = 
			((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "SaveOutFile")
	    {
		this->mSaveResultFileName = 
			((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "PCMdistanceFile")
	    {
		// Proximity Clouds Method
		this->mDistanceDataFileName = 
			((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "CompressCommand")
	    {
		// Compress command name
		this->mCompressCommand = 
		    ((LineParserNameElem *)pMElem)->getName();
	    }
	    else if(pMElem->getIdentifier() == "GenvolOutputFileNameFormat")
	    {
		// for genvol4mpi2
		this->mGenvolOutFileNameFormat = 
			((LineParserNameElem *)pMElem)->getName();
	    }
	    // radiosity の中間結果間隔のパラメータ
	    else if(pMElem->getIdentifier() == "RadiosityIFileOutParam")
	    {
		this->mRadioOutStartHC 	= 
		    (int)(((LineParserNumberElem *)pMElem)->getVecValue(0));
		this->mRadioOutEndHC	= 
		    (int)(((LineParserNumberElem *)pMElem)->getVecValue(1));
		this->mRadioOutIncHCNum	=
		    (int)(((LineParserNumberElem *)pMElem)->getVecValue(2));

		// 内部ワーク変数の初期化
		this->mRadioOutLoopVar = this->mRadioOutStartHC;
	    }
	    // radiosity の中間結果出力型
	    else if(pMElem->getIdentifier() == "RadiosityIFileType")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == 0) || (res == 1) || (res == 2));
		this->mRadioIntermFileType = res;
	    }
	    // 最終結果出力ファイルの型
	    else if(pMElem->getIdentifier() == "SaveOutFileType")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == 0) || (res == 1));
		this->mSaveFileFormat = res;
	    }
	    else if(pMElem->getIdentifier() == "ImmediateImageOut")
	    {
		int res = ((LineParserTypeElem *)pMElem)->getTypeNumber();
		assert((res == Off) || (res == On));
		if(res == On)
		{
		    this->setIntensityImmidiateOut();
		}
	    }
	    else if(pMElem->getIdentifier() == "ProfileMonBasename")
	    {
		// profile monitor
		this->setTraceFileBaseName(((LineParserNameElem *)pMElem)->
					   getName());
	    }else
	    {
		cout << "Error! : illegal entry : line " << line << 
		" in [" << this->mConfigFileName << "]\n" << buf << endl;
		isParseOK = NO;
	    }
	}else
	{
	    cout << "Error! : can not analize : line " << line << 
	    " in [" << this->mConfigFileName << "]\n" << buf << endl;
	    isParseOK = NO;
	}
    }

    fclose(pfp);
    return(isParseOK);    
}

//------------------------------------------------------------
// コマンドライン option で FileInfo の上書きを行う
// 	file 名に関しては command line option が優先される．
//	ただし，Mode option に関しては conf file と command line
//	option の OR となる．
//------------------------------------------------------------
static void overrideMes(const char *pMes, const char *pFname)
{
    if((Option::getInstance()->getVerboseMode() == YES) || 
       (Option::getInstance()->getDebugMode()   == YES))
    {
	cout << VERBOSEHEAD << " Override " << pMes << 
	" as [" << pFname << "]." << endl;
    }
}

void FileInfo::
overrideFileInfoWithOption(const FileInfo *pCommandLineFileInfo)
{
    // command line option による FileInfo の上書き
    if(pCommandLineFileInfo->getSpcFileName() != INIT_FILENAME)
    {
	this->setSpcFileName(pCommandLineFileInfo->getSpcFileName());
	overrideMes("spcinfo  filename", 
		    pCommandLineFileInfo->getSpcFileName());
    }
    if(pCommandLineFileInfo->getMatFileName() != INIT_FILENAME)
    {
	this->setMatFileName(pCommandLineFileInfo->getMatFileName());
	overrideMes("material filename",
		    pCommandLineFileInfo->getMatFileName());
    }
    if(pCommandLineFileInfo->getGeoFileName() != INIT_FILENAME)
    {
	this->setGeoFileName(pCommandLineFileInfo->getGeoFileName());
	overrideMes("geometry filename",
		    pCommandLineFileInfo->getGeoFileName());
    }
    if(pCommandLineFileInfo->getSaveResultFileName() != INIT_FILENAME) 
    {
	this->setSaveResultFileName(pCommandLineFileInfo->
				    getSaveResultFileName()); 
	this->setSaveFileFormat(pCommandLineFileInfo->getSaveFileFormat());
	overrideMes("save result filename",
		    pCommandLineFileInfo->getSaveResultFileName());
	
    }
    if(pCommandLineFileInfo->getSysInfoFileName() != INIT_FILENAME)
    { 
	this->setSysInfoFileName(pCommandLineFileInfo->getSysInfoFileName());
	overrideMes("sysinfo filename", 
		    pCommandLineFileInfo->getSysInfoFileName());
    }
    if(pCommandLineFileInfo->getViewDataFileName() != INIT_FILENAME)
    {
	this->setViewDataFileName(pCommandLineFileInfo->getViewDataFileName());
	overrideMes("view filename",
		    pCommandLineFileInfo->getViewDataFileName());
    }
    if(pCommandLineFileInfo->getRadioStageOutFileName() != INIT_FILENAME)
    {
	this->setRadioStageOutFileName(pCommandLineFileInfo->
				       getRadioStageOutFileName());
	overrideMes("radiosity stage result filename",
		    pCommandLineFileInfo->getRadioStageOutFileName());
    }
    if(pCommandLineFileInfo->getRadioIntermediateBaseName() != INIT_FILENAME)
    {
	this->setRadioIntermediateBaseName(pCommandLineFileInfo->
					   getRadioIntermediateBaseName());
	overrideMes("radiosity intermediate result basename",
		    pCommandLineFileInfo->getRadioIntermediateBaseName());
    }
    if(pCommandLineFileInfo->getVolumeInFileListName() != INIT_FILENAME)
    {
	this->setVolumeInFileListName(pCommandLineFileInfo->
				      getVolumeInFileListName());
	overrideMes("volume infile list filename",
		    pCommandLineFileInfo->getVolumeInFileListName());
    }
    if(pCommandLineFileInfo->getDistanceDataFileName() != INIT_FILENAME)
    {
	this->setDistanceDataFileName(pCommandLineFileInfo->
				      getDistanceDataFileName());
	overrideMes("proximity clouds method distance filename",
		    pCommandLineFileInfo->getDistanceDataFileName());
    }
}
