/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2010 Pelican Mapping
 * 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 OSGEARTHSYMBOLOGY_GEOMETRY_H
#define OSGEARTHSYMBOLOGY_GEOMETRY_H 1

#include <osgEarthSymbology/Common>
#include <osgEarth/GeoData>
#include <osg/Array>
#include <vector>
#include <stack>

namespace osgEarth { namespace Symbology 
{
    using namespace osgEarth;

    /** Options for the Geometry::buffer() operation. */
    class BufferParameters
    {
    public:
        enum CapStyle  { CAP_DEFAULT, CAP_SQUARE, CAP_ROUND, CAP_FLAT };
        enum JoinStyle { JOIN_ROUND, JOIN_MITRE, JOIN_BEVEL};
        BufferParameters( CapStyle capStyle =CAP_DEFAULT, JoinStyle joinStyle = JOIN_ROUND, int cornerSegs =0, bool singleSided=false, bool leftSide=false )
            : _capStyle(capStyle), _joinStyle(joinStyle),_cornerSegs(cornerSegs), _singleSided(singleSided), _leftSide(leftSide) { }
        CapStyle  _capStyle;
        JoinStyle _joinStyle;
        int       _cornerSegs; // # of line segment making up a rounded corner
        bool      _singleSided; //Whether or not to do a single sided buffer
        bool      _leftSide;    //If doing a single sided buffer are we buffering to the left?  If false, buffer to the right
    };

    /**
     * Baseline geometry class. All Geometry objects derive from this
     * class, even MultiGeometry (which does not make use of the Vec3dArray,
     * but it keeps the class hierarchy simple).
     */
    class OSGEARTHSYMBOLOGY_EXPORT Geometry : public osg::Vec3dArray
    {
    public:
        Geometry( const osg::Vec3dArray* toCopy );

    public:
        enum Type {
            TYPE_UNKNOWN,
            TYPE_POINTSET,
            TYPE_LINESTRING,
            TYPE_RING,
            TYPE_POLYGON,
            TYPE_MULTI
        };

        static std::string toString( Type t ) {
            return 
                t == TYPE_POINTSET ? "pointset" :
                t == TYPE_LINESTRING ? "linestring" :
                t == TYPE_RING ? "ring" :
                t == TYPE_POLYGON ? "polygon" :
                t == TYPE_MULTI ? "multi" :
                "unknown";
        }

        static Geometry* create( Type type, const osg::Vec3dArray* toCopy );

        // true if osgEarth is compiled for buffering
        static bool hasBufferOperation();

    public:
        // gets the total number of points in this geometry.
        virtual int getTotalPointCount() const;

        /**
         * Converts this geometry to another type. This function will return "this" if
         * the type is the same, and will return NULL if the conversion is impossible.
         */
        virtual Geometry* cloneAs( const Geometry::Type& newType ) const;

        /**
         * Creates a new Vec3Array (single-precision), copies the part into it, and
         * returns the new object. 
         */
        osg::Vec3Array* toVec3Array() const;

        virtual Bounds getBounds() const;

        /**
         * Runs a buffer (dialate/erode) operation on this geometry and returns the
         * result in the output parameter. Returns true if the op succeeded.
         */
        bool buffer(
            double distance,
            osg::ref_ptr<Geometry>& output,
            const BufferParameters& bp =BufferParameters() ) const;

        /**
         * Crops this geometry to the region represented by the crop polygon, returning
         * the result in the output parameter. Returns true if the op succeeded.
         */
        bool crop(
            const class Polygon* cropPolygon,
            osg::ref_ptr<Geometry>& output ) const;

    public:
        virtual Type getType() const =0;
        virtual Type getComponentType() const { return getType(); }
        virtual bool isValid() const { return size() >= 1; }

        virtual osg::Object* clone( const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) const =0;

    protected:
        Geometry( int capacity =0 );
        Geometry( const Geometry& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );
    };

    typedef std::vector< osg::ref_ptr<Geometry> > GeometryCollection;

    /**
     * An unordered collections of points.
     */
    class OSGEARTHSYMBOLOGY_EXPORT PointSet : public Geometry
    {
    public:
        PointSet( int capacity =0 ) : Geometry( capacity ) { }
        PointSet( const osg::Vec3dArray* toCopy ) : Geometry( toCopy ) { }
        PointSet( const PointSet& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );

        META_Object( osgEarthSymbology, PointSet );

    public:
        virtual Type getType() const { return Geometry::TYPE_POINTSET; }
    };

    /**
     * An ordered set of points forming a single contiguous line string.
     */
    class OSGEARTHSYMBOLOGY_EXPORT LineString : public Geometry
    {
    public:
        LineString( int capacity =0 ) : Geometry( capacity ) { }
        LineString( const LineString& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );
        LineString( const osg::Vec3dArray* toCopy );

        META_Object( osgEarthSymbology, LineString );

        double getLength() const;
        bool getSegment(double length, osg::Vec3d& start, osg::Vec3d& end);

    public:
        virtual Type getType() const { return Geometry::TYPE_LINESTRING; }
        virtual bool isValid() const { return size() >= 2; }
    };

    /**
     * A Ring is a closed region. It is open (the first and last
     * points are not the same). It has an orientation, i.e. it is either
     * wound clockwise or counter-clockwise.
     */
    class OSGEARTHSYMBOLOGY_EXPORT Ring : public Geometry
    {
    public:
        enum Orientation {
            ORIENTATION_CCW,
            ORIENTATION_CW,
            ORIENTATION_DEGENERATE
        };

    public:
        Ring( int capacity =0 ) : Geometry( capacity ) { }
        Ring( const Ring& ring, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );
        Ring( const osg::Vec3dArray* toCopy );

        META_Object( osgEarthSymbology, Ring );

        // override
        virtual Geometry* cloneAs( const Geometry::Type& newType ) const;

        // gets the ring's orientation
        Orientation getOrientation() const;

        // gets the signed area of a part that is known to be open. A positive
        // area means it's would CCW; negative means it's would CW.
        //double getSignedArea() const;

        // ensures that the first and last points are not idential.
        void open();
        
        // opens and rewinds the ring to the specified orientation.
        void rewind( Orientation ori );

    public:
        virtual Type getType() const { return Geometry::TYPE_RING; }
        virtual bool isValid() const { return size() >= 3; }
    };

    typedef std::vector<osg::ref_ptr<Ring> > RingCollection;

    /**
     * A Polygon is a geometry that consists of one outer boundary Ring, and
     * zero or more inner "hole" rings. The boundary ring is would CCW, and the
     * inner "holes" are wound CW.
     */
    class OSGEARTHSYMBOLOGY_EXPORT Polygon : public Ring
    {
    public:
        Polygon( int capacity =0 ) : Ring( capacity ) { }
        Polygon( const Polygon& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );
        Polygon( const osg::Vec3dArray* toCopy );

        META_Object( osgEarthSymbology, Polygon );
        
    public:
        virtual Type getType() const { return Geometry::TYPE_POLYGON; }
        virtual int getTotalPointCount() const;

    public:
        RingCollection& getHoles() { return _holes; }
        const RingCollection& getHoles() const { return _holes; }

    protected:
        //osg::ref_ptr<Ring> _boundary;
        RingCollection _holes;
    };

    /**
     * A collection of multiple geometries (aka, a "multi-part" geometry).
     */
    class OSGEARTHSYMBOLOGY_EXPORT MultiGeometry : public Geometry
    {
    public:
        MultiGeometry() { }
        MultiGeometry( const GeometryCollection& parts );
        MultiGeometry( const MultiGeometry& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );

        META_Object( osgEarthSymbology, MultiGeometry );

    public:
        virtual Type getType() const { return Geometry::TYPE_MULTI; }        
        virtual Type getComponentType() const;
        virtual int getTotalPointCount() const;

        // override
        virtual Geometry* cloneAs( const Geometry::Type& newType ) const;
        virtual bool isValid() const;
        virtual Bounds getBounds() const;

    public:
        GeometryCollection& getComponents() { return _parts; }
        const GeometryCollection& getComponents() const { return _parts; }

    protected:
        GeometryCollection _parts;
    };

    /**
     * Iterates over a Geometry object, returning each component Geometry
     * in turn. The iterator automatically traverses MultiGeometry objects,
     * returning their components. The interator NEVER returns an actual
     * MultiGeometry object.
     */
    class OSGEARTHSYMBOLOGY_EXPORT GeometryIterator
    {
    public:
        GeometryIterator( Geometry* geom );

        // set to TRUE to return MG children, but never an MG itself;
        // set to FALSE to return MGs and NOT their children.
        // default = TRUE
        bool& traverseMultiGeometry() {
            return _traverseMulti; }

        // set to TRUE to return Polygons AND Polygon hole geometries;
        // set to FALSE to only return Polygon (outer ring).
        // default = TRUE
        bool& traversePolygonHoles() {
            return _traversePolyHoles; }

        bool hasMore() const;
        Geometry* next();

    private:
        Geometry* _next;
        std::stack<Geometry*> _stack;
        bool _traverseMulti;
        bool _traversePolyHoles;

        void fetchNext();
    };

    class OSGEARTHSYMBOLOGY_EXPORT ConstGeometryIterator
    {
    public:
        ConstGeometryIterator( const Geometry* geom );

        // set to TRUE to return MG children, but never an MG itself;
        // set to FALSE to return MGs and NOT their children.
        // default = TRUE
        bool& traverseMultiGeometry() {
            return _traverseMulti; }

        // set to TRUE to return Polygons AND Polygon hole geometries;
        // set to FALSE to only return Polygon (outer ring).
        // default = TRUE
        bool& traversePolygonHoles() {
            return _traversePolyHoles; }

        bool hasMore() const;
        const Geometry* next();

    private:
        const Geometry* _next;
        std::stack<const Geometry*> _stack;
        bool _traverseMulti;
        bool _traversePolyHoles;

        void fetchNext();
    };

    typedef std::vector<osg::ref_ptr<Geometry> > GeometryList;

} } // namespace osgEarth::Symbology


#endif // OSGEARTHSYMBOLOGY_GEOMETRY_H

