/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2010 Pelican Ventures, Inc.
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#ifndef SEAMLESS_PATCH
#define SEAMLESS_PATCH 1

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Node>
#include <osg/PrimitiveSet>

namespace seamless
{
class PatchSet;

class Patch : public osg::Node
{
public:
    Patch();
    Patch(const Patch& rhs,
          const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);

    META_Node(seamless, Patch);

    virtual void traverse(osg::NodeVisitor& nv);
    virtual osg::BoundingSphere computeBound() const;
    virtual void resizeGLObjectBuffers(unsigned int maxSize);
    virtual void releaseGLObjects(osg::State* = 0) const;

    virtual void init();

    // Wrapper around all the data for a patch at the different
    // LODs. The Geometry objects associated with triles point to the
    // arrays stored here.
    struct Data : public osg::Object
    {
        Data();
        Data(const Data&,
             const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
        ~Data() {}
        META_Object(seamless, Patch::Data);
        void getGeometryAttributes(const osg::Geometry* geom);
        void setGeometryAttributes(osg::Geometry* geom);
        osg::Geometry::ArrayData vertexData;
        osg::Geometry::ArrayData normalData;
        osg::Geometry::ArrayData colorData;
        osg::Geometry::ArrayData secondaryColorData;
        osg::Geometry::ArrayData fogCoordData;
        osg::Geometry::ArrayDataList texCoordList;
        osg::Geometry::ArrayDataList vertexAttribList;
    };

    Data* getData() { return _data.get(); }
    const Data* getData() const { return _data.get(); }
    void setData(Data* data)
    {
        _data = data;
        init();
    }
    /** Dirty vertex data. A convenience method.
     */
    void dirtyVertexData();
    /** Get patch error. This is the maximum of the edge errors.
     *   @param eye The eyepoint in local coordinates.
     */
    float getPatchError(const osg::Vec3& eye);
    virtual float getEdgeError(const osg::Vec3& eye, int edge);
    void setPatchSet(PatchSet* patchSet);
    PatchSet* getPatchSet() const;
    /**
     * Set error threshold between high and low resolution
     * LOD. Default is .5.
     */
    void setErrorThreshold(float threshold) { _errorThreshold = threshold; }
    float getErrorThreshold() const { return _errorThreshold; }
protected:
    ~Patch();
    // Triles at two resolutions, counterclockwise from bottom. 0 is
    // low, 1 is high.
    osg::ref_ptr<osg::Geode> _trile[2][4];
    // connecting strips at each diagonal, counterclockwise from lower
    // left. There are 4 possibilities for each strip:
    // 0 both neighbors low
    // 1 clockwise neighbor low, counterclockwise neighbor high
    // 2 clockwise neighbor high, counterclockwise neighbor low
    // 3 both neighbors high
    osg::ref_ptr<osg::Geode> _strip[4][4];
    osg::ref_ptr<Data> _data;
    osg::ref_ptr<PatchSet> _patchSet;
    float _errorThreshold;
};

// Utilities useful in Patch subclasses
// Find the point closest to P3 on the line segment from P1 to P2
extern osg::Vec3 closestPointOnSegment(
    const osg::Vec3& p1, const osg::Vec3& p2, const osg::Vec3& p3);
extern float distanceToSegment(
    const osg::Vec3& p1, const osg::Vec3& p2, const osg::Vec3& p3);
}
#endif
