//
// restricted VRML 2 parser
//
// used in G-PASS (3d Shadow) project
// 
// Copyright 1999-2000 Consilium AB
// Copyright 1999-2000 Antonio Solo (aka SoloTony)
//

#include "stdafx.h"
#include "vrml_2.h"
#include "vrml.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

static char current_file[_MAX_PATH] = "";
static DWORD line = 0;
static DWORD pos = 0;
static char* buf = NULL;
static DWORD bufSize;
static char* currentLine = NULL;

static _List rootNodesList;
static _List allNodes;
static CList<_pList, _pList> allLists;
static CList<_pIntList, _pIntList> allIntLists;
static CList<_pCoordList, _pCoordList> allCoordLists;


////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////    lexical analyzer functions ////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

static CString logStr;
static bool skipNodes;

#define T_EOF 0

// return true if current char is separator
bool IsSpaceChar()
{
	switch (buf[pos])
	{
	case ' ':
	case '\t':
	case '\r':
	case '\n':
		return true;
	}
	return false;
}

// return true if current char is token
static bool IsTokenChar()
{
	switch (buf[pos])
	{
	case '[':
	case ']':
	case '{':
	case '}':
	case ',':
		return true;
	}
	return false;
}

// check new line
static void CheckNewLine()
{
	if (buf[pos]=='\n')
	{
		line++;
		currentLine = &buf[pos+1];
	}
}

// check new end of file
static bool IsEOF()
{
	return ((pos>=bufSize) || (buf[pos]=='\0'));
}

// move position to the next char
static bool GetNextChar()
{
	pos ++;
	return (!IsEOF());
}

// read integer number from current position
static bool GetInteger(LPCSTR str, DWORD len, int& _res)
{
	int res = 0;
	bool minus = false;
	DWORD i=0;
	if (str[0]=='-') { i++; minus = true; }
	else if (str[0]=='+') { i++;}
	for (; i<len; i++)
	{
		if ((str[i] < '0') || (str[i] > '9')) return false;
		res = res*10 + str[i] - '0';
	}
	_res = minus?-res:res;
	return true;
}

// read float number from current position
static bool GetFloat(LPCSTR str, DWORD len, float& _res)
{
	double res = 0;
	bool minus = false;
	bool powerMinus = false;
	DWORD i=0;
	double fract = 0.1;
	int power = 0;;
	if (str[0]=='-') { i++; minus = true; }
	else if (str[0]=='+') { i++;}
	for (; i<len; i++)
	{
		if (str[i] == '.') {i++; goto __fraction__;}
		if ((str[i] == 'e') || (str[i] == 'E')) {i++; goto __power__;}
		if ((str[i] < '0') || (str[i] > '9')) return false;
		res = res*10 + (str[i] - '0');
	}
	goto __ok__;
__fraction__:
	for (; i<len; i++)
	{
		if ((str[i] == 'e') || (str[i] == 'E')) {i++; goto __power__;}
		if ((str[i] < '0') || (str[i] > '9')) return false;
		res = res + (str[i] - '0')*fract;
		fract = fract / 10;
	}
	goto __ok__;
__power__:
	if (str[i]=='-') { i++; powerMinus = true; }
	else if (str[i]=='+') { i++;}
	for (; i<len; i++)
	{
		if ((str[i] < '0') || (str[i] > '9')) return false;
		power = power*10 + (str[i] - '0');
	}
	if (powerMinus) power = -power;
	res = res*pow(10, power);
__ok__:
	if (minus) res = -res;
	_res = (float) res;
	return true;
}

// known VRML 2.0 names

struct CTokenInfo
{
	char name[64];
	int  id;
};

// known VRML 2.0 names
static CTokenInfo tis[] = 
{
	{"Material",			T_Material},
	{"Anchor",				T_Anchor},
	{"Appearance",			T_Appearance},
	{"Coordinate",			T_Coordinate},
	{"Group",				T_Group},
	{"IndexedFaceSet",		T_IndexedFaceSet},
	{"Normal",				T_Normal},
	{"Shape",				T_Shape},
	{"Transform",			T_Transform},
	{"Box",					T_Box},
	{"Color",				T_Color},
	{"Cone",				T_Cone},
	{"Cylinder",			T_Cylinder},
	{"ElevationGrid",		T_ElevationGrid},
	{"Extrusion",			T_Extrusion},
	{"ImageTexture",		T_ImageTexture},
	{"IndexedLineSet",		T_IndexedLineSet},
	{"MovieTexture",		T_MovieTexture},
	{"PixelTexture",		T_PixelTexture},
	{"PointSet",			T_PointSet},
	{"Sphere",				T_Sphere},
	{"Text",				T_Text},
	{"TextureCoordinate",	T_TextureCoordinate},
	{"TextureTransform",	T_TextureTransform},
	{"ambientIntensity",	T_ambientIntensity},
	{"appearance",			T_appearance},
	{"bboxCenter",			T_bboxCenter},
	{"bboxSize",			T_bboxSize},
	{"ccw",					T_ccw},
	{"center",				T_center},
	{"children",			T_children},
	{"color",				T_color},
	{"colorIndex",			T_colorIndex},
	{"colorPerVertex",		T_colorPerVertex},
	{"convex",				T_convex},
	{"coord",				T_coord},
	{"coordIndex",			T_coordIndex},
	{"creaseAngle",			T_creaseAngle},
	{"diffuseColor",		T_diffuseColor},
	{"description",			T_description},
	{"emissiveColor",		T_emissiveColor},
	{"geometry",			T_geometry},
	{"material",			T_material},
	{"normal",				T_normal},
	{"normalIndex",			T_normalIndex},
	{"normalPerVertex",		T_normalPerVertex},
	{"parameter",			T_parameter},
	{"point",				T_point},
	{"rotation",			T_rotation},
	{"shininess",			T_shininess},
	{"scale",				T_scale},
	{"scaleOrientation",	T_scaleOrientation},
	{"solid",				T_solid},
	{"specularColor",		T_specularColor},
	{"texCoord",			T_texCoord},
	{"texCoordIndex",		T_texCoordIndex},
	{"texture",				T_texture},
	{"textureTransform",	T_textureTransform},
	{"translation",			T_translation},
	{"transparency",		T_transparency},
	{"url",					T_url},
	{"vector",				T_vector},
	{"TRUE",				T_TRUE},
	{"FALSE",				T_FALSE},
	{"DEF",					T_DEF}
};

// count of known VRML 2.0 names
static const int tisn = sizeof(tis) / sizeof(tis[0]);

// lexical analyzer 
int yylex(void)
{
	int startPos;
	int endPos;
	int tokenLen;
	int i;
__repeat__:

	if (IsEOF()) return T_EOF;

	while (IsSpaceChar())
	{
		CheckNewLine();
		if (!GetNextChar()) return T_EOF;
	}

	if (buf[pos]=='#')
	{
		while (buf[pos]!='\n')
		{
			if (!GetNextChar()) return T_EOF;
		}	
		goto __repeat__;
	}

	if (IsTokenChar())
	{
		return buf[pos++];
	}

	if (buf[pos] == '"')
	{
		goto __start_string__;
	}

	startPos = pos;

	while (!IsSpaceChar() && !IsTokenChar())
	{
		pos++;
	}

	endPos = pos;

	tokenLen = min(endPos-startPos, 63);

	if (GetInteger(&buf[startPos], tokenLen, yylval.i))
	{
		return T_INTEGER;
	}

	if (GetFloat(&buf[startPos], tokenLen, yylval.f))
	{
		return T_FLOAT;
	}

	memcpy(&yylval.s, &buf[startPos], min(tokenLen,63));
	yylval.s[min(tokenLen,63)] = '\0';

	for (i=0; i<tisn; i++)
	{
		if (strcmp(yylval.s.d, tis[i].name)==0)
		{
			SetProgress(pos);
			return tis[i].id;
		}
	}

	return T_Unknown;

__start_string__:

	startPos = pos + 1;
	do
	{
		if (!GetNextChar()) return T_EOF;
		if ((buf[pos]=='\r') || (buf[pos]=='\n')) return T_ERROR;
	}
	while (buf[pos] != '"');

	endPos = pos;

	tokenLen = min(endPos-startPos, 63);
	memcpy(&yylval.s, &buf[startPos], min(tokenLen,63));
	yylval.s[min(tokenLen,63)] = '\0';

	pos++;
	SetProgress(pos);
	return T_STRING;
}

void yyerror(LPCSTR str)
{
	if (IsEOF())
	{
		MessageBox(NULL, "End of file unexpected", "VRML Parser", MB_OK|MB_ICONSTOP);
	}
	else
	{
		char buff[1024];
		int i = 0;

		while (currentLine[i]!=0)
		{
			if ((i>=128)||(currentLine[i]=='\r')||(currentLine[i]=='\n'))
			{
				currentLine[i] = 0;
				break;
			}
			i++;
		}
		
		sprintf(buff,"Error in file '%s' in line %d : \n'%s'\n'%s'", current_file, line, currentLine, str);

		MessageBox(NULL, buff, "VRML Parser", MB_OK|MB_ICONSTOP);
	}
}



////////////////////////////////////////////////////////////////////////////
//				ParseVRML
//parameters:
//	fileName		- file, containing data
//	geometrySkipped	- will be 'true', if unknown geometry appear
//	parserLog		- parser log
//return value:
//	true, if parsed OK
//description:
//	parse VRML data from file
bool ParseVRML(LPCSTR fileName, bool& geometrySkipped, CString& parserLog)
{
	HANDLE hf = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hf == INVALID_HANDLE_VALUE)
	{
		return false;
	}

	bufSize = GetFileSize(hf, NULL);

	if (bufSize <= 0)
	{
		CloseHandle(hf);
		return false;
	}

	buf = new char[bufSize+1];

	DWORD readed;
	BOOL readRes = ReadFile(hf, buf, bufSize, &readed, NULL);
	buf[bufSize] = '\0';

	CloseHandle(hf);

	if (!readRes)
	{
		delete buf;
		buf = NULL;
		return false;
	}

	strcpy(current_file, fileName);

	pos = 0;
	line = 1;
	currentLine = buf;

	logStr.Empty();
	skipNodes = false;
	int parseRes = yyparse();
	

	/* use here */
	
	/* clean */
	while (!rootNodesList.IsEmpty()) rootNodesList.RemoveTail();
	while (!allNodes.IsEmpty()) delete allNodes.RemoveTail();
	while (!allLists.IsEmpty()) delete allLists.RemoveTail();
	while (!allIntLists.IsEmpty()) delete allIntLists.RemoveTail();
	while (!allCoordLists.IsEmpty()) delete allCoordLists.RemoveTail();

	return (parseRes==0);
}

////////////////////////////////////////////////////////////////////////////
//				ParseVRML
//parameters:
//	fileName		- file, original data container
//	psg				- CSceneGraph object, to be filled
//	geometrySkipped	- will be 'true', if unknown geometry appear
//	parserLog		- parser log
//return value:
//	true, if parsed OK
//description:
//	parse VRML data from memory buffer
bool ParseVRML(LPSTR buff, DWORD buffSize, LPCSTR fileName, CSceneGraph* psg)
{

	bufSize = buffSize;
	buf = buff;
	pos = 0;
	line = 1;
	currentLine = buf;

	strcpy(current_file, fileName);

	BeginProgress(bufSize, "Parsing, please wait");
	int parseRes = yyparse();
	StopProgress("Parsing done");

	if (parseRes==0)
	{
		POSITION pos = rootNodesList.GetHeadPosition();
		while (pos)
		{
			CsgnNode* pNode = rootNodesList.GetNext(pos)->Build();
			if (pNode)
			{
				psg->AddTail(pNode);
			}
		}
		psg->SetRestored(false);
	}

	while (!rootNodesList.IsEmpty()) rootNodesList.RemoveTail();
	while (!allNodes.IsEmpty()) delete allNodes.RemoveTail();
	while (!allLists.IsEmpty()) delete allLists.RemoveTail();
	while (!allIntLists.IsEmpty()) delete allIntLists.RemoveTail();
	while (!allCoordLists.IsEmpty()) delete allCoordLists.RemoveTail();

	return (parseRes==0);
}



////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////     parser callback functions ////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////


// return pointer to root object
_pList CreateRootNodesList()
{
	TRACE("CreateRootNodesList()\n");
	return &rootNodesList;
}
// append _Node object into _List object
void AppendNode(_pList pList, _pNode pNode)
{
	if (pNode!=NULL)
	{
		TRACE("AppendNode()\n");
		pList->AddTail(pNode);
	}
}
// show warning if root list at the end is empty
void WarningOnEmptyRootList()
{
	TRACE("WarningOnEmptyRootList()\n");
}
// create _List object, return pointer to it
_pList CreateNodesList()
{
	TRACE("CreateNodesList()\n");
	_pList pList = new _List;
	allLists.AddTail(pList);
	return pList;
}
// show warning if non-root list at the end is empty
void WarningOnEmptyList()
{
	TRACE("WarningOnEmptyRootList()\n");
}
// create _Anchor object, return pointer to it
_pNode CreateAnchor(S_Anchor a)
{
	TRACE("CreateAnchor()\n");
	_Anchor* pNode = new _Anchor;
	pNode->a = a;
	allNodes.AddTail(pNode);
	return pNode;
}
// create _Coordinate object, return pointer to it
_pNode CreateCoordinate(S_Coordinate c)
{
	TRACE("CreateCoordinate()\n");
	_Coordinate* pNode = new _Coordinate;
	pNode->c = c;
	allNodes.AddTail(pNode);
	return pNode;
}
// create _Group object, return pointer to it
_pNode CreateGroup(S_Group g)
{
	TRACE("CreateCoordinate()\n");
	_Group* pNode = new _Group;
	pNode->g = g;
	allNodes.AddTail(pNode);
	return pNode;
}
// create _IndexedFaceSet object, return pointer to it
_pNode CreateIndexedFaceSet(S_IndexedFaceSet i)
{
	TRACE("CreateIndexedFaceSet()\n");
	_IndexedFaceSet* pNode = new _IndexedFaceSet;
	pNode->i = i;
	allNodes.AddTail(pNode);
	return pNode;
}
// create _Normal object, return pointer to it
_pNode CreateNormal(S_Normal n)
{
	TRACE("CreateNormal()\n");
	_Normal* pNode = new _Normal;
	pNode->n = n;
	allNodes.AddTail(pNode);
	return pNode;
}
// create _Shape object, return pointer to it
_pNode CreateShape(S_Shape s)
{
	TRACE("CreateShape()\n");
	_Shape* pNode = new _Shape;
	pNode->s = s;
	allNodes.AddTail(pNode);
	return pNode;
}
// create _Transform object, return pointer to it
_pNode CreateTransform(S_Transform t)
{
	TRACE("CreateTransform()\n");
	_Transform* pNode = new _Transform;
	pNode->t = t;
	allNodes.AddTail(pNode);
	return pNode;
}

// this function is related to lexic analyser
// skip data between { } brackets
void SkipNode(_str64 nodeName)
{
	TRACE("SkipNode(%s)\n", (char*) nodeName);

	CString str;
	str.Format("Node '%s' was skipped (line %d)\r\n", (char*)nodeName, line);
	logStr = logStr + str;
	skipNodes = true;

	int count = 0;

	while (count==0)
	{
		if (buf[pos]=='{') count++;
		CheckNewLine();
		if (!GetNextChar()) return;
	}

	while (count!=0)
	{
		if (buf[pos]=='{') count++;
		else if (buf[pos]=='}') count--;
		CheckNewLine();
		if (!GetNextChar()) return;
	}
}
// set name to the _Node object
void SetNodeName(_pNode pNode, _str64 nodeName)
{
	TRACE("SetNodeName()\n");
	pNode->name = nodeName;
}
// create _CoordList object, return pointer to it
_pCoordList CreateCoordList()
{
	TRACE("CreateCoordList()\n");
	_pCoordList pList = new _CoordList;
	allCoordLists.AddTail(pList);
	return pList;
}
// delete _CoordList object
void DestroyCoordList(_pCoordList pList)
{
	TRACE("DestroyCoordList()\n");
//	delete pList;
}
// append int value into _CoordList object
void AppendToCoordList(_pCoordList pList, _float3 data)
{
//	TRACE("AppendToCoordList()\n");
	pList->AddTail(data);
}
// create _IntList object, return pointer to it
_pIntList CreateIntList()
{
	TRACE("CreateIntList()\n");
	_pIntList pList = new _IntList;
	allIntLists.AddTail(pList);
	return pList;
}
// delete _IntList object
void DestroyIntList(_pIntList pList)
{
	TRACE("DestroyIntList()\n");
//	delete pList;
}
// append int value into _IntList object
void AppendToIntList(_pIntList pList, int data)
{
//	TRACE("AppendToIntList()\n");
	pList->AddTail(data);
}

// _Node default constructor
_Node::_Node()
{
	memset(name, 0, 64);
}

// This functions trace data

void _Anchor::Trace()
{
	TRACE("Anchor '%s' {\n", (char*)name);

	if (a.fields & S_Anchor_description)
	{
		TRACE("description = '%s'\n", (char*)a.description);
	}

	if (a.fields & S_Anchor_children)
	{
		POSITION pos = a.children->GetHeadPosition();
		while (pos) a.children->GetNext(pos)->Trace();
	}
	else
	{
		TRACE("EMPTY");
	}

	TRACE("} //Anchor '%s'\n", (char*)name);
}
void _Coordinate::Trace()
{
	TRACE("Coordinate '%s' {\n", (char*)name);

	TRACE("} //Coordinate '%s'\n", (char*)name);
}
void _Group::Trace()
{
	TRACE("Group '%s' {\n", (char*)name);

	if (g.fields & S_Group_children)
	{
		POSITION pos = g.children->GetHeadPosition();
		while (pos) g.children->GetNext(pos)->Trace();
	}
	else
	{
		TRACE("EMPTY");
	}

	TRACE("} //Group '%s'\n", (char*)name);
}
void _IndexedFaceSet::Trace()
{
	TRACE("IndexedFaceSet '%s' {\n", (char*)name);

	TRACE("} //IndexedFaceSet '%s'\n", (char*)name);
}
void _Normal::Trace()
{
	TRACE("Normal '%s' {\n", (char*)name);

	TRACE("} //Normal '%s'\n", (char*)name);
}
void _Transform::Trace()
{
	TRACE("Transform '%s' {\n", (char*)name);

	if (t.fields & S_Transform_children)
	{
		POSITION pos = t.children->GetHeadPosition();
		while (pos) t.children->GetNext(pos)->Trace();
	}
	else
	{
		TRACE("EMPTY");
	}

	TRACE("} //Transform '%s'\n", (char*)name);
}
void _Shape::Trace()
{
	TRACE("Shape '%s' {\n", (char*)name);

	TRACE("} //Shape '%s'\n", (char*)name);
}



