/* -*-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 OSGEARTHFEATURES_FEATURE_OGR_GEOM_UTILS
#define OSGEARTHFEATURES_FEATURE_OGR_GEOM_UTILS 1

#include <osgEarthSymbology/Geometry>
#include <osgEarth/StringUtils>
#include <osg/Notify>
#include <ogr_api.h>

using namespace osgEarth;
using namespace osgEarth::Features;

struct GeometryUtils
{
    static void
    populate( OGRGeometryH geomHandle, Symbology::Geometry* target, int numPoints )
    {
        for( int v = numPoints-1; v >= 0; v-- ) // reverse winding.. we like ccw
        {
            double x=0, y=0, z=0;
            OGR_G_GetPoint( geomHandle, v, &x, &y, &z );
            osg::Vec3d p( x, y, z );
            if ( target->size() == 0 || p != target->back() ) // remove dupes
                target->push_back( p );
        }
    }

    static Symbology::Polygon*
    createPolygon( OGRGeometryH geomHandle )
    {
        Symbology::Polygon* output = 0L;

        int numParts = OGR_G_GetGeometryCount( geomHandle );
        if ( numParts == 0 )
        {
            int numPoints = OGR_G_GetPointCount( geomHandle );
            output = new Symbology::Polygon( numPoints );
            populate( geomHandle, output, numPoints );
            output->open();
        }
        else if ( numParts > 0 )
        {
            for( int p = 0; p < numParts; p++ )
            {
                OGRGeometryH partRef = OGR_G_GetGeometryRef( geomHandle, p );
                int numPoints = OGR_G_GetPointCount( partRef );
                if ( p == 0 )
                {
                    output = new Symbology::Polygon( numPoints );
                    populate( partRef, output, numPoints );
                    //output->open();
                    output->rewind( Symbology::Ring::ORIENTATION_CCW );
                }
                else
                {
                    Symbology::Ring* hole = new Symbology::Ring( numPoints );
                    populate( partRef, hole, numPoints );
                    //hole->open();
                    hole->rewind( Symbology::Ring::ORIENTATION_CW );
                    output->getHoles().push_back( hole );
                }
            }
        }
        return output;
    }

    static Symbology::Geometry*
    createGeometry( OGRGeometryH geomHandle )
    {
        Symbology::Geometry* output = 0L;

        OGRwkbGeometryType wkbType = OGR_G_GetGeometryType( geomHandle );        
        
        if (
            wkbType == wkbPolygon ||
            wkbType == wkbPolygon25D )
        {
            output = createPolygon( geomHandle );
        }
        else if (
            wkbType == wkbLineString ||
            wkbType == wkbLineString25D )
        {
            int numPoints = OGR_G_GetPointCount( geomHandle );
            output = new Symbology::LineString( numPoints );
            populate( geomHandle, output, numPoints );
        }
        else if (
            wkbType == wkbLinearRing )
        {
            int numPoints = OGR_G_GetPointCount( geomHandle );
            output = new Symbology::Ring( numPoints );
            populate( geomHandle, output, numPoints );
        }
        else if ( 
            wkbType == wkbPoint ||
            wkbType == wkbPoint25D )
        {
            int numPoints = OGR_G_GetPointCount( geomHandle );
            output = new Symbology::PointSet( numPoints );
            populate( geomHandle, output, numPoints );
        }
        else if (
            wkbType == wkbGeometryCollection ||
            wkbType == wkbGeometryCollection25D ||
            wkbType == wkbMultiPoint ||
            wkbType == wkbMultiPoint25D ||
            wkbType == wkbMultiLineString ||
            wkbType == wkbMultiLineString25D ||
            wkbType == wkbMultiPolygon ||
            wkbType == wkbMultiPolygon25D )
        {
            Symbology::MultiGeometry* multi = new Symbology::MultiGeometry();

            int numGeoms = OGR_G_GetGeometryCount( geomHandle );
            for( int n=0; n<numGeoms; n++ )
            {
                OGRGeometryH subGeomRef = OGR_G_GetGeometryRef( geomHandle, n );
                if ( subGeomRef )
                {
                    Symbology::Geometry* geom = createGeometry( subGeomRef );
                    if ( geom ) multi->getComponents().push_back( geom );
                }
            } 

            output = multi;
        }

        return output;
    }

    static Symbology::Geometry*
    createGeometryFromWKT( const std::string& wkt )
    {       
        OGRwkbGeometryType type = 
            startsWith( wkt, "POINT" ) ? wkbPoint :
            startsWith( wkt, "LINESTRING" ) ? wkbLineString :
            startsWith( wkt, "POLYGON" ) ? wkbPolygon :
            startsWith( wkt, "MULTIPOINT" ) ? wkbMultiPoint :
            startsWith( wkt, "MULTILINESTRING" ) ? wkbMultiLineString :
            startsWith( wkt, "MULTIPOLYGON" ) ? wkbMultiPolygon :
            startsWith( wkt, "GEOMETRYCOLLECTION" ) ? wkbGeometryCollection :
            wkbNone;
            
        Symbology::Geometry* output = 0L;
        if ( type != wkbNone )
        {
            OGRGeometryH geom = OGR_G_CreateGeometry( type );
            if ( geom )
            {
                char* ptr = (char*)wkt.c_str();
                if ( OGRERR_NONE == OGR_G_ImportFromWkt( geom, &ptr ) )
                {
                    output = createGeometry( geom );
                    OGR_G_DestroyGeometry( geom );
                }
                else
                {
                    OE_NOTICE
                        << "OGR Feature Source: malformed WKT geometry" << std::endl;
                }
            }
        }
        return output;
    }
};


#endif // OSGEARTHFEATURES_FEATURE_OGR_GEOM_UTILS

