/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2020 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/>
 */
#pragma once

#include <osgEarth/Common>
#include <osgEarth/ElevationLayer>
#include <osgEarth/ElevationPool>
#include <osgEarth/LayerReference>
#include <osgEarth/FeatureSource>
#include <osgEarth/Session>
#include <osgEarth/StyleSheet>

namespace osgEarth { namespace Contrib
{
    /**
     * Elevation layer that overlays modified elevation samples intended to
     * flatten the terrain around vector features. The use case is to make
     * roads flat or prevent rivers and lakes from sloping with the terrain.
     */
    class OSGEARTH_EXPORT FlatteningLayer : public ElevationLayer
    {
    public:
        class OSGEARTH_EXPORT Options : public ElevationLayer::Options {
        public:
            META_LayerOptions(osgEarth, Options, ElevationLayer::Options);
            OE_OPTION_LAYER(FeatureSource, featureSource);
            OE_OPTION_VECTOR(ConfigOptions, filters);
            OE_OPTION_LAYER(StyleSheet, styleSheet);
            OE_OPTION(NumericExpression, lineWidth, 10);
            OE_OPTION(NumericExpression, bufferWidth, 20);
            OE_OPTION(bool, fill, false);
            virtual Config getConfig() const;

        private:
            void fromConfig(const Config&);
        };

    public:
        META_Layer(osgEarth, FlatteningLayer, Options, ElevationLayer, FlattenedElevation);

        //! Feature source layer to get features from
        void setFeatureSource(FeatureSource* layer);
        FeatureSource* getFeatureSource() const { return options().featureSource().getLayer(); }

        //! Style sheet for optional scripting
        void setStyleSheet(StyleSheet* layer);
        StyleSheet* getStyleSheet() const { return options().styleSheet().getLayer(); }

        //! For line features, the width around the line to flatten
        void setLineWidth(const NumericExpression& value);
        const NumericExpression& getLineWidth() const;

        //! Width of the buffer between the flattened terrain and the natural terrain,
        //! which will serve as a transition area.
        void setBufferWidth(const NumericExpression& value);
        const NumericExpression& getBufferWidth() const;

        //! Whether to write all samples (default=false) with source elev instead of
        //! writing NO_DATA_VALUE where no features exist
        void setFill(const bool& value);
        const bool& getFill() const;

    public: // ElevationLayer

        void init() override;

        Status openImplementation() override;

        Config getConfig() const override;

    protected: // ElevationLayer

        GeoHeightField createHeightFieldImplementation(
            const TileKey& key,
            ProgressCallback* progress) const override;

        //! called by the map when this layer is added
        void addedToMap(const class Map*) override;

        //! called by the map when this layer is removed
        void removedFromMap(const class Map*) override;

    protected:

        virtual ~FlatteningLayer();

    private:

        osg::ref_ptr<ElevationPool> _pool;
        mutable ElevationPool::WorkingSet _elevWorkingSet;
        osg::ref_ptr<Session> _session;
        FeatureFilterChain _filterChain;

        GeoHeightField createFromFeatures(const TileKey& key, ProgressCallback* progress) const;
        GeoHeightField createFromSDF(const TileKey& key, ProgressCallback* progress) const;
    };

} }

OSGEARTH_SPECIALIZE_CONFIG(osgEarth::Contrib::FlatteningLayer::Options);
