//------------------------------------------------------------
// $Id: readView.cc,v 1.3 2000/05/16 19:17:38 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 read view data file 
//	1996/7/18
//	Copyright (C) 1993--1998 YAMAUCHI Hitoshi 山内 斉
// view の定義は dataCollections.h にある
//------------------------------------------------------------
#include <stdio.h>
#include <iostream.h>
#include <assert.h>
#include "lineParser.h"
#include "mpi2exit.h"
#include "viewdata.h"

static const int MAX_LINE 	     = 1024;

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

// view data は全てベクトル型の entry である( = 引数が数字のものだけ)
enum view_entry_e {
    // for ray-tracing
    View_point_e,
    View_vector_e,
    View_up_vector_e,
    View_fov_e,	
    View_screen_size_e,
    View_max_ray_ref_param_e,
    View_dist_ray_num_e,
    View_Frustum_e,
    // for radiosity
    View_min_rad_energy_e,
    View_hemres_e,
    View_ENTRYNUM_e		// number of entries
    };

//------------------------------
// initialize line parser
//------------------------------
static void initLPWithViewEntry(void)
{
    assert(PLp == NULL);
    PLp = new LineParser(View_ENTRYNUM_e);
    LineParserNumberElem lpNumElem[View_ENTRYNUM_e]; 
    
    lpNumElem[View_point_e		].set("viewpoint", 	3);
    lpNumElem[View_vector_e		].set("viewvector", 	3);
    lpNumElem[View_up_vector_e		].set("viewupvector", 	3);
    lpNumElem[View_fov_e		].set("viewfov", 	2);
    lpNumElem[View_screen_size_e	].set("screensize", 	2);
    lpNumElem[View_max_ray_ref_param_e	].set("maxRayReflectionParam", 2);
    lpNumElem[View_dist_ray_num_e	].set("distributedRayNum", 1);
    lpNumElem[View_Frustum_e		].set("frustum", 	1);
    lpNumElem[View_min_rad_energy_e	].set("minRadiationEnergy", 1);
    lpNumElem[View_hemres_e		].set("hemres", 	1);

    for(int i = 0; i < View_ENTRYNUM_e; i++)
    {
	PLp->addEntry(&(lpNumElem[i]));
    }
    
    // PLp->print();		// DEBUG LineParser
}


//------------------------------
// err out
//------------------------------
static void readViewError(int errline, const char* pMes)
{
    cerr << "readViewdata:" << errline << ": " << pMes << endl;
    commMpi2_exit(1);
}

//------------------------------
// one line analyzer (LineParser で解析できるコマンドは 1 line)
//	この line の解析に成功したら YES を返す
//-----------------------------
static void analyzeOneLine(ViewDataClass *pViewdata, 
			   int line, const char *pBuf)
{
    LineParserElemIF *pLPIF = NULL;
    if(PLp->evalOneLine(pBuf, &pLPIF) == NO)
    {
	readViewError(line, "Error! : fail to analyze : view data");
    }else
    {
	if(pLPIF->getType() != LP_NUMBER_e)
	{
	    readViewError(line, "Error! : view data must only include "
			  "`setnum' type");
	}

	LineParserNumberElem *pLpType = (LineParserNumberElem *)pLPIF;
	double dval[4];
	for(int i = 0; i < pLpType->getVectorLen(); i++)
	{
	    dval[i] = pLpType->getVecValue(i);
	}

	if(pLpType->getIdentifier() == "viewpoint")
	{
	    // case View_point_e:
	    assert(pLpType->getVectorLen() == 3);
	    pViewdata->setViewPoint(Vector3(dval));
	}
	else if(pLpType->getIdentifier() == "viewvector")
	{
	    // case View_vector_e:
	    assert(pLpType->getVectorLen() == 3);
	    pViewdata->setViewVector(Vector3(dval));
	}
	else if(pLpType->getIdentifier() == "viewupvector")
	{
	    // case View_up_vector_e:
	    assert(pLpType->getVectorLen() == 3);
	    pViewdata->setViewUpVector(Vector3(dval));
	}
	else if(pLpType->getIdentifier() == "viewfov")
	{
	    // case View_fov_e:
	    assert(pLpType->getVectorLen() == 2);
	    pViewdata->setFov(dval[0], dval[1]);
	}		    
	else if(pLpType->getIdentifier() == "screensize")
	{
	    // case View_screen_size_e:
	    assert(pLpType->getVectorLen() == 2);
	    pViewdata->setScreenSize(int(dval[0]), int(dval[1]));
	}
	else if(pLpType->getIdentifier() == "maxRayReflectionParam")
	{
	    // case View_max_ray_ref_param_e:
	    assert(pLpType->getVectorLen() == 2);
	    pViewdata->setMaxRayRefParam(int(dval[0]), dval[1]);
	}
	else if(pLpType->getIdentifier() == "distributedRayNum")
	{
	    // case View_dist_ray_num_e:
	    assert(pLpType->getVectorLen() == 1);
	    pViewdata->setDistributedRayNum(int(dval[0]));
	}
	else if(pLpType->getIdentifier() == "frustum")
	{
	    // case View_Frustum_e:
	    assert(pLpType->getVectorLen() == 1);
	    pViewdata->setFrustumParam(int(dval[0]));
	}
	else if(pLpType->getIdentifier() == "minRadiationEnergy")
	{
	    // case View_min_rad_energy_e:
	    assert(pLpType->getVectorLen() == 1);
	    pViewdata->setMinRadEnergy(dval[0]);
	}
	else if(pLpType->getIdentifier() == "hemres")
	{
	    // case View_hemres_e:
	    assert(pLpType->getVectorLen() == 1);
	    pViewdata->setHemres(int(dval[0]));
	}
	else 
	{
	    // default:
	    readViewError(line, "Error! : internal error : readview.cc "
			  "analyzeOneLine()");
	    commMpi2_exit(1);
	}
    }
}

//------------------------------
// Read viewdata 
//------------------------------
YorN readViewdataFile(const char *pViewdataFile, ViewDataClass *pViewdata)
{
    FILE *pfp = fopen(pViewdataFile, "r");
    if(pfp == NULL)
    {
	cerr << "Error! : can't open viewdata file [" << pViewdataFile <<
	    "]" << endl;
	return(NO);
    }

    // initialize line parser
    initLPWithViewEntry();

    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;
	}
 	analyzeOneLine(pViewdata, line, buf);
    }

    if(PLp->getNumberofUnparsedEntries() > 0)
    {
	cout << "Warning ! : viewdata file [" << pViewdataFile << "] has " 
	    "lack of some entries." << endl;  
	PLp->printUnparsedEntries();
	// 	myString *pUnparsedEntrys = PLp->newNgetUnparsedEntries();
	// 	for(int i = 0; i < PLp->getNumberofUnparsedEntries(); i++)
	// 	{
	//    cout << "unparsed entry [" << i << "]:" << pUnparsedEntrys[i] << 
	// 		endl;
	// 	}
	// 	delete [] pUnparsedEntrys;
    }

    // check view vector and screen vector
    Vector3 vvec = pViewdata->getViewVector();
    Vector3 vup  = pViewdata->getViewUpVector();
    vvec.VNormalize();
    vup.VNormalize();
    if(fabs(vvec.dInn_prd(vup)) > 0.98) // 0.98 は十分似ている方向を見ている値
    {
	// 視線が view up vector と同じであると、スクリーンがなくなってしまう 
	// 特殊効果を狙って、もしそれでも良いのならば、exit を外すこと
	// if view vector and voew up is too near, the screen is like line.
	// But for special effect(warping image), you can erase exit;
	cout << "Error! : at readViewdataFile : "
	    "view vector and view up is too near. " << endl;
	cout << "view up  = (" << pViewdata->getViewUpVector() << ")" << endl;
	cout << "view vec = (" << pViewdata->getViewVector()   << ")" << endl;
	cout << "inner product = " << fabs(vvec.dInn_prd(vup)) << endl;
	commMpi2_exit(1);	// If erase this, image warp will be permitted.
    }
    return(YES);
}

//------------------------------------------------------------
// test for read view
//------------------------------------------------------------
#ifdef TEST_READVIEWDATA
void commMpi2_exit(int errnum)
{
    exit(errnum);
}

const char* genHd(const char *pHead, const IntVec3& PEid)
{
    static char buf[256];    
    sprintf(buf, "%s PE:: %d %d %d : ", pHead, PEid.get(IntVec3::X), 
	    PEid.get(IntVec3::Y), PEid.get(IntVec3::Z))
    return(buf);
}

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
	cerr << "Usage : " << argv[0] << " viewdata file" << endl;
	return(1);
    }
    ViewDataClass viewdata;
    
    if(readViewdataFile(argv[1], &viewdata) == NO)
    {
	cerr << "Error! : read fail " << argv[1] << endl;
	commMpi2_exit(1);
    }
    cout << "//----- read viewdata succeeded" << endl;
    viewdata.print("//", IntVec3(0, 0, 0));
}
// g++ -DTEST_READVIEWDATA -g readview.cc lineParser.o IntVec3.o Vector3.o getvals.o myString.o
#endif
