/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2008-2019 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 OSGEARTH_JOIN_POINTSLINES_FEATUREFILTER_OPTIONS
#define OSGEARTH_JOIN_POINTSLINES_FEATUREFILTER_OPTIONS 1

#include <osgEarth/Common>
#include <osgEarth/FeatureSource>
#include <osgEarth/LayerReference>

namespace osgEarth { namespace Util
{
    using namespace osgEarth;

    class JoinPointsLinesFilterOptions : public ConfigOptions // NO EXPORT; header only
    {
    public:
        JoinPointsLinesFilterOptions( const ConfigOptions& opt = ConfigOptions() ) : ConfigOptions( opt )
        {
            _conf.set("driver", "joinpointslines");            
            fromConfig( _conf );
        }

        /** Features to load and use as boundary geometries */
        OE_OPTION_LAYER(FeatureSource, lineSource);

        optional<bool>& createPointFeatures() { return _createPointFeatures; }
        optional<bool> createPointFeatures() const { return _createPointFeatures; }

    public:
        Config getConfig() const {
            Config conf = ConfigOptions::getConfig();
            conf.key() = "join_points_lines";
            lineSource().set(conf, "line_features");
            return conf;
        }

    protected:
        void mergeConfig( const Config& conf ) {
            ConfigOptions::mergeConfig( conf );
            fromConfig( conf );
        }

    private:
        void fromConfig( const Config& conf ) {
            _createPointFeatures.init(false);
            lineSource().get(conf, "line_features");
            conf.get("create_point_features", createPointFeatures());
        }

        //optional<std::string>            _lineLayerName;
        //optional<FeatureSource::Options> _lineFeatures;
        optional<bool>                   _createPointFeatures;
    };

    class JoinPointsLinesFilter : public FeatureFilter, public JoinPointsLinesFilterOptions
    {
    public:
        JoinPointsLinesFilter(const ConfigOptions& options = ConfigOptions())
            : FeatureFilter(), JoinPointsLinesFilterOptions(options)
            {
            }

    public:
        Status initialize(const osgDB::Options* readOptions);

        void getLineFeatures(const GeoExtent& extent, FeatureList& features);

        FilterContext push(FeatureList& input, FilterContext& context);
    };

    struct PointEntry
    {
        PointEntry(Feature* feature)
            : pointFeature(feature), previous(DBL_MAX, DBL_MAX, DBL_MAX), next(DBL_MAX, DBL_MAX, DBL_MAX)
            {}
        PointEntry() : PointEntry(0L) {}
        osg::ref_ptr<Feature> pointFeature;
        FeatureList lineFeatures;
        osg::Vec3d previous;
        osg::Vec3d next;
    };

    // Map that ignores elevation component of points
    struct CompPoints
    {
        bool operator()(const osg::Vec3d& lhs, const osg::Vec3d& rhs) const
        {
            if (lhs.x() < rhs.x())
            {
                return true;
            }
            else if (lhs.x() > rhs.x())
            {
                return false;
            }
            else
            {
                return lhs.y() < rhs.y();
            }
        }
    };

    typedef std::map<osg::Vec3d, PointEntry, CompPoints> PointMap;
    inline PointEntry& getPoint(PointMap& map, const osg::Vec3d& coord)
    {
        return map[coord];
    }
    inline PointMap::iterator findPoint(PointMap& map, const osg::Vec3d& coord)
    {
        return map.find(coord);
    }
} } // namespace osgEarth::Util

#endif // OSGEARTH_JOIN_POINTSLINES_FEATUREFILTER_OPTIONS

