/*
								+---------------------------------+
								|                                 |
								|   ***   3ds file loader  ***    |
								|                                 |
								|  Copyright  © -tHE SWINe- 2006  |
								|                                 |
								|            Load3ds.h            |
								|                                 |
								+---------------------------------+
*/

#pragma once
#ifndef _3DS_LOADER_INCLUDED
#define _3DS_LOADER_INCLUDED

/**
 *	@file Load3ds.h
 *	@author -tHE SWINe-
 *	@date 2006
 *	@brief 3D Studio .3ds file loader
 *
 *	@date 2006-06-05
 *
 *	rewritten based on the old "C" 3ds loader
 *
 *	@date 2007-06-04
 *
 *	fixed !fread typo in ReadTrackColor()
 *
 *	@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.
 *
 *	Unified linux / windows line ends.
 *
 */

#include "Vector.h"

namespace _3DS {

/**
 *	@brief vertex structure
 */
struct TVertex {
	int n_flag;				/**< vertex flag */

    Vector3f v_pos;			/**< vertex position */
	Vector3f v_texcoord;	/**< texture coordinate */

    Vector3f v_normal;		/**< normal vector @note Normal vectors aren't stored in .3ds file, it needs to be recalculated. */
};

/**
 *	@brief material description
 */
struct TMaterial {
    int n_id;				/**< material id, referenced by TFace::n_material */
    char *p_s_name;			/**< null-terminated string, containing material name */

	Vector3f v_ambient;		/**< material ambient color */
	Vector3f v_diffuse;		/**< material diffuse color */
	Vector3f v_specular;	/**< material specular color */
	float f_self_illum;		/**< material self-illumination */
};

/**
 *	@brief light structure
 */
struct TLight {
    int n_id;				/**< light id */
    char *p_s_name;			/**< null-terminated string, containing light name */

    int b_spot;				/**< spot-light flag */
    int b_off;				/**< switched-off flag */
    int b_cast_shadows;		/**< shadows flag */

    Vector3f v_position;	/**< light position */
    Vector3f v_target;		/**< spot-light target position */

    float f_hotspot;		/**< spot-light hotspot angle */
    float f_faloff;			/**< spot-light faloff angle */

    float f_inner_range;	/**< light attenuation inner range */
    float f_outer_range;	/**< light attenuation outer range */
    float f_multiplier;		/**< light intensity multiplier */

    Vector3f v_color;		/**< light color */
};

/**
 *	@brief triangle structure
 */
struct TFace {
    int n_id,				/**< triangle id */
		n_used;				/**< used flag */
    int n_material;			/**< material id, or -1 if no material is set */

	int n_group;			/**< group id */
	char b_original;		/**< original flag */
	char b_neg_plane;		/**< negative plane flag */

    TVertex *p_vertex[3];	/**< pointers to triangle vertices */
    Plane3f t_normal;		/**< face normal plane */
};

/**
 *	@brief position animation key-frame
 */
struct TPosFrame {
	float f_time;			/**< key-frame time */
	Vector3f v_position;	/**< position */
	float f_tension,		/**< tension parameter (Kochanek-Bartels spline) */
		  f_continuity,		/**< continuity parameter (Kochanek-Bartels spline) */
		  f_bias;			/**< bias parameter (Kochanek-Bartels spline) */
};

/**
 *	@brief rotation animation key-frame
 */
struct TRotFrame {
	float f_time;			/**< key-frame time */
	Quatf t_rotation;		/**< rotation, described by quaternion */
	float f_tension,		/**< tension parameter (Kochanek-Bartels spline) */
		  f_continuity,		/**< continuity parameter (Kochanek-Bartels spline) */
		  f_bias;			/**< bias parameter (Kochanek-Bartels spline) */
};

/**
 *	@brief object structure
 */
struct TObject {
    int n_id;				/**< object id (referenced by TObject::n_parent) */
    int n_parent;			/**< parent object id */
    char *p_s_name;			/**< null-terminated string, containing object name */

    int n_vertex_num;		/**< number of object vertices */
    int n_face_num;			/**< number of object triangles */

    TFace *p_face;			/**< array of object triangles */
    TVertex *p_vertex;		/**< array of object vertices @note This can be used to calculate triangle indices by substracting from TFace::p_vertex. */

    Matrix4f t_matrix;		/**< matrix, containing object transformation */

    int n_pos_num;			/**< number of position keyframes */
    int n_rot_num;			/**< number of rotation keyframes */

	TPosFrame *p_pos;		/**< array of position keyframes */
	TRotFrame *p_rot;		/**< array of rotation keyframes */

	void Calc_FaceNormals();	/**< @brief calculates triangle normals */
	void Calc_VertexNormals();	/**< @brief calculates vertex normals by averaging face normals @note Need to Calc_FaceNormals() first. */

	/**
	 *	@brief welds vertices which are less than f_epsilon apart
	 *
	 *	@param[in] f_epsilon is maximal distance between vertices to be weld
	 *
	 *	@return Returns true on sucess, false on failure (not enough memory).
	 */
	bool WeldVertices(float f_epsilon = .001f);
};

/**
 *	@brief camera structure
 */
struct TCamera {
    int n_id;				/**< camera id */
    char *p_s_name;			/**< null-terminated string, containing camera name */

    Matrix4f t_matrix;		/**< matrix, containing camera transformation */

	Vector3f v_position;	/**< camera position */
	Vector3f v_target;		/**< camera target position */
    float f_bank_angle;		/**< banking angle */
    float f_focus;			/**< camera focus (field of view angle) */

    int n_target_num;		/**< number of camera target position keyframes */
    int n_position_num;		/**< number of camera position keyframes */
	
	TPosFrame *p_target;	/**< array of camera target position keyframes */
	TPosFrame *p_position;	/**< array of camera position keyframes */

	void CalcMatrix();		/**< calculates camera matrix from the first position and target keyframe */
};

/**
 *	@brief 3DS scene structure
 */
struct TScene {
    int n_start_frame;		/**< index of first animation ley-frame */
    int n_end_frame;		/**< index of last animation ley-frame */

    int n_object_num;		/**< number of objects */
    int n_camera_num;		/**< number of cameras */
    int n_light_num;		/**< number of lights */
    int n_material_num;		/**< number of materials */

    TObject *p_object;		/**< array of objects */
    TCamera *p_camera;		/**< array of cameras */
    TLight *p_light;		/**< array of lights */
    TMaterial *p_material;	/**< array of materials */

	/**
	 *	@brief default constructor
	 *
	 *	Initializes all fields to null.
	 */
	TScene();

	/**
	 *	@brief destructor
	 */
	~TScene();

	/**
	 *	@brief loads scene from a 3DS file
	 *
	 *	@param[in] p_s_file_name is input file name
	 *
	 *	@return Returns pointer to loaded scene on success, or null on failure.
	 *
	 *	@note This just calls C3DSLoader::p_Load().
	 */
	static TScene *p_Load(const char *p_s_file_name);

private:
	TScene(const TScene &r_scene) {} // do not copy scenes like this. use pointers instead!
	TScene &operator =(const TScene &r_scene) { return *this; } // do not copy scenes like this. use pointers instead!
};

};

/**
 *	@brief class, loading 3DS files
 */
class C3DSLoader {
private:
	_3DS::TScene *m_p_scene;

	Vector3f m_v_color;
	float m_f_percentage;

	int m_n_node_object_index;
	int m_n_face_num;

	char m_p_s_name[64];
	char m_p_s_track_name[64];

	int m_n_track_type, m_n_camera_index, m_n_object_index;
	int m_n_material_index, m_n_element_index;
	int m_n_light_index;

	int m_b_count_entites;

	struct TChunkHeader { 
		unsigned short n_id;
		unsigned long n_len;
	};

	struct TChunkName {
		unsigned short n_id;
		bool b_subchunk;
		bool (*function)(C3DSLoader *p_this, FILE *fp, long n_pos);
	};

	static const TChunkName p_chunk_name_list[];

public:
	/**
	 *	@brief loads scene from a 3DS file
	 *
	 *	@param[in] p_s_file_name is input file name
	 *
	 *	@return Returns pointer to loaded scene on success, or null on failure.
	 */
	_3DS::TScene *p_Load(const char *p_s_file_name);

private:
	bool Chunk_Read(FILE *fp, long n_pos);
	bool Set_Object_Id(int n_index, int n_id, int n_parent_id);
	int n_Object(const char *p_s_name) const;
	int n_Camera(const char *p_s_name) const;
	int n_Light(const char *p_s_name) const;
	int n_Material(const char *p_s_name) const;
	static bool Read_CStr(char *p_s_name, FILE *fp);
	static bool Read_Skip(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Skip_Float(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Skip_Word(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Skip_DWord(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_Color_RGB(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_Color_RGBF(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_NPercentage(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_FPercentage(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_Light(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_SpotLight(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_LightInnerRange(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_LightOuterRange(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_LightMultiplier(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_LightOff(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_LightShadowed(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MeshName(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MapFile(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_NodeId(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TriObject(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_VertexList(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_VertexFlags(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_FaceList(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_FaceMaterial(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MapList(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_SmoothingList(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MeshMatrix(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_Camera(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MaterialName(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MaterialAmbient(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MaterialDiffuse(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MaterialSpecular(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_MaterialSelfIllum(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_Frames(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackNodeName(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_PivotPoint(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_SplineFlags(FILE *fp, unsigned short &r_n_time,
		float &r_f_tens, float &r_f_cont, float &r_f_bias);
	static bool Read_TrackFlags(FILE *fp, unsigned short &r_n_frame_num);
	static bool Read_TrackPos(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackColor(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackRot(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackFOV(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackRoll(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackScale(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackCamera(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackCamTgt(C3DSLoader *p_this, FILE *fp, long n_pos);
	static bool Read_TrackInfo(C3DSLoader *p_this, FILE *fp, long n_pos);
	int n_Chunk(unsigned short n_id) const;
};

#endif // !_3DS_LOADER_INCLUDED
