/* -*-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/>
 */

#ifndef OSGEARTH_SDF_H
#define OSGEARTH_SDF_H

#include <osgEarth/Common>
#include <osgEarth/Feature>
#include <osgEarth/GLUtils>
#include <osg/Image>

namespace osg {
    class Uniform;
}

namespace osgEarth { namespace Util
{
    class Session;

    class OSGEARTH_EXPORT SDFGenerator
    {
    public:
        SDFGenerator();

        //! Allocate a new SDF image
        //! @param size X and Y dimension; must be a power of 2
        //! @param extent Geospatial extent
        GeoImage allocateSDF(
            unsigned size,
            const GeoExtent& extent,
            GLenum pixelFormat =GL_RED) const;

        //! Encode a nearest-neighbor field for vector data. Each pixel contains
        //! the coordinates of the nearest pixel with feature data. The coordinate
        //! system is [x,y] relate to the lower-left corner.
        //!
        //! You can pass this field to the createDistanceField function to convert
        //! it to a distance field.
        //!
        //! @param features Features to use for distance calculation
        //! @param nnfieldSize Size of raster to create
        //! @param extent Geospatial extent of the raster
        //! @param nnfield Input/output nearest neighbor field
        //! @param inverted Whether to test empty pixels instead of full pixels
        //! @param progress Progress tracker object
        //! @return True upon success
        bool createNearestNeighborField(
            const FeatureList& features,
            unsigned nnfieldSize,
            const GeoExtent& extent,
            bool inverted,
            GeoImage& nnfield,
            Cancelable* progress) const;

        //! Encode a nearest-neighbor field from a raster image.
        //! The input raster is typically rasterized feature data, but can be
        //! anything.
        //!
        //! You can pass this field to the createDistanceField function to convert
        //! it to a distance field.
        //!
        //! @param input Input raster data
        //! @param inverted Whether to test empty pixels instead of full pixels
        //! @param nnfield Input/output nearest neighbor field
        //! @param progress Progress tracker object
        //! @return True upon success
        bool createNearestNeighborField(
            const GeoImage& input,
            bool inverted,
            GeoImage& nnfield,
            Cancelable* progress) const;

        //! Encode a distance field into an image.
        //! @param nnfield Input NN Field to conver to a distance field
        //! @param sdf Distance field to populate (additively). Distance values are
        //!    in the range [0..1] normalized from the min_dist and max_dist inputs.
        //! @param min_dist Distances <= min_dist are encoded as 0.0
        //! @param max_dist Distances >= max_dist are encoded as 1.0
        //! @param progress Progress tracker object
        void createDistanceField(
            const GeoImage& nnfield,
            GeoImage& sdf,
            float tile_size,
            float min_dist,
            float max_dist,
            Cancelable* progress) const;

        //! Whether to permit use of the GPU. Set this to true if there
        //! is a running frame loop with an active graphics context available
        //! and you are willing to shunt the processing to the GPU.
        void setUseGPU(bool value);

    private:

        void compute_nnf_on_gpu(osg::Image* buf) const;
        void compute_nnf_on_cpu(osg::Image* buf) const;

        struct NNFSession : public ComputeImageSession
        {
        public:
            NNFSession() : _L_uniform(-1) { }
            void renderImplementation(osg::State* state) override;
            GLint _L_uniform;
        };

        PerThreadComputeSession<NNFSession> _compute;
        osg::ref_ptr<osg::Program> _program;
        bool _useGPU;

    };
} } // osgEarth::Util


#endif // OSGEARTH_SDF_H
