/* osgEarth
* Copyright 2008-2012 Pelican Mapping
* MIT License
*/
#pragma once

#include <osgEarthProcedural/Export>
#include <osgEarthProcedural/BiomeLayer>
#include <osgEarthProcedural/LifeMapLayer>
#include <osgEarth/GLUtils>
#include <osgEarth/LayerReference>
#include <osgEarth/TextureArena>

namespace osgEarth { namespace Procedural
{
    using namespace osgEarth;

    //! Layer that renders geotypical textures on the terrain based on
    //! Biome classification data ("texture splatting").
    class OSGEARTHPROCEDURAL_EXPORT TextureSplattingLayer : public VisibleLayer
    {
    public:
        class OSGEARTHPROCEDURAL_EXPORT Options : public VisibleLayer::Options
        {
        public:
            META_LayerOptions(osgEarthProcedural, Options, VisibleLayer::Options);
            OE_OPTION(int, numLevels, 1);
            OE_OPTION(bool, useHexTiler, true);
            OE_OPTION(bool, enableHexTilerAnisotropicFiltering, false);
            OE_OPTION(float, hexTilerGradientBias, 0.0f);
            OE_OPTION(float, normalMapPower, 1.0f);
            OE_OPTION(float, lifeMapMaskThreshold, 0.0f);
            OE_OPTION(float, displacementDepth, 0.1f);
            OE_OPTION(unsigned, maxTextureSize, 63356);
            Config getConfig() const override;
        private:
            void fromConfig(const Config& conf);
        };

    public:
        META_Layer(osgEarthProcedural, TextureSplattingLayer, Options, VisibleLayer, ProceduralImage);

        //BiomeLayer* getBiomeLayer() const;

        //LifeMapLayer* getLifeMapLayer() const;

        //! Whether to use a shader that obfuscates the repeating textures 
        //! found in texture splatting. Incurs a small GPU performance penalty.
        //! Default is true.
        void setUseHexTiler(bool value);
        bool getUseHexTiler() const;

        //! Whether the hex tiler, when active, should support anisotropic filtering.
        //! When true, splatting quality is improved at the expense of GPU performance.
        //! Default is false.
        void setEnableHexTilerAnisotropicFiltering(bool value);
        bool getEnableHexTilerAnisotropicFiltering() const;

        void setHexTilerGradientBias(float value);
        float getHexTilerGradientBias() const;

        //! Multiplication power to apply to normal maps, to artificially
        //! enhance to effect of splatted normals. [0..1] Default is 1.0
        void setNormalMapPower(float value);
        float getNormalMapPower() const;

        //! The lifemap threshold (dense+lush+rugged) below which the 
        //! splatting layer will beome transparent and allow lower layer(s)
        //! to show through. [0..1] Default = 1.0 (off)
        void setLifeMapMaskThreshold(float value);
        float getLifeMapMaskThreshold() const;

        //! Amount by which the textures' displacement map affects
        //! blending across splatting textures. [0..1, default = 0.1]
        void setDisplacementDepth(float value);
        float getDisplacementDepth() const;

        //! Sets a maximum size for splatting textures on the GPU
        void setMaxTextureSize(unsigned value);
        unsigned getMaxTextureSize() const;


    protected:

        //! Override post-ctor init
        void init() override;
        void prepareForRendering(TerrainEngine* engine) override;

    public:

        void addedToMap(const Map* map) override;
        void removedFromMap(const Map* map) override;
        void update(osg::NodeVisitor&) override;
        void resizeGLObjectBuffers(unsigned maxSize) override;
        void releaseGLObjects(osg::State* state) const override;

    protected:        
        struct Materials
        {
            using Ptr = std::shared_ptr<Materials>;
            osg::ref_ptr<TextureArena> _arena;
            osg::ref_ptr<osg::Uniform> _textureScales;
            std::vector<const MaterialAsset*> _assets;
        };

        osg::ref_ptr<const Profile> _mapProfile;
        Future<Materials::Ptr> _materialsJob;
        Materials::Ptr _materials;
        TextureImageUnitReservation _noiseUnit;
        osg::observer_ptr<BiomeLayer> _biomeLayer_weak;
        osg::observer_ptr<LifeMapLayer> _lifeMapLayer_weak;

        void buildStateSets();

        Materials::Ptr loadMaterials(const AssetCatalog&, Cancelable*) const;
    };
} } // namespace osgEarth::Procedural

