//
// ASCII scene .MAS loader
//


#ifndef __MASLOADERC_H__
#define __MASLOADERC_H__

#include "PajaTypes.h"
#include "ColorC.h"
#include "DemoInterfaceC.h"
#include "ImportableImageI.h"
#include "ScenegraphItemI.h"
#include "ContVector3C.h"
#include "ContQuatC.h"
#include "ContFloatC.h"
#include "CameraC.h"
#include "LightC.h"
#include "MeshC.h"
#include "BoneC.h"
#include "ShapeC.h"
#include "ParticleSystemC.h"

// stl
#include <vector>
#include <string>



class MASLoaderC
{
public:
	MASLoaderC();
	virtual ~MASLoaderC();

	bool				load( const char* szName, PajaSystem::DemoInterfaceC* pInterface );

	void				get_scenegraph( std::vector<ScenegraphItemI*>& rScenegraph );
	void				get_lights( std::vector<LightC*>& rLights );
	void				get_cameras( std::vector<CameraC*>& rCameras );

	void				get_time_parameters( PajaTypes::int32& i32FPS, PajaTypes::int32& i32TicksPerFrame, PajaTypes::int32& i32FirstFrame, PajaTypes::int32& i32LastFrame );


private:

	//
	// mesh decomposition
	//

	struct MASTexChannelS {
		std::vector<PajaTypes::Vector2C>	m_rCoords;
	};

	struct MASEdgeS {
		PajaTypes::uint32	m_ui32VA, m_ui32VB;
		PajaTypes::uint32	m_ui32FA, m_ui32FB;
		bool				m_bEdgeFlag;
		bool				m_bCuspEdge;
	};

	class MASFaceC {
	public:
		MASFaceC() :
			m_ui32EA( 0 ), m_ui32EB( 0 ), m_ui32EC( 0 ),
			m_ui8EfA( 0 ), m_ui8EfB( 0 ), m_ui8EfC( 0 ),
			m_ui32UA( 0 ), m_ui32UB( 0 ), m_ui32UC( 0 ),
			m_ui32VA( 0 ), m_ui32VB( 0 ), m_ui32VC( 0 ),
			m_ui32NA( 0 ), m_ui32NB( 0 ), m_ui32NC( 0 ),
			m_ui32Edge( 0 ), m_ui32Smooth( 0 ), m_ui32Mtl( 0 )
		{};
		virtual ~MASFaceC() {};

		void	set_texindex( PajaTypes::uint32 ui32A, PajaTypes::uint32 ui32B, PajaTypes::uint32 ui32C, PajaTypes::uint32 ui32Ch )
		{
			// enlagre the arrays if they are too small.
			while( ui32Ch >= m_rTA.size() ) {
				m_rTA.push_back( 0 );
				m_rTB.push_back( 0 );
				m_rTC.push_back( 0 );
			}

			m_rTA[ui32Ch] = ui32A;
			m_rTB[ui32Ch] = ui32B;
			m_rTC[ui32Ch] = ui32C;
		}

		PajaTypes::Vector3C	m_rNorm;
		PajaTypes::uint32	m_ui32EA, m_ui32EB, m_ui32EC;
		PajaTypes::uint8	m_ui8EfA, m_ui8EfB, m_ui8EfC;
		PajaTypes::uint32	m_ui32UA, m_ui32UB, m_ui32UC;
		PajaTypes::uint32	m_ui32VA, m_ui32VB, m_ui32VC;
		PajaTypes::uint32	m_ui32NA, m_ui32NB, m_ui32NC;
		PajaTypes::uint32	m_ui32Edge, m_ui32Smooth, m_ui32Mtl;
		std::vector<PajaTypes::uint32>	m_rTA;
		std::vector<PajaTypes::uint32>	m_rTB;
		std::vector<PajaTypes::uint32>	m_rTC;
	};

	struct MASVertUsageS {
		PajaTypes::uint32				m_ui32N;
		std::vector<PajaTypes::uint32>	m_rT;
	};

	struct MASBoneWeightS {
		PajaTypes::uint32				m_ui32Vert;
		PajaTypes::float32				m_f32Weight;
		PajaTypes::uint32				m_ui32Bone;
	};

	struct MASBoneWeightListS {
		std::string						m_sBoneName;
		std::vector<PajaTypes::float32>	m_rWeights;
		std::vector<PajaTypes::uint32>	m_rIndices;
		PajaTypes::Matrix3C				m_rBaseTM;
	};
	
	class MASVertexC {
	public:

		MASVertexC() {};
		virtual ~MASVertexC() {};

		PajaTypes::uint32 add_norm( const PajaTypes::Vector3C& v, PajaTypes::uint32 ui32Smooth )
		{
			for( PajaTypes::uint32 i = 0; i < m_rGroups.size(); i++ ) {
				if( m_rGroups[i] & ui32Smooth ) {
					m_rNorms[i] = m_rNorms[i] + v;
					m_rGroups[i] |= ui32Smooth;
					return i;
				}
			}

			m_rNorms.push_back( v );
			m_rGroups.push_back( ui32Smooth );
			return m_rNorms.size() - 1;
		}

		PajaTypes::uint32	add_texcoord( const PajaTypes::Vector2C& rCoord, PajaTypes::uint32 ui32Ch )
		{
			// enlarge the array if it is too small.
			while( ui32Ch >= m_rTexChannels.size() ) {
				MASTexChannelS	rCh;
				m_rTexChannels.push_back( rCh );
			}

			// kill duplicates
			for( PajaTypes::uint32 i = 0; i < m_rTexChannels[ui32Ch].m_rCoords.size(); i++ ) {
				if( m_rTexChannels[ui32Ch].m_rCoords[i] == rCoord )
					return i;
			}

			// no matching coord was found, add new one.
			m_rTexChannels[ui32Ch].m_rCoords.push_back( rCoord );
			return m_rTexChannels[ui32Ch].m_rCoords.size() - 1;
		}

		PajaTypes::uint32	add_usage( PajaTypes::uint32 ui32NIdx, std::vector<PajaTypes::uint32>& rTIdx )
		{
			PajaTypes::int32	i32Count = 0;

			// check if similar usage is found
			for( PajaTypes::uint32 i = 0; i < m_rVertUsage.size(); i++ ) {
				i32Count = 1 + rTIdx.size();

				if( ui32NIdx == m_rVertUsage[i].m_ui32N )
					i32Count--;

				for( PajaTypes::uint32 j = 0; j < rTIdx.size(); j++ )
					if( rTIdx[j] == m_rVertUsage[i].m_rT[j] )
						i32Count--;

				if( i32Count == 0 )
					return i;
			}

			// nope, add new usage
			MASVertUsageS	rUsage;

			rUsage.m_ui32N = ui32NIdx;
			for( PajaTypes::uint32 j = 0; j < rTIdx.size(); j++ )
				rUsage.m_rT.push_back( rTIdx[j] );

			m_rVertUsage.push_back( rUsage );

			return m_rVertUsage.size() - 1;
		}

		PajaTypes::Vector3C					m_rVert;
		std::vector<PajaTypes::Vector3C>	m_rNorms;
		std::vector<PajaTypes::uint32>		m_rGroups;
		PajaTypes::uint32					m_ui32BaseIdx;
		std::vector<MASTexChannelS>			m_rTexChannels;
		std::vector<MASVertUsageS>			m_rVertUsage;
		PajaTypes::float32					m_f32Weigth;
	};


	struct MASMorphChannelS {
		MASMorphChannelS() :
			m_bUseLimits( false ),
			m_f32Min( 0 ),
			m_f32Max( 100 ),
			m_pPercentageController( 0 )
		{
		};

		~MASMorphChannelS() {
			delete m_pPercentageController;
		};

		bool						m_bUseLimits;
		PajaTypes::float32			m_f32Min, m_f32Max;
		PajaTypes::float32			m_f32Percentage;
		std::vector<MASVertexC>		m_rVertices;
		ContFloatC*					m_pPercentageController;
	};

	struct MASMeshS {

		MASMeshS() :
			m_bAdditive( false ),
			m_bToon( false ),
			m_bAlphaTest( false ),
			m_bCastShadows( true )
		{
		};

		~MASMeshS() {
			for( PajaTypes::uint32 i = 0; i < m_rMorphChannels.size(); i++ )
				delete m_rMorphChannels[i];
		}

		PajaTypes::uint32	add_edge( PajaTypes::uint32 ui32A, PajaTypes::uint32 ui32B, PajaTypes::uint32 ui32Face )
		{
			PajaTypes::uint32	i;

			// find existing edge
			for( i = 0; i < m_rEdges.size(); i++ ) {
				if( (m_rEdges[i].m_ui32VA == ui32A && m_rEdges[i].m_ui32VB == ui32B) ||
					(m_rEdges[i].m_ui32VA == ui32B && m_rEdges[i].m_ui32VB == ui32A) ) {
					m_rEdges[i].m_ui32FB = ui32Face;
					return i;
				}
			}

			// not found => add new
			MASEdgeS	rEdge;
			rEdge.m_ui32VA = ui32A;
			rEdge.m_ui32VB = ui32B;
			rEdge.m_ui32FA = ui32Face;
			rEdge.m_ui32FB = 0xffffffff;
			rEdge.m_bEdgeFlag = false;
			rEdge.m_bCuspEdge = false;
			m_rEdges.push_back( rEdge );

			return m_rEdges.size() - 1;
		};

		bool								m_bAdditive;
		bool								m_bToon;
		bool								m_bAlphaTest;
		bool								m_bCastShadows;
		std::vector<MASEdgeS>				m_rEdges;
		std::vector<MASFaceC>				m_rFaces;
		std::vector<MASVertexC>				m_rVertices;
		std::vector<MASMorphChannelS*>		m_rMorphChannels;
		std::vector<MASBoneWeightS>			m_rBoneWeights;		// v 3.10 <=
		std::vector<MASBoneWeightListS>		m_rBoneWeighLists;	// v 3.20 >
		PajaTypes::Matrix3C					m_rSkinBaseTM;		// v 3.20 >
	};

	struct MASIndexListS {
		IndexListC*							m_pList;
		PajaTypes::uint32					m_ui32MtlIndex;
	};

	//
	// material
	//

	struct MASMaterialS
	{
		MASMaterialS() :
			m_f32Shininess( 0 ),
			m_f32Transparency( 0 ),
			m_bTwoSided( false ),
			m_bAdditive( false ),
			m_f32SelfIllum( 0 ),
			m_ui32DiffuseMappingType( 0 ),
			m_ui32DiffuseTexChannel( 0 ),
			m_ui32ReflectionMappingType( 0 ),
			m_ui32ReflectionTexChannel( 0 ),
			m_ui32LightmapMappingType( 0 ),
			m_ui32LightmapTexChannel( 0 ),
			m_pTransparencyCont( 0 ),
			m_pSelfIllumCont( 0 ),
			m_pDiffuseCont( 0 ),
			m_pSpecularCont( 0 )
		{
			// empty
		};

		~MASMaterialS() {
			PajaTypes::uint32	i;
			for( i = 0 ; i < m_rSubMaterials.size(); i++ )
				delete m_rSubMaterials[i];

			delete m_pTransparencyCont;
			delete m_pSelfIllumCont;
			delete m_pDiffuseCont;
			delete m_pSpecularCont;
		}

		std::string					m_sDiffuseTexName;
		PajaTypes::uint32			m_ui32DiffuseMappingType;
		PajaTypes::uint32			m_ui32DiffuseTexChannel;

		std::string					m_sReflectionTexName;
		PajaTypes::uint32			m_ui32ReflectionMappingType;
		PajaTypes::uint32			m_ui32ReflectionTexChannel;

		std::string					m_sLightmapTexName;
		PajaTypes::uint32			m_ui32LightmapMappingType;
		PajaTypes::uint32			m_ui32LightmapTexChannel;

		PajaTypes::ColorC			m_rAmbient;
		PajaTypes::ColorC			m_rDiffuse;
		PajaTypes::ColorC			m_rSpecular;
		PajaTypes::float32			m_f32Shininess;
		PajaTypes::float32			m_f32Transparency;
		bool						m_bTwoSided;
		bool						m_bAdditive;
		PajaTypes::float32			m_f32SelfIllum;
		std::vector<MASMaterialS*>	m_rSubMaterials;

		ContFloatC*					m_pTransparencyCont;
		ContFloatC*					m_pSelfIllumCont;
		ContVector3C*				m_pDiffuseCont;
		ContVector3C*				m_pSpecularCont;
	};

	void					read_row();
	char*					get_row();
	void					remove_nl( char* buf );
	char*					extract_string( char* szBuf );
	bool					is_block();
	bool					is_token( const char* token );
	bool					eof();
	PajaTypes::int32		get_ort( const char* szORT );
	Import::FileHandleC*	get_texture( std::string& name );
	ScenegraphItemI*		find_object( const std::string& sName );
	void					add_scenegraphitem( ScenegraphItemI* pItem );

	void					parse_scene();

	void					parse_map( std::string& sName, PajaTypes::uint32& ui32Mapping, PajaTypes::uint32& ui32TexCh );
	void					parse_material_param_anim( MASMaterialS* pMtl );
	void					parse_materials();
	void					parse_material( MASMaterialS* pMtl );

	ContVector3C*			parse_tcb_vector3_track();
	ContVector3C*			parse_lin_vector3_track();
	ContVector3C*			parse_sample_vector3_track();

	ContVector3C*			parse_tcb_pos_track();
	ContVector3C*			parse_lin_pos_track();
	ContVector3C*			parse_sample_pos_track();

	ContQuatC*				parse_tcb_rot_track();
	ContQuatC*				parse_lin_rot_track();
	ContQuatC*				parse_sample_rot_track();

	void					parse_tcb_scale_track( ContVector3C** pContScale, ContQuatC** pContRot );
	void					parse_lin_scale_track( ContVector3C** pContScale, ContQuatC** pContRot );
	void					parse_sample_scale_track( ContVector3C** pContScale, ContQuatC** pContRot );

	ContFloatC*				parse_tcb_float_track();
	ContFloatC*				parse_lin_float_track();
	ContFloatC*				parse_sample_float_track();

	ContBoolC*				parse_bool_track();

	void					parse_object_tm_anim( ScenegraphItemI* pItem );
	void					parse_object_tm( ScenegraphItemI* pItem );
	void					parse_object_vis_track( ScenegraphItemI* pItem );

	void					parse_skin_modifier( MASMeshS& rMesh );
	void					parse_skin_modifier_bone( MASMeshS& rMesh );
	void					parse_skin_modifier_bone_tm( PajaTypes::Matrix3C& rTM );

	void					parse_morph_modifier( MASMeshS& rMesh );
	void					parse_morph_modifier_channel( MASMeshS& rMesh );
	void					parse_morph_modifier_channel_anim( MASMorphChannelS* pChannel );

	void					parse_particle_param_anim( ParticleSystemC* pPSys );
	void					parse_particle_modifiers( ParticleSystemC* pPSys );
	void					parse_particle( ParticleSystemC* pPSys );

	void					parse_wsm_object_wind_param_anim( WindC* pWind );
	void					parse_wsm_object_gravity_param_anim( GravityC* pGrav );
	void					parse_wsm_object_deflector_param_anim( DeflectorC* pDef );

	void					parse_wsm_object_wind( WSMObjectC* pObj );
	void					parse_wsm_object_gravity( WSMObjectC* pObj );
	void					parse_wsm_object_deflector( WSMObjectC* pObj );

	ScenegraphItemI*		parse_wsm_object();
	ScenegraphItemI*		parse_particleobject();
	ScenegraphItemI*		parse_geomobject();
	ScenegraphItemI*		parse_helperobject();

	PajaTypes::uint32		parse_dummy();
	void					parse_tcset( MASMeshS& rMesh, PajaTypes::uint32 ui32Channel );
	void					parse_mesh( MASMeshS& rMesh, PajaTypes::uint32& ui32MaxTexCh );

	PathC*					parse_path();
	ScenegraphItemI*		parse_shape();

	bool					parse_look_at_node_tm( PajaTypes::Vector3C& rPos, PajaTypes::float32& f32Roll );

	ScenegraphItemI*		parse_bone();

	CameraC*				parse_camera();
	void					parse_camera_param_anim( CameraC* pCam );
	void					parse_camera_tm_anim( CameraC* pCam );
	void					parse_camera_settings( CameraC* pCam );

	void					parse_light_param_anim( LightC* pLight );
	void					parse_light_settings( LightC* pLight );
	void					parse_light_tm_anim( LightC* pLight );
	LightC*					parse_light();


	FILE*							m_pStream;
	char							m_szRow[1024];
	char							m_szWord[256];

	PajaTypes::uint32				m_ui32Version;
	std::vector<MASMaterialS*>		m_rMaterials;

	std::vector<CameraC*>			m_rCameras;
	std::vector<LightC*>			m_rLights;
	std::vector<ScenegraphItemI*>	m_rScenegraph;

	PajaSystem::DemoInterfaceC* m_pInterface;

	PajaTypes::int32				m_i32FPS;
	PajaTypes::int32				m_i32TicksPerFrame;
	PajaTypes::int32				m_i32FirstFrame;
	PajaTypes::int32				m_i32LastFrame;
};


#endif
