/*
  defines the bod_bob_parser - class that can parse bod format and create 
  bob_dom_bob object
*/

#ifndef BOD_BOB_PARSER_INCLUDED
#define BOD_BOB_PARSER_INCLUDED

#include "bob_dom_bob.h"
#include "bod_text_parser.h"
#include "token_stream.h"
#include "settings.h"
#include "bod_parser_base.h"

class bod_bob_parser : public bod_parser_base
{
	private:
		struct texture_coord_t
		{
			int x;
			int y;
			int index;
		};
		
		struct texture_coords_t : public ext::simple_list<texture_coord_t*>
		{
			int flag;
			texture_coord_t * findCoords(int x, int y)
			{
				for(iterator &it=begin(); it!=end(); ++it){
					if(it->x==x && it->y==y)
						return *it;
				}
				return 0;
			}
			
			~texture_coords_t()
			{
				for(iterator &it=begin(); it!=end(); ++it){
					delete *it;
				}
			}
		};
		
		struct point_t : public ext::simple_list<texture_coords_t*>
		{
			int x, y, z;
			
			point_t() {}
			point_t(int x, int y, int z) { this->x=x; this->y=y; this->z=z; }
			
			~point_t()
			{
				for(iterator &it=begin(); it!=end(); ++it){
					delete *it;
				}
			}
			
			texture_coords_t * findTextureCoords(int flag)
			{
				for(iterator &it=begin(); it!=end(); ++it){
					if(it->flag==flag) return *it;
				}
				texture_coords_t *coords=new texture_coords_t();
				coords->flag=flag;
				push_back(coords);
				return coords;
			}
			
			size_t pointCount() const
			{
				size_t c=0;
				for(const_iterator &it=begin(); it!=end(); ++it){
					c+=it->size();
				}
				return c;
			}
		};
		
	protected:
		const Settings *m_settings;
		
		bool checkImmediatePlacement(const bod_bob_parser::token *token1, const bod_bob_parser::token *token2);
		
	private:
		typedef ext::simple_list<point_t*> PointList;
		typedef ext::array<point_t*> PointArray;
		typedef ext::array<bob_dom_weight*> WeightArray;
		
		const bob_dom_materials *m_materials;
		PointArray m_points;
		WeightArray m_weights;
		
	private:
		bool loadMatRGB(token_stream& is, bob_dom_material5::rgb& rgb);
		bool loadMatPair(token_stream& is, bob_dom_material5::pair& pair);
		bool loadMatPair(token_stream& is, bob_dom_material6::Small::pair& pair);
		bool loadMat(bob_dom_material5 *mat, token_stream &is);
		bool loadMat(bob_dom_material6 *mat, token_stream &is);
		bool loadMaterial(bob_dom_bob *bob, token_stream& is);
		bool loadBody(bob_dom_bob *bob, token_stream& is);
		bool loadPoints(bob_dom_body *body, token_stream& is, PointList& points);
		bool loadPart(bob_dom_body *body, token_stream& is);
		bool loadFace(int matIdx, bob_dom_body& body, bob_dom_part& part, token_stream& is);
		bool flagsFromString(token_stream& is, int& flags);
		
		material6_value * loadMat6Value(token_stream& is);
		
		bool loadMaterial6Small(bob_dom_material6 *mat, token_stream &is);
		bool loadMaterial6Big(bob_dom_material6 *mat, token_stream &is);
		
		bool loadSpecialValues(bob_dom_point *points[3], int magic[3], token_stream& is);
		bool loadSpecialPointValues(token_stream& is, bob_dom_point *point);
		
		bool loadBones(bob_dom_body *body, token_stream& is);
		bool loadWeights(bob_dom_body *body, token_stream& is);
		
		bool loadSecondaryUVRecord(token_stream& is, bob_dom_part& part);
		bool loadSecondaryUVs(token_stream& is, bob_dom_part& part);
		
		bool loadX3PartValues(token_stream& is, bob_dom_part& part);
		
		void deleteTempData()
		{
			for(PointArray::iterator &it=m_points.begin(); it!=m_points.end(); ++it){
				delete *it;
			}
			m_points.clear();
			
			for(WeightArray::iterator &it=m_weights.begin(); it!=m_weights.end(); ++it){
				delete *it;
			}
			m_weights.clear();
		}
		
	public:
		
		bod_bob_parser(const Settings *settings) { m_settings=settings; }
		~bod_bob_parser() { deleteTempData(); }
		
		bob_dom_bob * loadBOB(token_stream& is, bool bEmbedded);
		
		bool compile(token_stream& is, bob_dom_obinaryfilestream& os, bool bEmbedded);
};

#endif //!defined(BOD_BOB_PARSER_INCLUDED)