//------------------------------------------------------------
// $Id: readVolumeInfile.cc,v 1.1.1.1 1999/11/25 06:26:09 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 read volume infile 
//	1997年11月19日(水)
//	Copyright (C) 1996--1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
// ここでは以下のエントリを解析する。
// entry ID を以下に示す
// [0] setnum volmin     = x y z
// [1] setnum volmax     = x y z
// [2] setnum numofgrids = x y z
// [3] setnum rgbdepth   = depth
// [4] setnum fileSequence = from to increment 
// [5] settype slicedir  = {x|y|z}
// [6] settype datatype  = {slice|?(現在は slice のみ)}
// [7] setname fileStrFormat = STRING
//------------------------------
#include <stdio.h>
#include <iostream.h>
#include "lineParser.h"
#include "volumeInfile.h"

#define MAX_VOLINFILE_VECTOR_ARGNUM 4	// 数値を取るエントリの引数最大値
const int MAX_LINE      = 1024;

//------------------------------
// line parser for volume in file
//------------------------------
static LineParser *PLp = NULL;

//------------------------------
// initialize line parser
//------------------------------
static void initLPWithVolumeInfileEntry(void)
{
    // entry 数は (+ 5 2 1) = 8
    const int NUMofVolInFileEntry  = 8;
    PLp = new LineParser(NUMofVolInFileEntry);
    
    // set vector entry
    const int NumOfVec = 5; // 数値を取るパラメータは 4 つ
    LineParserNumberElem lpElemNum[NumOfVec];
    lpElemNum[0].set("volmin", 	3);
    lpElemNum[1].set("volmax", 	3);
    lpElemNum[2].set("numofgrids", 3);
    lpElemNum[3].set("rgbdepth", 1);
    lpElemNum[4].set("fileSequence", 3);

    int i;
    for(i = 0; i < NumOfVec; i++)
    {
	PLp->addEntry(&(lpElemNum[i]));
    }
    
    // set type entry 
    const int NumOfType = 2; // type を取るパラメータは 2 つ
    LineParserTypeElem lpElemType[2];
    const char *pVolDataType[] = {
	"slice", NULL,
    };
    lpElemType[0].set("datatype", 1, pVolDataType);
    const char *pSliceDir[] = {
	"x", "y", "z", NULL,
    };
    lpElemType[1].set("slicedir", 3, pSliceDir);
    
    for(i = 0; i < NumOfType; i++)
    {
	PLp->addEntry(&(lpElemType[i]));
    }

    // set name entry 
    LineParserNameElem lpElemName;    
    lpElemName.set("fileStrFormat");
    PLp->addEntry(&lpElemName);    

#ifndef NDEBUG
    // cout << "DEBUG : volume infile" << endl;
    // PLp->print();    // for debug 
#endif
}

//------------------------------
// errout
//------------------------------
void volumeInfoReadErr(int line, const char *pErrline, const char *pMes)
{
    cout << "Error! : at line " << line << ": fail to analyze volinfile" << 
	endl;
    cout << pErrline << pMes << endl;
    commMpi2_exit(1);
}


//------------------------------
// set vector elements
//------------------------------
static YorN setVectorElems(LineParserElemIF *pLpEIF, 
			   volumeInfileInfoClass *pVolumeInfileInfo)
{
    LineParserNumberElem *pLpNum = (LineParserNumberElem *)pLpEIF;
    GlobalGridInfoClass gginfo = pVolumeInfileInfo->getGlobalGridInfo();
    double dval[3];
    for(int i = 0; i < pLpNum->getVectorLen(); i++)
    {
	dval[i] = pLpNum->getVecValue(i);
    }

    if(pLpNum->getIdentifier() == "volmin")
    {
	// [0] setnum volmin  = x y z
	assert(pLpNum->getVectorLen() == 3);
	gginfo.setMinPos(Vector3(dval));
    }
    else if(pLpNum->getIdentifier() == "volmax")
    {
	// [1] setnum volmax  = x y z
	assert(pLpNum->getVectorLen() == 3);
	gginfo.setMaxPos(Vector3(dval));
    }
    else if(pLpNum->getIdentifier() == "numofgrids")
    {
	// [2] setnum numofgrids = x y z
	if((dval[0] != dval[1]) || (dval[1] != dval[2]))
	{
	    cerr << "Error!: Size of voxel resolution is not "
	    "differ according to axes." << endl; 
	    commMpi2_exit(1);
	}
	assert(pLpNum->getVectorLen() == 3);
	gginfo.setResolSize(IntVec3((int)(dval[0]), (int)(dval[1]), 
				    (int)(dval[2])));
    }
    else if(pLpNum->getIdentifier() == "rgbdepth")
    {
	// [3] setnum rgbdepth = depth
	assert(pLpNum->getVectorLen() == 1);
	gginfo.setRGBDepth((int)(dval[0]));
    }
    else if(pLpNum->getIdentifier() == "fileSequence")
    {
	// [4] setnum fileSequence   = start end increment
	assert(pLpNum->getVectorLen() == 3);
	pVolumeInfileInfo->setFileSequence(int(dval[0]), 
					   int(dval[1]), int(dval[2]));
    }
    else
    {
	return(NO);	
    }

    pVolumeInfileInfo->setGlobalGridInfo(gginfo);
    return(YES);    
}

//------------------------------
// set type elements
//------------------------------
static YorN setTypeElems(LineParserElemIF *pLpEIF, 
			 volumeInfileInfoClass *pVolumeInfileInfo)
{
    LineParserTypeElem *pLpType = (LineParserTypeElem *)pLpEIF;
    
    if(pLpType->getIdentifier() == "datatype")
    {
	// [4] settype slicedir = {X|Y|Z}
	pVolumeInfileInfo->setDataType(pLpType->getTypeNumber());
    }
    else if(pLpType->getIdentifier() == "slicedir")    
    {
	// [5] settype datatype = {slice}
	pVolumeInfileInfo->setSliceDir(pLpType->getTypeNumber());
    }
    else
    {
	return(NO);
    }
    return(YES);    
}

//------------------------------
// set name element
//------------------------------
static YorN setNameElems(LineParserElemIF *pLpEIF, 
			 volumeInfileInfoClass *pVolumeInfileInfo)
{
    LineParserNameElem *pLpName = (LineParserNameElem *)pLpEIF;
    
    if(pLpName->getIdentifier() == "fileStrFormat")
    {
	// [6] settype fileStrFormat = volimg%03g.svol
	pVolumeInfileInfo->setFileNameFormat(pLpName->getName());
    }
    else
    {
	return(NO);
    }
    return(YES);    
}

//------------------------------
// one line analyzer (基本的に私のコマンドは 1 line)
//	この line の解析に成功したら YES を返す
// 今更ではあるが data を pool してあとでアクセスする visiter pattern 
// の方が本当は自由度が高いが，直すのは骨か．
//------------------------------
static YorN analyzeOneLine(volumeInfileInfoClass *pVolumeInfileInfo, 
			   int line,  const char *pBuf)
{
    LineParserElemIF *pLpEIF = NULL;
    
    if(PLp->evalOneLine(pBuf, &pLpEIF) == NO)
    {
	volumeInfoReadErr(line, pBuf, "can not analyze volume infile file");
    }

    switch(pLpEIF->getType())
    {
    case LP_NUMBER_e:
	if(setVectorElems(pLpEIF, pVolumeInfileInfo) == NO)
	{
	    volumeInfoReadErr(line, pBuf, 
			      "illegal vector element in volume infile file");
	}
	break;
    case LP_TYPE_e:
	if(setTypeElems(pLpEIF, pVolumeInfileInfo) == NO)
	{
	    volumeInfoReadErr(line, pBuf, 
			      "illegal type element in volume infile file");
	}
	break;
    case LP_NAME_e:
	if(setNameElems(pLpEIF, pVolumeInfileInfo) == NO)
	{
	    volumeInfoReadErr(line, pBuf, 
			      "illegal name element in volume infile file");
	}
	break;

    default:
	volumeInfoReadErr(line, pBuf, 
			  "illegal `set' elements in volume infile file");
	break;
    }
    return(YES);
}

//------------------------------
// read volume infile
//	一つの volume infile (volume file が複数書いてある) を読む
//------------------------------
YorN readVolumeInfile(const char *pVolInfileName,
		      volumeInfileInfoClass *pVolInfileInfo)
{
    // initialize reader
    initLPWithVolumeInfileEntry();

    FILE *pfp = fopen(pVolInfileName, "r");
    if(pfp == NULL)
    {
	cerr << "Error! : can't open volume infile file [" <<
	    pVolInfileName << "]" << endl;
	return(NO);
    }
    
    char buf[MAX_LINE];
    int  line = 0;
    while(fgets(buf, MAX_LINE, pfp) != NULL)
    {
	line++;
	// skip comment and void line
	if(((buf[0] == '/') && (buf[1] == '/'))	|| // //
	    (buf[0] == '#') 			|| // #
	    (buf[0] == ' ') 			|| // ' '
	    (buf[0] == '\n')			   // '\n'	   
	   )
	{
	    continue;
	}
	if(analyzeOneLine(pVolInfileInfo, line, buf) == NO)
	{
	    cout << "Error! : readVolumeInfile : at line " << line << 
		": fail to analyze" << endl;  
	    cout << "Error Occur at `" << buf << "'" << endl;
	    commMpi2_exit(1);
	}
    }
    if(PLp->getNumberofUnparsedEntries() > 0)
    {
	cout << "Error! : volume infile file [" << pVolInfileName <<
	    "] has some lacks of entries." << endl;
	PLp->printUnparsedEntries();
	commMpi2_exit(1);
    }
    pVolInfileInfo->calcVoxelEdgeLen();
    fclose(pfp);
    return(YES);
}

//------------------------------------------------------------
// test read volume infile file
//------------------------------------------------------------
#ifdef TEST_READVOLINFILE
void commMpi2_exit(int i)
{
    exit(i);
}

const char* genHd(const char *pHead, const int rank)
{
    static char buf[256];    
    sprintf(buf, "%s rank %d : ", pHead, rank);
    return(buf);    
}

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
	cerr << "Usage : " << argv[0] << " volumeInfileFile " << endl;
	return(1);
    }

    // read volume infile
    volumeInfileInfoClass volinfo;
    if(readVolumeInfile(argv[1], &volinfo) == NO)
    {
	cerr << "Error! : read fail " << argv[1] << endl;
	exit(1);
    }
    cout << "// test ----- read volumeinfo succeeded" << endl;
    volinfo.print("//", IntVec3(0, 0, 0));
}
// g++ -DTEST_READVOLINFILE -g readVolInfile.cc lineParser.o IntVec3.o Vector3.o myString.o getvals.o -lm
#endif // TEST_READVOLINFILE
