/*
    Copyright (c) 2008 NetAllied Systems GmbH

	This file is part of COLLADAFramework.
	
    Licensed under the MIT Open Source License, 
    for details please see LICENSE file or the website
    http://www.opensource.org/licenses/mit-license.php
*/

#ifndef __COLLADAFW_MESHPRIMITIVE_H__
#define __COLLADAFW_MESHPRIMITIVE_H__

#include "COLLADAFWPrerequisites.h"
#include "COLLADAFWConstants.h"
#include "COLLADAFWTypes.h"
#include "COLLADAFWEdge.h"
#include "COLLADAFWIndexList.h"

#include <map>
#include <vector>


namespace COLLADAFW
{

    /**
    Geometric primitives, which assemble values from the inputs into vertex attribute data. 
    Can be any combination of any primitive types in any order. 

    To describe geometric primitives that are formed from the vertex data, the <mesh> element may 
    contain zero or more of the primitive elements <lines>, <linestrips>, <polygons>, <polylist>,
    <triangles>, <trifans>, and <tristrips>.
    The <vertices> element under <mesh> is used to describe mesh-vertices. Polygons, triangles, and 
    so forth index mesh-vertices, not positions directly. Mesh-vertices must have at least one 
    <input> (unshared) element with a semantic attribute whose value is POSITION.
    For texture coordinates, COLLADA’s right-handed coordinate system applies; therefore, an ST 
    texture coordinate of [0,0] maps to the lower-left texel of a texture image, when loaded in a 
    professional 2-D texture viewer/editor.
    */
    class MeshPrimitive
    {

    public:

        /** The types of primitives. */
	    enum PrimitiveType
	    {
		    LINES, /**< A list of lines. Only one element is contained in the face-vertex count list.
			    It represents the total number of line vertices. The total number of lines is
			    equal to half the total number of line vertices. */
		    LINE_STRIPS, /**< A list of continuous lines. Each element in the face-vertex count list
			    represents the number of consecutive line vertices before restarting. */
		    POLYGONS, /**< A list of polygons. All the polygons may be triangles.
			    This is the most common primitive type. The polygons may have holes. Each element in the
			    face-vertex count list represent the number of vertices for one polygon. */
            POLYLIST,
            TRIANGLES, 
		    TRIANGLE_FANS, /**< A list of triangle fans. Each element in the face-vertex count list
			    represents the number of vertices for one fan. A triangle fan is defined by re-using the first
			    vertex for every triangle. Advancing pairs are then used in order to generate adjacent triangles
			    such that if there are 5 vertices, then 3 triangles are created: {0,1,2}, {0,2,3} and {0,3,4}. */
		    TRIANGLE_STRIPS, /**< A list of continuous triangles. Each element in the face-vertex count list
			    represents the number of vertices for one strip. A triangle strip is defined by re-using two
			    advancing vertices from the previous triangle for the next triangle. If there are 5 vertices in 
			    the strip, then 3 triangles are created: {0,1,2}, {1,2,3}, {2,3,4}. Note that vertex winding must
			    also be taken into consideration and every even triangle in the strip has its vertices swapped
			    from the above pattern. */
		    POINTS, /**< A list of Camera-facing sprites. The face-vertex count list will contain one element that 
			    represents the total number of points. Two non-COLLADA geometry sources (POINT_SIZE and POINT_ROTATION)
			    are specific to this type. */
            UNDEFINED_PRIMITIVE_TYPE
	    };

    private:

        /** 
        * The type of the current primitive. Possible values are:
        * <lines>, <linestrips>, <polygons>, <polylist>, <triangles>, <trifans>, and <tristrips>. 
        */
        PrimitiveType mPrimitiveType;

        /**
        * The attribute indicates the number of polygon faces in the current primitive element.
        */
        size_t mFaceCount;

        /**
        * The material attribute declares a symbol for a material. This symbol 
        * is bound to a material at  the time of instantiation. If the material 
        * attribute is not specified then the lighting and  shading results are 
        * application defined. Optional attribute. 
        */
        String mMaterial;

		/** The material id of the sub mesh. This material id is used to assign material 
		to submeshes when the mesh gets instantiated.*/
		MaterialId mMaterialId;

		/** 
        * The index list of the positions array. 
        */
        UIntValuesArray mPositionIndices;

        /** 
        * The index list of the normals array. 
        */
        UIntValuesArray mNormalIndices;

        /** 
        * The index list of the colors array  (support of multiple colors). 
        */
        IndexListArray mColorIndicesArray;

        /** 
        * The index list of the uv coordinates array (support of multiple uv sets). 
        */
        IndexListArray mUVCoordIndicesArray;
        
    public:	

        /**
        * Constructor.
        */
        MeshPrimitive ();

        /**
        * Constructor.
        */
        MeshPrimitive ( PrimitiveType primitiveType );

        /**
        * Destructor.
        */
        virtual ~MeshPrimitive() {}

        /** The type of the current primitive. Possible values are:
        <lines>, <linestrips>, <polygons>, <polylist>, <triangles>, <trifans>, and <tristrips>. */
        const MeshPrimitive::PrimitiveType getPrimitiveType () const { return mPrimitiveType; }

        /** The type of the current primitive. Possible values are:
        <lines>, <linestrips>, <polygons>, <polylist>, <triangles>, <trifans>, and <tristrips>. */
        void setPrimitiveType ( const MeshPrimitive::PrimitiveType PrimitiveType ) { mPrimitiveType = PrimitiveType; }

        /**
        * Gets the count attribute.
        * @return Returns the count attribute.
        */
        const size_t getFaceCount () const { return mFaceCount; }

        /**
        * Sets the count attribute.
        * @param atCount The new value for the count attribute.
        */
        void setFaceCount ( const size_t count ) { mFaceCount = count; }

        /**
        * Gets the material attribute.
        * @return Returns a xsNCName of the material attribute.
        */
        String getMaterial () const { return mMaterial; }

        /**
        * Sets the material attribute.
        * @param atMaterial The new value for the material attribute.
        */
        void setMaterial ( const String& material ) { mMaterial = material; }

		/** 
		* The index list of the positions array. 
		*/
		COLLADAFW::UIntValuesArray& getPositionIndices () { return mPositionIndices; }

		/** 
		* The index list of the positions array. 
		*/
		const COLLADAFW::UIntValuesArray& getPositionIndices () const { return mPositionIndices; }

        /** 
        * The index list of the positions array. 
        */
        void setPositionIndices ( const COLLADAFW::UIntValuesArray& PositionIndices ) { mPositionIndices = PositionIndices; }

		/** 
		* The index list of the normals array. 
		*/
		COLLADAFW::UIntValuesArray& getNormalIndices () { return mNormalIndices; }

		/** 
		* The index list of the normals array. 
		*/
		const COLLADAFW::UIntValuesArray& getNormalIndices () const{ return mNormalIndices; }

        /** 
        * The index list of the normals array. 
        */
        void setNormalIndices ( const COLLADAFW::UIntValuesArray& NormalIndices ) { mNormalIndices = NormalIndices; }

        /** 
        * The index list of the colors array. 
        */
        IndexList* getColorIndices ( size_t index ) 
        { 
            assert ( mColorIndicesArray.getCount () > index );
            return mColorIndicesArray [ index ]; 
        }

        /** 
        * The index list of the colors array. 
        */
        const IndexList* getColorIndices ( size_t index ) const 
        { 
            assert ( mColorIndicesArray.getCount () > index );
            return mColorIndicesArray [ index ]; 
        }

        /** 
        * The index list of the colors array. 
        */
        IndexListArray& getColorIndicesArray () { return mColorIndicesArray; }

        /** 
        * The index list of the colors array. 
        */
        const IndexListArray& getColorIndicesArray () const { return mColorIndicesArray; }

        /** 
        * The index list of the colors array. 
        */
        void appendColorIndices ( IndexList* colorIndices ) { mColorIndicesArray.append ( colorIndices ); }

        /** 
        * The index list of the uv coordinates array. 
        */
        IndexListArray& getUVCoordIndicesArray () { return mUVCoordIndicesArray; }

        /** 
        * The index list of the uv coordinates array. 
        */
        const IndexListArray& getUVCoordIndicesArray () const { return mUVCoordIndicesArray; }

        /** 
        * The index list of the uv coordinates array. 
        */
        IndexList* getUVCoordIndices ( size_t index )
        {
            assert ( mUVCoordIndicesArray.getCount () > index );
            return mUVCoordIndicesArray [ index ]; 
        }

        /** 
        * The index list of the uv coordinates array. 
        */
        const IndexList* getUVCoordIndices ( size_t index ) const 
        {
            assert ( mUVCoordIndicesArray.getCount () > index );
            return mUVCoordIndicesArray [ index ]; 
        }

        /** 
        * The index list of the uv coordinates array. 
        */
        void appendUVCoordIndices ( IndexList* uvCoordIndices ) { mUVCoordIndicesArray.append ( uvCoordIndices ); }

		/** @return The material id of the sub mesh. This material id is used to assign material 
		to submeshes when the mesh gets instantiated.*/
		COLLADAFW::MaterialId getMaterialId() const { return mMaterialId; }

		/** Sets the material id of the sub mesh. This material id is used to assign material 
		to submeshes when the mesh gets instantiated.*/
		void setMaterialId(COLLADAFW::MaterialId val) { mMaterialId = val; }
		
		/*
         *	Fills the array with the index list of the edges 
         *  (the index list referes on the position indices)
         */
        COLLADAFW::UIntValuesArray& getEdgeIndices ( COLLADAFW::UIntValuesArray& edgeIndices );

        /*
        * Determine the edge indices (unique edges, also for multiple primitive elements)
        * and write it into the lists (the indices referes on the position indices).
        * Does it for triangle elements, polygons or polylist. Trifans and tristrips are a little 
        * bit special.
        * @param edgeIndices 
        *           A vector of edge indices. We use it to write the list of edges into the maya 
        *           file. The vector is already sorted.
        * @param edgeIndicesMap 
        *           We store the edge indices also in a sorted map. The dublicate data holding 
        *           is reasonable, because we need the index of a given edge. The search of  
        *           values in a map is much faster than in a vector!
        */
        void appendEdgeIndices ( 
            std::vector<Edge>& edgeIndices, 
            std::map<Edge,size_t>& edgeIndicesMap );

        /*
        * Determine the edge indices (unique edges, also for multiple primitive elements)
        * and write it into the lists (the indices referes on the position indices).
        * Does it for triangle elements, polygons or polylist. 
        * @param edgeIndices 
        *           A vector of edge indices. We use it to write the list of edges into the maya 
        *           file. The vector is already sorted.
        * @param edgeIndicesMap 
        *           We store the edge indices also in a sorted map. The dublicate data holding 
        *           is reasonable, because we need the index of a given edge. The search of  
        *           values in a map is much faster than in a vector!
        */
        void appendPolygonEdgeIndices ( 
            std::vector<Edge>& edgeIndices, 
            std::map<Edge,size_t>& edgeIndicesMap );

        /*
        * Determine the edge indices (unique edges, also for multiple primitive elements)
        * and write it into the lists (the indices referes on the position indices).
        * Does it for trifans.
        * @param edgeIndices 
        *           A vector of edge indices. We use it to write the list of edges into the maya 
        *           file. The vector is already sorted.
        * @param edgeIndicesMap 
        *           We store the edge indices also in a sorted map. The dublicate data holding 
        *           is reasonable, because we need the index of a given edge. The search of  
        *           values in a map is much faster than in a vector!
        */
        void appendTrifansEdgeIndices (
            std::vector<Edge>& edgeIndices, 
            std::map<Edge,size_t>& edgeIndicesMap );

        /*
        * Determine the edge indices (unique edges, also for multiple primitive elements)
        * and write it into the lists (the indices referes on the position indices).
        * Does it for tristrips.
        * @param edgeIndices 
        *           A vector of edge indices. We use it to write the list of edges into the maya 
        *           file. The vector is already sorted.
        * @param edgeIndicesMap 
        *           We store the edge indices also in a sorted map. The dublicate data holding 
        *           is reasonable, because we need the index of a given edge. The search of  
        *           values in a map is much faster than in a vector!
        */
        void appendTristripsEdgeIndices (
            std::vector<Edge>& edgeIndices, 
            std::map<Edge,size_t>& edgeIndicesMap );

        /*
        * Determine the number of grouped vertex elements in the current mesh primitive.
        */
        int getGroupedVertexElementsCount () const;

        /*
        * Appends the data of an edge, if it is not already in the list.
        */
        void appendEdge( 
            const Edge& edge, 
            std::vector<Edge>& edgeIndices, 
            std::map<Edge,size_t>& edgeIndicesMap );

        /*
         *	Returns the vertex count of the face with the specified index.
         */
        const int getGroupedVerticesVertexCount ( const size_t faceIndex ) const;

    };

    
    typedef ArrayPrimitiveType<MeshPrimitive*> MeshPrimitiveArray;

}

#endif // __COLLADAFW_MESHPRIMITIVE_H__
