/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commercial and non commercial
 * applications, as long as this copyright notice is maintained.
 *
 * This application 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.
 *
*/

#ifndef OPENGLES_GEOMETRY_OPTIMIZER
#define OPENGLES_GEOMETRY_OPTIMIZER

#include <osg/Node>
#include <algorithm> //std::max

#include "AnimationVisitor"
#include "BindPerVertexVisitor"
#include "DetachPrimitiveVisitor"
#include "DrawArrayVisitor"
#include "GeometrySplitterVisitor"
#include "IndexMeshVisitor"
#include "PreTransformVisitor"
#include "TangentSpaceVisitor"
#include "TriangleStripVisitor"
#include "UnIndexMeshVisitor"
#include "WireframeVisitor"


class OpenGLESGeometryOptimizer
{
public:
    OpenGLESGeometryOptimizer() :
        _useDrawArray(false),
        _disableTriStrip(false),
        _disableMergeTriStrip(false),
        _disablePreTransform(false),
        _disablePostTransform(false),
        _triStripCacheSize(16),
        _triStripMinSize(2),
        _generateTangentSpace(false),
        _tangentUnit(0),
        _maxIndexValue(65535),
        _wireframe("")
    {}

    // run the optimizer
    osg::Node* optimize(osg::Node& node);

    // handle options
    void setUseDrawArray(bool s) { _useDrawArray = s; }

    void setDisableTriStrip(bool s) { _disableTriStrip = s; }
    void setDisableMergeTriStrip(bool s) { _disableMergeTriStrip = s; }
    void setDisablePreTransform(bool s) { _disablePreTransform = s; }
    void setDisablePostTransform(bool s) { _disablePostTransform = s; }
    void setTripStripCacheSize(unsigned int size) { _triStripCacheSize = size; }
    void setTripStripMinSize(unsigned int size) { _triStripMinSize = std::max<unsigned int>(size, 2); }

    void setTexCoordChannelForTangentSpace(int uv) {
        _tangentUnit = uv;
        _generateTangentSpace = true;
    }

    void setMaxIndexValue(unsigned int s) { _maxIndexValue = s; }
    void setWireframe(const std::string& s) {
        _wireframe = s;
        if(_wireframe == std::string("outline")) {
            // no use to build strip if we only want wireframe
            setDisableTriStrip(true);
        }
    }

protected:
    void makeAnimation(osg::Node* node) {
        AnimationVisitor anim;
        node->accept(anim);
    }

    void makeWireframe(osg::Node* node) {
        WireframeVisitor wireframe(_wireframe == std::string("inline"));
        node->accept(wireframe);
    }

    void makeBindPerVertex(osg::Node* node) {
        BindPerVertexVisitor bindpervertex;
        node->accept(bindpervertex);
    }

    void makeIndexMesh(osg::Node* node) {
        IndexMeshVisitor indexer;
        node->accept(indexer);
    }

    void makeTangentSpace(osg::Node* node) {
        TangentSpaceVisitor tangent(_tangentUnit);
        node->accept(tangent);
    }

    void makeSplit(osg::Node* node) {
        GeometrySplitterVisitor splitter(_maxIndexValue, _disablePostTransform);
        node->accept(splitter);
    }

    void makeTriStrip(osg::Node* node) {
        TriangleStripVisitor strip(_triStripCacheSize, _triStripMinSize, !_disableMergeTriStrip);
        node->accept(strip);
    }

    void makeDrawArray(osg::Node* node) {
        DrawArrayVisitor drawarray;
        node->accept(drawarray);
    }

    void makePreTransform(osg::Node* node) {
        PreTransformVisitor preTransform;
        node->accept(preTransform);
    }

    void makeDetach(osg::Node* node) {
        DetachPrimitiveVisitor detacher("wireframe", false, _wireframe == std::string("inline"));
        node->accept(detacher);
    }

    bool _useDrawArray;

    bool _disableTriStrip;
    bool _disableMergeTriStrip;
    bool _disablePreTransform;
    bool _disablePostTransform;
    unsigned int _triStripCacheSize;
    unsigned int _triStripMinSize;

    bool _generateTangentSpace;
    int _tangentUnit;

    unsigned int _maxIndexValue;
    std::string _wireframe;
};

#endif
