/* osgEarth
 * Copyright 2025 Pelican Mapping
 * MIT License
 */
#ifndef OSGEARTH_SPLAT_ZONE
#define OSGEARTH_SPLAT_ZONE 1

#include "Export"
#include "Surface"
#include <osgEarth/TerrainEngineNode>
#include <osg/BoundingBox>
#include <osg/Polytope>
#include <osg/StateSet>
#include <osg/NodeCallback>
#include <vector>

namespace osgDB {
    class Options;
}

namespace osgEarth { namespace Splat
{
    using namespace osgEarth;


    //........................................................................

    /**
     * Serializable options data for a Zone object
     */
    class OSGEARTHSPLAT_EXPORT ZoneOptions : public ConfigOptions
    {
    public:
        ZoneOptions(const ConfigOptions& conf = ConfigOptions()) : ConfigOptions(conf) {
            fromConfig(_conf);
        }

        //! Name of this zone (readable)
        optional<std::string> name() { return _name; }
        const optional<std::string> name() const { return _name; }

        //! Boundary set for this zone (optional)
        std::vector<osg::BoundingBox>& boundaries() { return _boundaries; }
        const std::vector<osg::BoundingBox>& boundaries() const { return _boundaries; }

        //! Surface rendering options
        optional<SurfaceOptions>& surface() { return _surface; }
        const optional<SurfaceOptions>& surface() const { return _surface; }

    protected:
        optional<std::string> _name;
        std::vector<osg::BoundingBox> _boundaries;
        optional<SurfaceOptions> _surface;

    public:
        void fromConfig(const Config& conf);
        Config getConfig() const;
    };

    typedef std::vector<ZoneOptions> ZoneOptionsVector;
    

    /**
     * A zone limits a particular surface or land cover layer to a set of
     * geographic boundaries.
     */
    class OSGEARTHSPLAT_EXPORT Zone : public osg::Referenced
    {
    public:
        struct Boundary
        {
            GeoExtent     extent;
            double        zmin, zmin2;
            double        zmax, zmax2;
            double        meanRadius2;
            osg::Polytope tope;
        };
        typedef std::vector<Boundary> Boundaries;

    public:
        Zone();

        Zone(const ZoneOptions& options);

        void setName(const std::string& name) { _name = name; }
        const std::string& getName() const { return _name; }

        Boundaries& getBoundaries() { return _boundaries; }
        const Boundaries& getBoundaries() const { return _boundaries; }

        void setSurface(Surface* surface) { _surface = surface; }
        Surface* getSurface() const { return _surface.get(); }

        bool contains(const osg::Vec3& points) const;

        void setUID(UID uid) { _uid = uid; }
        UID getUID() const { return _uid; }

        void resizeGLObjectBuffers(unsigned maxSize);
        void releaseGLObjects(osg::State* state) const;

    protected:
        virtual ~Zone() { }
        std::string                 _name;
        UID                         _uid;
        Boundaries                  _boundaries;
        osg::ref_ptr<Surface>       _surface;
        const ZoneOptions           _options;

    public:
        bool configure(const Map* map, const osgDB::Options* readOptions);
    };

    typedef std::vector<osg::ref_ptr<Zone> > Zones;
} } // namespace osgEarth::Splat

OSGEARTH_SPECIALIZE_CONFIG(osgEarth::Splat::ZoneOptions);

#endif // OSGEARTH_SPLAT_ZONE

