/*
								+----------------------------------+
								|                                  |
								| ***  ÜLame modelling script  *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|             Scene.h              |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __UBERLAME_MODELLING_SCRIPT_INCLUDED
#define __UBERLAME_MODELLING_SCRIPT_INCLUDED

/**
 *	@file dev/Scene.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief ÜLame modelling script
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2009-10-11
 *
 *	replaced stl container ::resize() by stl_ut::Resize_*() to avoid unhandled
 *	std::bad_alloc
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

#include "../Vector.h"
#include "PolyMesh.h"
#include "GeoSphere.h"
#include "PlatPrim.h"
#include "TexCoords.h"
#include "Spline.h"
#include "BezierCubic.h"
#include "SplineSurf.h"

/*
 *	class CScriptModeller
 *		- simple scene loader, constructing scenes according to loaded script
 */
class CScriptModeller {
public:
	/*
	 *	CScriptModeller::<unnamed_0>
	 *		- node class names
	 */
	enum {
		node_MatList,
			node_Material,
				type_Resource,
				type_Emission,
				type_Ambient,
				type_Diffuse,
				type_Specular,
				type_Shininess,
				type_Reflectance,
		node_Geometry,
			node_PlatPrim,
				type_GeoSphere,
				type_Sphere,
				type_Box,
				type_Plane,
				type_Cylinder,
				type_Cone,
				type_Tube,
				type_Torus,
			node_Surface,
				type_ExtrudeSurf,
				type_RuledSurf,
				type_RevolutionSurf,
				type_RailSweepSurf,
				type_RailSweep2Surf,
					type_CBSpline,
			type_Color,
			type_MaterialId,
		node_Transform,
			type_Offset,
			type_Scale,
			type_Rotate,
		node_UVMapping,
		node_GeomModifier
	};

	/*
	 *	struct CScriptModeller::TMaterialNode
	 *		- material definition node
	 */
	struct TMaterialNode {
		int n_id;
		std::string s_resource; // can point to jpeg file or to material file
		Vector4f v_emission, v_ambient, v_diffuse, v_specular;
		float f_shininess, f_reflectance;

		/*
		 *	CScriptModeller::TMaterialNode::TMaterialNode(int _n_id = -1)
		 *		- default constructor; creates material with if _n_id
		 *		  and default colors (defaults similar to OpenGL material
		 *		  params defaults)
		 */
		TMaterialNode(int _n_id = -1);
	};

	/*
	 *	struct CScriptModeller::TGeometryNode
	 *		- polygonal object node; contains object name, mesh and transformation matrix
	 */
	struct TGeometryNode {
		std::string s_name;
		CPolyMesh t_mesh;
		Matrix4f t_transform;

		/*
		 *	CScriptModeller::TGeometryNode::TGeometryNode()
		 *		- default constructor; sets t_transform to be identity matrix
		 */
		TGeometryNode();

		/*
		 *	void CScriptModeller::TGeometryNode::Swap(TGeometryNode &r_node)
		 *		- swaps contents of TGeometryNode and this
		 */
		void Swap(TGeometryNode &r_node);
	};

	/*
	 *	struct CScriptModeller::TScene
	 *		- scene node; contains materials and objects
	 *		- future extensions: lights, particle systems, cameras, animation tracks
	 */
	struct TScene {
		std::vector<TMaterialNode> material_list;
		std::vector<TGeometryNode> object_list;
	};

	/*
	 *	class CScriptModeller::CNodeInterface
	 *		- interface for node construction, allowing for loader extensions in run-time
	 */
	class CNodeInterface {
	public:
		/*
		 *	virtual bool CScriptModeller::CNodeInterface::b_IsNodeComplete() const
		 *		- returns true if node is complete, otherwise returns false
		 *		- note by defaul all nodes are complete on line of their definition
		 *		  so this always returns true
		 */
		virtual bool b_IsNodeComplete() const;

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::FinishNode()
		 *		- finishes node; called upon '}' character
		 *		- returns true on success, false on failuire
		 *		- note by default no node needs finalization so this always returns true
		 */
		virtual bool FinishNode();

		/*
		 *	- those functions adds (fully constructed) subnode to the current node
		 *	- default node can't have any subnodes so all these functions fail;
		 *	  have to override if necessary
		 */

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::AddMaterial(
		 *		const TMaterialNode &r_t_material)
		 *		- adds material definition r_t_material to the current node
		 *		- returns true on success (node can accept material definition
		 *		  and no error(s) occured), false on failuire
		 *		- note default node can't have any subnodes so
		 *		  this function always returns false
		 */
		virtual bool AddMaterial(const TMaterialNode &r_t_material);

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::AddGeometry(TGeometryNode &r_t_mesh_node)
		 *		- adds geometry node r_t_mesh_node to the current node
		 *		  (use TGeometryNode::Swap() to avoid copying meshes)
		 *		- returns true on success (node can accept polygonal object
		 *		  and no error(s) occured), false on failuire
		 *		- note default node can't have any subnodes so
		 *		  this function always returns false
		 */
		virtual bool AddGeometry(TGeometryNode &r_t_mesh_node);

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::PushTransform(
		 *		const Matrix4f &r_t_transform)
		 *		- adds polygonal object r_t_mesh to the current node (use CPolyMesh::Swap()
		 *		  to avoid copying meshes)
		 *		- returns true on success (node can accept transformation data
		 *		  and no error(s) occured), false on failuire
		 *		- note default node can't have any subnodes so
		 *		  this function always returns false
		 */
		virtual bool PushTransform(const Matrix4f &r_t_transform);

		/*
		 *	virtual CPolyMesh *CScriptModeller::CNodeInterface::p_Geometry()
		 *		- returns pointer to node geometry (this is intended to be used
		 *		  for adding more geometry to mesh node and by texcoord generators
		 *		  or mesh modifiers)
		 *		- returns 0 if node doesn't contain geometry
		 *		- note default node doesn't contain geometry so
		 *		  this function always returns 0
		 */
		virtual CPolyMesh *p_Geometry();

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::SetFloatv(int n_name,
		 *		int n_size, const float *p_value)
		 *		- sets array of n_size float values p_value with name n_name
		 *		- returns true on success (node can accept float data with name n_name
		 *		  and no error(s) occured), false on failuire
		 *		- note default node doesn't accept any data so
		 *		  this function always returns false
		 */
		virtual bool SetFloatv(int n_name, int n_size, const float *p_value);

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::SetIntegerv(int n_name,
		 *		int n_size, const int *p_value)
		 *		- sets array of n_size integer values p_value with name n_name
		 *		- returns true on success (node can accept integer data with name n_name
		 *		  and no error(s) occured), false on failuire
		 *		- note default node doesn't accept any data so
		 *		  this function always returns false
		 */
		virtual bool SetIntegerv(int n_name, int n_size, const int *p_value);

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::SetStringv(int n_name,
		 *		std::string &r_s_value)
		 *		- sets string value r_s_value (it's recommended to use std::string::swap()
		 *		  instead of copying the string) with name n_name
		 *		- returns true on success (node can accept string data with name n_name
		 *		  and no error(s) occured), false on failuire
		 *		- note default node doesn't accept any data so
		 *		  this function always returns false
		 */
		virtual bool SetStringv(int n_name, std::string &r_s_value);

		/*
		 *	virtual bool CScriptModeller::CNodeInterface::SetObject(int n_name,
		 *		int n_size, void *p_pointer)
		 *		- sets object passed as pointer p_pointer with name n_name and size n_size
		 *		- returns true on success (node can accept such and object
		 *		  and no error(s) occured), false on failuire
		 *		- note default node doesn't accept any objects so
		 *		  this function always returns false
		 *		- note in case this function fails, it's calleer's responsibility
		 *		  to delete object (in case it's allocated on heap, that is)
		 */
		virtual bool SetObject(int n_name, int n_size, void *p_pointer);
	};

	/*
	 *	class CScriptModeller::CUtilParser
	 *		- parser for basic data types
	 */
	class CUtilParser {
	public:
		/*
		 *	static bool CScriptModeller::CUtilParser::ParseColor(CNodeInterface &r_parent_iface,
		 *		const char *p_s_node_name, std::string &r_s_line, int n_type)
		 *		- parses RGB(A) color, passes to r_parent_iface using SetFloatv with name n_type
		 *		- returns true on success, false on failure
		 */
		static bool ParseColor(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);

		/*
		 *	static bool CScriptModeller::CUtilParser::ParseQuotedString(
		 *		CNodeInterface &r_parent_iface, const char *p_s_node_name,
		 *		std::string &r_s_line, int n_type)
		 *		- parses quoted string, passes to r_parent_iface using SetStringv with name n_type
		 *		- returns true on success, false on failure
		 */
		static bool ParseQuotedString(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);

		/*
		 *	static bool CScriptModeller::CUtilParser::ParseFloat(CNodeInterface &r_parent_iface,
		 *		const char *p_s_node_name, std::string &r_s_line, int n_type)
		 *		- parses float literal, passes to r_parent_iface using SetFloatv with name n_type
		 *		- returns true on success, false on failure
		 */
		static bool ParseFloat(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);

		/*
		 *	static bool CScriptModeller::CUtilParser::ParseInteger(CNodeInterface &r_parent_iface,
		 *		const char *p_s_node_name, std::string &r_s_line, int n_type)
		 *		- parses int literal, passes to r_parent_iface using SetIntegerv with name n_type
		 *		- returns true on success, false on failure
		 */
		static bool ParseInteger(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);
	};

	/*
	 *	class CScriptModeller::CTransformParser
	 *		- parser for transformation nodes (offset, scaling, rotation)
	 */
	class CTransformParser {
	public:
		/*
		 *	static bool CScriptModeller::CTransformParser::ParseOffset(
		 *		CNodeInterface &r_parent_iface, const char *p_s_node_name,
		 *		std::string &r_s_line, int n_type)
		 *		- parses offset transform node, creates transformation
		 *		  matrix and pushes it to r_parent_iface transform
		 *		- returns true on success, false on failure
		 */
		static bool ParseOffset(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);

		/*
		 *	static bool CScriptModeller::CTransformParser::ParseScale(
		 *		CNodeInterface &r_parent_iface, const char *p_s_node_name,
		 *		std::string &r_s_line, int n_type)
		 *		- parses scaling transform node, creates transformation
		 *		  matrix and pushes it to r_parent_iface transform
		 *		- returns true on success, false on failure
		 */
		static bool ParseScale(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);

		/*
		 *	static bool CScriptModeller::CTransformParser::ParseRotate(
		 *		CNodeInterface &r_parent_iface, const char *p_s_node_name,
		 *		std::string &r_s_line, int n_type)
		 *		- parses rotation transform node, creates transformation
		 *		  matrix and pushes it to r_parent_iface transform
		 *		- returns true on success, false on failure
		 */
		static bool ParseRotate(CNodeInterface &r_parent_iface,
			const char *p_s_node_name, std::string &r_s_line, int n_type);
	};

	/*
	 *	class CScriptModeller::CPolyMeshUtil
	 *		- utility function for CPolyMesh
	 */
	class CPolyMeshUtil {
	protected:
		class CApplyTransform;
		class CSetVertexColor; // utility classes

	public:
		/*
		 *	static void CScriptModeller::CPolyMeshUtil::Apply_Transform(
		 *		CPolyMesh &r_t_mesh, const Matrix4f &r_t_transform)
		 *		- applies transformation described by matrix r_t_transform
		 *		  to all vertices of r_t_mesh
		 *		- note normals are transformed as well, using inverse-transpose of r_t_transform
		 */
		static void Apply_Transform(CPolyMesh &r_t_mesh, const Matrix4f &r_t_transform);

		/*
		 *	static void CScriptModeller::CPolyMeshUtil::Set_VertexColor(
		 *		CPolyMesh &r_t_mesh, Vector4f v_color)
		 *		- sets color of all vertices of r_t_mesh to v_color
		 */
		static void Set_VertexColor(CPolyMesh &r_t_mesh, Vector4f v_color);

		/*
		 *	static void CScriptModeller::CPolyMeshUtil::Set_MaterialId(
		 *		CPolyMesh &r_t_mesh, int n_material_id)
		 *		- sets material id of all polygons of r_t_mesh to n_material_id
		 */
		static void Set_MaterialId(CPolyMesh &r_t_mesh, int n_material_id);
	};

protected:
	class CSceneIface;
	class CMatListIface;
	class CMaterialIface;
	class CGeometryIface;
	class CPlatPrimIface;
	class CSurfaceIface;
	class CExtrudeSurfIface;
	class CRuledSurfIface;
	class CRevolSurfIface;
	class CRailSweepSurfIface;
	class CUVMapIface;
	// interface classes

	typedef bool (*TParseFuncPtr)(CNodeInterface &r_parent_iface,
		const char *p_s_node_name, std::string &r_s_line, int n_type);
	typedef CNodeInterface *(*TIfaceSpawnFuncPtr)(CNodeInterface &r_parent_iface,
		const char *p_s_node_name, std::string &r_s_line, int n_type);
	struct TNodeHandler {
		const char *p_s_name;
		int n_type; // translation of name
		int n_class; // one of node_*
		TParseFuncPtr p_parse_func; // for nodes that don't have subnodes
		TIfaceSpawnFuncPtr p_iface_spawn_func; // for nodes with subnodes
	};
	static const TNodeHandler m_p_handler_list[];
	// node handlers

	class CReader {
	public:
		virtual bool b_Error() const = 0;
		virtual bool b_EOF() const = 0;
		virtual int n_GetChar() = 0;
	};

	class CStringReader : public CReader {
	protected:
		const char *m_p_s_str, *m_p_s_end;

	public:
		CStringReader(const char *p_s_str)
			:m_p_s_str(p_s_str), m_p_s_end(p_s_str + strlen(p_s_str))
		{}

		virtual bool b_Error() const
		{
			return false;
		}

		virtual bool b_EOF() const
		{
			return m_p_s_str == m_p_s_end;
		}

		virtual int n_GetChar()
		{
			return (m_p_s_str == m_p_s_end)? EOF : *m_p_s_str ++;
		}
	};

	class CFileReader : public CReader {
	protected:
		FILE *m_p_fr;

	public:
		CFileReader(const char *p_s_file_name)
		{
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			if(fopen_s(&m_p_fr, p_s_file_name, "r"))
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			if(!(m_p_fr = fopen(p_s_file_name, "r")))
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
				m_p_fr = 0; // to mark error
		}

		virtual ~CFileReader()
		{
			if(m_p_fr)
				fclose(m_p_fr);
		}

		virtual bool b_Error() const
		{
			return ferror(m_p_fr) != 0;
		}

		virtual bool b_EOF() const
		{
			return feof(m_p_fr) != 0;
		}

		virtual int n_GetChar()
		{
			return fgetc(m_p_fr);
		}
	};

	CReader *m_p_reader;
	int m_n_cur_line;
	std::string m_s_line;
	TScene *m_p_scene;
	std::vector<CNodeInterface*> m_node_stack;

public:
	/*
	 *	CScriptModeller::CScriptModeller()
	 *		- default constructor; clears line counter
	 */
	CScriptModeller();

	/*
	 *	TScene *CScriptModeller::p_LoadScene(const char *p_s_file_name)
	 *		- loads scene from file p_s_file_name
	 *		- returns pointer to loaded scene on success or 0 on failure
	 */
	TScene *p_LoadScene(const char *p_s_file_name);

	/*
	 *	TScene *CScriptModeller::p_LoadScene(const char *p_s_file_name)
	 *		- loads scene from the given string
	 *		- returns pointer to loaded scene on success or 0 on failure
	 */
	TScene *p_LoadScene2(const char *p_s_scene_string);

	/*
	 *	int CScriptModeller::n_Last_Line() const
	 *		- returns number of line (zero-based index) where parser stopped,
	 *		  useful for fixing errors in scene files
	 */
	int n_Last_Line() const;

	/*
	 *	const std::string &CScriptModeller::s_Last_Line() const
	 *		- returns contents of line where parser stopped
	 *		- note whitespace from line beginning and end is stripped of
	 */
	const std::string &s_Last_Line() const;

	/*
	 *	static bool CScriptModeller::GetInteger(int &r_n_result, std::string &r_s_string)
	 *		- utility function; parses integer value from r_s_string,
	 *		  places it to r_n_result and erases it from the string
	 *		  (including whitespace surrounding it)
	 *		- returns true on success, false on failure
	 */
	static bool GetInteger(int &r_n_result, std::string &r_s_string);

	/*
	 *	static bool CScriptModeller::GetFloat(float &r_f_result, std::string &r_s_string)
	 *		- utility function; parses float value from r_s_string,
	 *		  places it to r_f_result and erases it from the string
	 *		  (including whitespace surrounding it)
	 *		- returns true on success, false on failure
	 */
	static bool GetFloat(float &r_f_result, std::string &r_s_string);

	/*
	 *	static bool CScriptModeller::GetIdent(std::string &r_s_result, std::string &r_s_string)
	 *		- utility function; parses c/c++ identifier from r_s_string,
	 *		  places it to r_s_result and erases it from the string
	 *		  (including whitespace surrounding it)
	 *		- returns true on success, false on failure
	 */
	static bool GetIdent(std::string &r_s_result, std::string &r_s_string);

	/*
	 *	static bool CScriptModeller::GetQuotedString(std::string &r_s_result,
	 *		std::string &r_s_string)
	 *		- utility function; parses apostrophe quoted string literal
	 *		  from r_s_string, places it to r_s_result and erases it from
	 *		  the string (including whitespace surrounding it)
	 *		- note this doesn't translate escape sequences, \' is illegal (it ends the string)
	 *		- returns true on success, false on failure
	 */
	static bool GetQuotedString(std::string &r_s_result, std::string &r_s_string);

protected:
	static inline void DeleteNodeIface(CNodeInterface *p_iface);
	static void TrimSpace(std::string &r_s_string);
	const TNodeHandler *p_NodeHandler(const std::string &r_s_node) const;
	bool Parse();
	bool ReadLine();
};

#endif // __UBERLAME_MODELLING_SCRIPT_INCLUDED
