//------------------------------------------------------------
// $Id: lineParser.cc,v 1.4.2.1 2000/06/14 15:00:11 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 one line parser implementation
//	(C) Copyright 1997--1998 YAMAUCHI Hitoshi  山内 斉
//------------------------------------------------------------
#include <assert.h>
#include <float.h>
#include <iostream.h>
#include "getvals.h"
#include "myString.h"
#include "lineParser.h"

//------------------------------------------------------------
// exit function
//------------------------------------------------------------
static void lineparser_exit(int i)
{
#ifndef LINE_PARSER_EXIT_FUNCTION
    exit(i);
#else
    LINE_PARSER_EXIT_FUNCTION ;
    i = i;			// for Wall
#endif
}

static const int RETURN_OF_FAILURE = -1; // 失敗時の返り値
//------------------------------------------------------------
// lineParserEmem 
//-----------------------------------------------------------
// number
//------------------------------
void LineParserNumberElem::
copy(const LineParserNumberElem& rhs)
{
    this->mID 		= rhs.mID;
    this->mNumVecOfArgs = rhs.mNumVecOfArgs;
    if(this->mNumVecOfArgs > 0)
    {
	if(this->mpVec != NULL)
	{
	    delete [] this->mpVec;
	}
	this->mpVec = new double[this->mNumVecOfArgs];
	for(int i = 0; i < this->mNumVecOfArgs; i++)
	{
	    this->mpVec[i] = rhs.mpVec[i];
	}
    }
}

LineParserNumberElem::
LineParserNumberElem(void)
{
    this->mNumVecOfArgs = -1;
    this->mpVec = NULL;
}

LineParserNumberElem::
~LineParserNumberElem(void)
{
    this->mNumVecOfArgs = -1;
    if(this->mpVec != NULL)
    {
	delete [] this->mpVec;
    }
    this->mpVec = NULL;
}

LineParserNumberElem::
LineParserNumberElem(const LineParserNumberElem& rhs)
{
    this->mNumVecOfArgs = -1;
    this->mpVec = NULL;
    this->copy(rhs);
}

LineParserNumberElem::
LineParserNumberElem(const char *pID, int numberOfVector)
{
    // this->mID;			
    this->mNumVecOfArgs = -1;
    this->mpVec = NULL;			
    
    this->set(pID, numberOfVector);
}

const LineParserNumberElem& LineParserNumberElem::
operator=(const LineParserNumberElem& rhs)
{
    if(this != &rhs)
    {
	this->copy(rhs);
    }
    return(*this);
}

YorN LineParserNumberElem::
isOK(void) const 
{
    // 初期化されている場合(ただし解析結果の値はまだ入っていなくとも OK)
    return((this->mNumVecOfArgs > 0) ? YES : NO);
}

void LineParserNumberElem::
print(void) const 
{
    cout << "type = Number, id = " << (const char*)this->mID << ", Number of Args = " << 
	this->mNumVecOfArgs << endl;
}

void LineParserNumberElem::
printContents(void) const
{
    if(this->isOK() == NO)
    {
	cout << "Not initialized" << endl;
    }
    else
    {
	for(int i = 0; i < this->mNumVecOfArgs; i++)
	{
	    cout << "[" << i << "]" << this->mpVec[i] << ", ";
	}
	cout << endl;
    }
} 

void LineParserNumberElem::
set(const char *pID, int numberOfVector)
{
    this->mID 		= myString(pID);    
    if(numberOfVector <= 0)
    {
	cout << "Error! : can not set size of vector. ignored." << endl;
	lineparser_exit(1);
    }
    this->mNumVecOfArgs = numberOfVector;

    if(this->mpVec != NULL)
    {
	delete [] this->mpVec;
    }
    this->mpVec = new double[this->mNumVecOfArgs];
    for(int i = 0; i < this->mNumVecOfArgs; i++)
    {
	this->mpVec[i] = -1.0;
    }
}

void LineParserNumberElem::
setVectorItem(int idx, double val)
{
    if((idx < 0) || (idx >= this->mNumVecOfArgs))
    {
	cout << "Error! : LineParserNumberElem::setVectorItem "
	"illegal index. ignored. " << idx << endl;
	lineparser_exit(1);
    }
    this->mpVec[idx] = val;
}


double LineParserNumberElem::
getVecValue(int idx) const
{
    if((idx < 0) || (idx >= this->mNumVecOfArgs))
    {
	cout << "Error!: LineParserNumberElem::getVecValue "
	"illegal index. ignored. " << idx << endl;
	lineparser_exit(1);
    }
    return(this->mpVec[idx]);
}

//------------------------------
// Type 
//------------------------------
void LineParserTypeElem::
copy(const LineParserTypeElem& rhs)
{
    this->mID			= rhs.mID;
    this->mNumTypeOfCandidates	= rhs.mNumTypeOfCandidates;
    if(this->mNumTypeOfCandidates > 0)
    {
	delete [] this->mpArgsCandidates;
    }
    this->mpArgsCandidates = new myString[rhs.mNumTypeOfCandidates];
    for(int i = 0; i < this->mNumTypeOfCandidates; i++)
    {
	this->mpArgsCandidates[i] = rhs.mpArgsCandidates[i];
    }
    this->mSelected = rhs.mSelected;
}

LineParserTypeElem::
LineParserTypeElem(void)
{
    this->mNumTypeOfCandidates 	= -1;
    this->mpArgsCandidates	= NULL;
    this->mSelected	   	= -1;
}

LineParserTypeElem::
~LineParserTypeElem(void)
{
    this->mNumTypeOfCandidates = -1;
    if(this->mpArgsCandidates != NULL)
    {
	delete [] this->mpArgsCandidates;
    }
    this->mpArgsCandidates = NULL;
}

LineParserTypeElem::
LineParserTypeElem(const LineParserTypeElem& rhs)
{
    this->mNumTypeOfCandidates 	= -1;
    this->mpArgsCandidates	= NULL;
    this->mSelected	   	= -1;
    this->copy(rhs);
}

LineParserTypeElem::
LineParserTypeElem(const char *pID, int numberOfArgsCandidate, 
		   const char **ppArgsCandidate)
{
    // this->mID;
    this->mNumTypeOfCandidates = -1;
    this->mpArgsCandidates	   = NULL;
    this->mSelected		   = -1;

    this->set(pID, numberOfArgsCandidate, ppArgsCandidate);  
}

const LineParserTypeElem& LineParserTypeElem::
operator=(const LineParserTypeElem& rhs)
{
    if(this != &rhs)
    {
	this->copy(rhs);
    }
    return(*this);
}

YorN LineParserTypeElem::
isOK(void) const 
{
    return((this->mNumTypeOfCandidates > 0) ? YES : NO);
}

void LineParserTypeElem::
print(void) const 
{
    cout << "type = Type , id = " << (const char*)this->mID << ", Number of Candidates = "
	 << this->mNumTypeOfCandidates << endl;
}

void LineParserTypeElem::
printContents(void) const
{
    if(this->isOK() == NO)
    {
	cout << "Not initialized" << endl;
    }
    else
    {
	cout << "Arg candidates ";
	for(int i = 0; i < this->mNumTypeOfCandidates; i++)
	{
	    cout << "[" << i << "] " << (const char*)this->mpArgsCandidates[i] << " ";
	}
	cout << endl;
	cout << "selected = " << this->mSelected << endl;
    }
}


void LineParserTypeElem::
set(const char *pID, int numberOfArgsCandidate, 
    const char **ppArgsCandidate)
{
    this->mID 		= myString(pID);    
    if(numberOfArgsCandidate <= 0)
    {
	cout << "Error! : can not set size of candidates. ignored." << endl;
	lineparser_exit(1);
    }
    this->mNumTypeOfCandidates = numberOfArgsCandidate;

    if(this->mpArgsCandidates != NULL)
    {
	delete [] this->mpArgsCandidates;
    }
    this->mpArgsCandidates = new myString[this->mNumTypeOfCandidates];
    for(int i = 0; i < this->mNumTypeOfCandidates; i++)
    {
	this->mpArgsCandidates[i] = myString(ppArgsCandidate[i]);
    }
}

void LineParserTypeElem::
setTypeNumber(int selected)
{
    if((selected < 0) || (selected >= this->mNumTypeOfCandidates))
    {
	cerr << "Error! : LineParserTypeElem::setTypeNumber "
	"selected number is illegal. not selected." << endl;
	lineparser_exit(1);
    }
    else
    {
	this->mSelected = selected;
    }
}

const myString& LineParserTypeElem::
getArgCandidate(int idx) const
{
    if((idx < 0) || (idx >= this->mNumTypeOfCandidates))
    {
	cout << "Error!: LineParserTypeElem::getArgCandidate "
	"illegal index. ignored. " << idx << endl;
	lineparser_exit(1);
    }
    return(this->mpArgsCandidates[idx]);
}

//------------------------------
// Name Entry Elements
//------------------------------
void LineParserNameElem::
copy(const LineParserNameElem& rhs)
{
    this->mID	= rhs.mID;
    this->mName	= rhs.mName;
}

LineParserNameElem::
LineParserNameElem(const LineParserNameElem& rhs)
{
    this->copy(rhs);
}

LineParserNameElem::
LineParserNameElem(const char *pID)
{
    this->set(pID);
}

const LineParserNameElem& LineParserNameElem::
operator=(const LineParserNameElem& rhs)
{
    if(this != &rhs)
    {
	this->copy(rhs);
    }
    return(*this);
}

//------------------------------------------------------------
// one line parser
//------------------------------------------------------------
LineParser::
LineParser(int maxEntryNum)
{
    this->mMaxEntryNum		= -1;
    this->mCurEntryNum		= -1;
    this->mpIsParsed		= NULL;
    this->mppEntries		= NULL;

    this->init(maxEntryNum);
}


void LineParser::
freemem(void)
{
    delete [] this->mpIsParsed;
    delete [] this->mppEntries;

    this->mMaxEntryNum 		= -1;
    this->mCurEntryNum 		= -1;
    this->mpIsParsed		= NULL;
    this->mppEntries		= NULL;
}

LineParser::
~LineParser(void)
{
    this->freemem();
}


YorN LineParserNameElem::
isOK(void) const 
{
    return(((myString &)this->mID != myString("")) ? YES : NO);
}

void LineParserNameElem::
print(void) const
{
    cout << "type = Name , id = " << (const char*)this->mID << endl;
}

void LineParserNameElem::
printContents(void) const
{
    if(this->isOK() == NO)
    {
	cout << "Not initialized" << endl;
    }
    else
    {
	cout << "contents = " << (const char*)this->mName << endl;
    }
}


//------------------------------
// 登録する前の初期化
//	何種類のパターンをパースするかを登録する
//------------------------------
void LineParser::
init(int maxEntryNum)
{
    // clean
    if(maxEntryNum <= 0)
    {
	cerr << "Error! : at LineParser::init() maxEntryNum must be > 0" 
	    << "(maxEntryNum = " << maxEntryNum << ")" << endl;
	lineparser_exit(1);
    }
    this->freemem();

    // allocate
    this->mMaxEntryNum		= maxEntryNum;
    this->mCurEntryNum		= 0;
    this->mpIsParsed = new YorN[maxEntryNum];
    for(int i = 0; i < maxEntryNum; i++)    
    {
	this->mpIsParsed[i] = NO;	
    }
    this->mppEntries	= new LineParserElemIF* [maxEntryNum];
}


//------------------------------------------------------------
// analyze one line
//------------------------------------------------------------
// 数字のエントリを検索する
//------------------------------
int LineParser::
getNumberEntry(myString words[])
{
    int i;
    for(i = 0; i < this->mMaxEntryNum; i++)
    {
	// setnum `words[1]' = dval [dval dval]
	//  0        1       2  3     4    5
	if((this->mppEntries[i]->getType() == LP_NUMBER_e) && // 数字のエントリ
	   ((myString)words[1] == this->mppEntries[i]->getIdentifier())) // 登録entry存在
	{
	    LineParserNumberElem *pLpne = 
	    		(LineParserNumberElem*)this->mppEntries[i];

	    int arg_i;
	    for(arg_i = 0; arg_i < pLpne->getVectorLen(); arg_i++)
	    {
		double val = getDouble(words[arg_i + 3]);
		if(val == DBL_MAX)
		{
		    return(RETURN_OF_FAILURE); // fail to analyze
		}
		pLpne->setVectorItem(arg_i, val);
	    }
	    return(i);		// return entry num
	}
    }
    return(RETURN_OF_FAILURE);	// fail to analyze
}	
	
//------------------------------
// 文字列のエントリを検索する
// 	@return entry id (内部の element の配列の添え字)
//------------------------------
int LineParser::
getTypeEntry(myString words[])
{
    int i;
    for(i = 0; i < this->mMaxEntryNum; i++)
    {
	// settype `words[1]' = string
	//    0       1       2  3
	if((this->mppEntries[i]->getType() == LP_TYPE_e) && // type のエントリ
	   (words[1] == this->mppEntries[i]->getIdentifier())) // 登録entry存在
	{
	    LineParserTypeElem *pLpte = 
	    	(LineParserTypeElem *)(this->mppEntries[i]);
	    int type_entry;
	    for(type_entry = 0; 
		type_entry < pLpte->getVectorLen();
		type_entry++)
	    {
		// words[3] が argment string を指す
		if(words[3] == pLpte->getArgCandidate(type_entry))
		{
		    pLpte->setTypeNumber(type_entry);
		    return(i); 
		}
	    }
	    cerr << "Error! : can not recognize the argment ["
		<< words[3] << "] of entry [" << words[1] << "]" << endl;
	    return(RETURN_OF_FAILURE);
	}
    }
    return(RETURN_OF_FAILURE);	// fail to analyze
}	

//------------------------------
// 名前のエントリを検索する
// 	@return entry id (内部の element の配列の添え字)
//------------------------------
int LineParser::
getNameEntry(myString words[])
{
    int i;
    for(i = 0; i < this->mMaxEntryNum; i++)
    {
	// setname `words[1]' = string
	//    0       1       2  3
	if((this->mppEntries[i]->getType() == LP_NAME_e) && // name のエントリ
	   (words[1] == this->mppEntries[i]->getIdentifier())) // 登録entry存在
	{
	    LineParserNameElem *pLpte = 
	    	(LineParserNameElem *)(this->mppEntries[i]);
	    pLpte->setName(words[3]);
	    return(i);
	}
    }
    return(RETURN_OF_FAILURE);	// fail to analyze
}	

//------------------------------
// エントリを登録する
//------------------------------
int LineParser::
addEntry(const LineParserElemIF *apLpEntry)
{
    // 追加可能なエントリか
    if(apLpEntry->isOK() == NO)
    {
	cerr << "Error! : LineParser::addEntry : can not add "
	"an illegal entry." << endl;
	return(RETURN_OF_FAILURE);
    }

    // 追加可能か : 既に一杯の場合には fail
    if(this->mCurEntryNum >= this->mMaxEntryNum)
    {
	cerr << "Error! : LineParser::addEntry : can not add "
	"no more entry." << endl;
	return(RETURN_OF_FAILURE);
    }	

    switch(apLpEntry->getType())
    {
    case LP_NUMBER_e:
	this->mppEntries[this->mCurEntryNum] = 
	    new LineParserNumberElem(*((LineParserNumberElem *)apLpEntry));
	break;
    case LP_TYPE_e:
	this->mppEntries[this->mCurEntryNum] = 
	    new LineParserTypeElem(*((LineParserTypeElem *)apLpEntry));
	break;
    case LP_NAME_e:
	this->mppEntries[this->mCurEntryNum] = 
	    new LineParserNameElem(*((LineParserNameElem *)apLpEntry));
	break;
    default:
	cerr << "Internal Error!: LineParser::addEntry : no type" << endl;
	lineparser_exit(1);
	break;
    }

    int ret = this->mCurEntryNum;
    this->mCurEntryNum++;	// 次の add に備える
    return(ret);
}

//------------------------------
// one line analyzer (ここでのコマンドは 1 line で 1 つ)
//	この line の解析に成功したら YES を返す
//------------------------------
YorN LineParser::
evalOneLine(const char 	*pLine, 	// 解析する行
	    LineParserElemIF **ppRetRefLPIF)
{
    assert(pLine 	!= NULL);
    (*ppRetRefLPIF) = NULL;

    // 最大の単語分割数を決定し，分割し，setnum, settype, setname で分岐する

    // `set{num|type}' + `id' + `=' + 引数の数 より、引数の数 + 3 が一
    // 行の最大単語数 それに余裕 + 1
    const int MAX_WORDS_IN_LINE = 
	((MAX_LINEPARSER_TYPE_ARGNUM > MAX_LINEPARSER_NUM_ARGNUM) ? 
	 MAX_LINEPARSER_TYPE_ARGNUM : MAX_LINEPARSER_NUM_ARGNUM) + 3 + 1;
    myString words[MAX_WORDS_IN_LINE]; 
    
    // get 1st word	
    myString s(pLine);
    const int num_of_words = getNumOfWords(s); // there is \n
    if(num_of_words >= MAX_WORDS_IN_LINE)
    {
	cout << "Error! : too many words in line." << endl;
	return(NO);
    }
    
    int resnum = splitWithWhite(s, words, num_of_words);
    assert(resnum == num_of_words);
    int entryID = -1;
    if(words[0] == "setnum")	// entry "setnum"
    {
	entryID = this->getNumberEntry(words);
    }
    else if(words[0] == "settype") // entry "settype"
    {
	entryID = this->getTypeEntry(words);
    }
    else if(words[0] == "setname") // entry "setname"
    {
	entryID = this->getNameEntry(words);
    }
    else
    {
	cerr << "Error! : this line is not ``line parser's line''" << endl;
	entryID = RETURN_OF_FAILURE;
	return(NO);	
    }
    
    this->mpIsParsed[entryID] = YES; // Parse したことを記録する．    
    if(entryID != RETURN_OF_FAILURE)
    {
	(*ppRetRefLPIF) = mppEntries[entryID];
	return(YES);
    }
    else
    {
	(*ppRetRefLPIF) = NULL;
	return(NO);
    }

    return(NO);			// never reached here, but gcc warn
}

//------------------------------
// 登録されたエントリのうち未解析のものの数を返す
//------------------------------
int LineParser::
getNumberofUnparsedEntries(void)
{
    if(this->mCurEntryNum < this->mMaxEntryNum)
    {
	// 予約しておいた登録数と現在までに登録している値が異なる
	cerr << "Warning! : number of max entries != number of current entries"
	    << " lineParser.cc:getNumberofUnparsedEntries" << endl;
    }
    int i;
    int unparsed = 0;
    for(i = 0; i < this->mCurEntryNum; i++)
    {
	if(this->mpIsParsed[i] == NO)
	{
	    unparsed++;
	}
    }
    return(unparsed);
}

//------------------------------
// 未解析のエントリの識別子を返す
//	返されたポインタは後に delete しなくてはいけない
//------------------------------
myString * LineParser::
newNgetUnparsedEntries(void)
{
    const int totalunparsed = this->getNumberofUnparsedEntries();
    myString *pRet = new myString[totalunparsed];

    int i;
    int unparsed = 0;
    for(i = 0; i < this->mCurEntryNum; i++)
    {
	if(this->mpIsParsed[i] == NO)
	{
	    pRet[unparsed] = this->mppEntries[i]->getIdentifier();
	    unparsed++;
	}
    }
    return(pRet);
}

//------------------------------
// 未解析のエントリを出力する
//------------------------------
void LineParser::
printUnparsedEntries(void)
{

    if(this->getNumberofUnparsedEntries() == 0)	
    {
	// 本来は不必要であるが、mCurEntryNum のテストのために呼ぶ
	return;
    }

    int i;
    for(i = 0; i < this->mCurEntryNum; i++)
    {
	if(this->mpIsParsed[i] == NO)
	{
	    cout << (this->mppEntries[i]->getIdentifier()) << endl;
	}
    }
}

//------------------------------
// 現在の内部状態の表示を行う
//------------------------------
void LineParser::
print(void)
{
    cout << "----- LineParser status -----\n";    
    cout << "max number of identifiers(登録可能数上限)              = " << 
	this->mMaxEntryNum << endl;
    cout << "current number of submitting identifiers(現在の登録数) = " <<
	this->mCurEntryNum << endl;
    cout << "--- submitted entries" << endl;
    int i;
    for(i = 0; i < this->mCurEntryNum; i++)
    {
	cout << "[" << i << "] ";
	this->mppEntries[i]->print();
	cout << endl;
	cout << " : " << ((this->mpIsParsed[i] == YES) ? "Parsed" :
			  "NotParsed") <<endl; 
    }
}

//------------------------------------------------------------
// self test for lineParser.cc 
//------------------------------------------------------------
#ifdef TEST_LINE_PARSER
// setnum  define_space_size = 10.0 10.0 20.5
// settype OS_type = $OSTYPE
//   	$OSTYPE = {FreeBSD|Linux|SunOS5|Windows95|MacOS8}
// setname InputFileName = test.conf
int main()
{
    // define_space_size, OS_type, InputFileName の 3 つを解析する
    int entrynum   = 3;
    LineParser lp(entrynum);
  
    // 解析したい要素を作成する
    // この要素は型 LineParserNumberElem や LineParserTypeElem も良い
    // が，これらは LineParserElemIF から派生されており，LineParser 自
    // 体は LineParserElemIF を受けるのでこうしておけば一つの配列で扱
    // うことができる．まあ，Object 指向の Interface に対するプログラ
    // ミングという基本中の基本の考えなのでわからなければ Object 指向
    // の本を読んでくれい．
    LineParserElemIF *pLpElem[entrynum];

    // LineParser の管理するエントリ番号のテーブル
    // 現在は登録した順番だが、もしかしたら将来はハッシュ値が返るかも
    // しれない 
    int entryTable[entrynum]; 
    enum entry_e { 
	Defspace, 
	OSTYPE, 
	InputFile
    };

    // define space size は数のベクトルで要素は 3 つ
    pLpElem[Defspace] = new LineParserNumberElem;
    ((LineParserNumberElem *)pLpElem[Defspace])->set("define_space_size", 3);

    // OS_type は 5 つの選択肢の中の一つ
    const char *pTypeArgs[] = { 
	"SunOS5", "Windows95", "MacOS8", "FreeBSD", "Linux", 
    };
    pLpElem[OSTYPE] = new LineParserTypeElem;
    ((LineParserTypeElem *)pLpElem[OSTYPE])->set("OS_type", 5, pTypeArgs);

    // input file
    pLpElem[InputFile] = new LineParserNameElem;    
    ((LineParserNameElem *)pLpElem[InputFile])->set("InputFile");

    // set entries 
    for(int i = 0; i < entrynum; i++)
    {
	entryTable[i] = lp.addEntry(pLpElem[i]);
	lp.print();
    }
    
    //------------------------------
    // では評価します。
    //------------------------------    
    cout << "----- Start evaluation of lines -----" << endl;
    const char *pInputs[] = { 
	"setnum  define_space_size = 4.0 3.0 12.6\n",	
	"settype OS_type = FreeBSD\n",
	"settype OS_type = MacOS\n",
	"setnum  define_space_size = -5.9 120 0.26\n",	
	"setname InputFile = /home/user/test-5.9\n",	
	"settype OS_type = SunOS5\n",
	"settype OS_type = MS-DOS\n",
	"setname  InputFile 	= ../../volumefile000.volume\n",	
	"setname  OutputFile 	= ../../volumefile000.volume\n",	
	NULL,
    };

    cout << "Number of unparsed entries = " << lp.getNumberofUnparsedEntries()
	<< endl;
    int i;
    // print unparsed entries 1
    myString *pUnparsed = lp.newNgetUnparsedEntries();
    for(i = 0; i < lp.getNumberofUnparsedEntries(); i++)
    {
	cout << "Unparsed [" << i << "] " << pUnparsed[i] << endl;
    }
    delete [] pUnparsed;

    // print unparsed entries 2
    cout << "Other method to print unparsed entries" << endl;
    lp.printUnparsedEntries();
    
    for(i = 0; pInputs[i] != NULL; i++)
    {
	cout << "INPUT := " << pInputs[i];
	
	LineParserElemIF *pMatchLpElemIF = NULL;
	if(lp.evalOneLine(pInputs[i], &pMatchLpElemIF) == YES) 
	{
	    // YES ならその行は登録したものに一致
	    pMatchLpElemIF->print();
	    pMatchLpElemIF->printContents();
	}else
	{
	    cerr << "Illegal line at " << pInputs[i] << endl;
	}
	cout << "-----" << endl;
    }
    cout << "Number of unparsed entries = " << lp.getNumberofUnparsedEntries()
	<< endl;
}
#endif
// cd lineparser/; xmkmf -a; make
// g++ -Wall -DTEST_LINE_PARSER lineParser.cc myString.cc getvals.cc
