/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSGUTIL_Tesselator
#define OSGUTIL_Tesselator

#include <osg/Geometry>

#include <osgUtil/Export>

#include <osg/GLU>

#include <vector>

/* Win32 calling conventions. (or a least thats what the GLUT example tess.c uses.)*/
#ifndef CALLBACK
#define CALLBACK
#endif


namespace osgUtil {

/** A simple class for tessellating a single polygon boundary.
  * Currently uses old style glu tessellation functions for portability.
  * It be nice to use the modern glu tessellation functions or to find
  * a small set of code for doing this job better.*/
class OSGUTIL_EXPORT Tesselator
{
    public:
   
        Tesselator();
        ~Tesselator();

        enum InputBoundaryDirection
        {
            CLOCK_WISE,
            COUNTER_CLOCK_WISE
        };
        
        typedef std::vector<osg::Vec3*> VertexPointList;
        
        struct Prim : public osg::Referenced
        {
            Prim(GLenum mode):_mode(mode) {}
        
            typedef std::vector<osg::Vec3*> VecList;

            GLenum  _mode;
            VecList _vertices;
        };
        
        void beginTesselation();
        
        void beginContour();
        void addVertex(osg::Vec3* vertex);
        void endContour();
        
        void endTesselation();

        typedef std::vector< osg::ref_ptr<Prim> > PrimList;
        
        PrimList& getPrimList() { return _primList; }        
        
        void retesselatePolygons(osg::Geometry& geom);
        
        void reset();
        
    protected:
        
        void begin(GLenum mode);
        void vertex(osg::Vec3* vertex);
        void combine(osg::Vec3* vertex,void* vertex_data[4],GLfloat weight[4]);
        void end();
        void error(GLenum errorCode);

    
        static void CALLBACK beginCallback(GLenum which, void* userData);
        static void CALLBACK vertexCallback(GLvoid *data, void* userData);
        static void CALLBACK combineCallback(GLdouble coords[3], void* vertex_data[4],
                              GLfloat weight[4], void** outData,
                              void* useData);
        static void CALLBACK endCallback(void* userData);
        static void CALLBACK errorCallback(GLenum errorCode, void* userData);
        
        
        struct Vec3d
        {
            double _v[3];
        };


        struct NewVertex
        {
        
            NewVertex():
                _f1(0),
                _v1(0),
                _f2(0),
                _v2(0),
                _f3(0),
                _v3(0),
                _f4(0),
                _v4(0) {}
            
            NewVertex(const NewVertex& nv):
                _f1(nv._f1),
                _v1(nv._v1),
                _f2(nv._f2),
                _v2(nv._v2),
                _f3(nv._f3),
                _v3(nv._v3),
                _f4(nv._f4),
                _v4(nv._v4) {}

            NewVertex(float f1,osg::Vec3* v1,
                      float f2,osg::Vec3* v2,
                      float f3,osg::Vec3* v3,
                      float f4,osg::Vec3* v4):
                _f1(f1),
                _v1(v1),
                _f2(f2),
                _v2(v2),
                _f3(f3),
                _v3(v3),
                _f4(f4),
                _v4(v4) {}
        
            float       _f1;
            osg::Vec3*  _v1;

            float       _f2;
            osg::Vec3*  _v2;

            float       _f3;
            osg::Vec3*  _v3;

            float       _f4;
            osg::Vec3*  _v4;
            
        };
        
        typedef std::map<osg::Vec3*,NewVertex> NewVertexList;
        typedef std::vector<Vec3d*> Vec3dList;

        GLUtesselator*  _tobj;
        PrimList        _primList;
        Vec3dList       _coordData;
        NewVertexList   _newVertexList;
        GLenum          _errorCode;

};

}

#endif
