#pragma once

#include <osgEarthImGui/ImGuiPanel>
#include <osgEarthProcedural/TextureSplattingLayer>
#include <algorithm>

namespace osgEarth
{
    namespace Procedural
    {
        using namespace osgEarth;

        struct TextureSplattingLayerGUI : public ImGuiPanel
        {
            osg::observer_ptr<TextureSplattingLayer> _tslayer;
            bool _installed;
            float _blend_start;
            float _blend_end;
            float _blend_rgbh_mix;
            float _blend_normal_mix;
            float _ao_power;
            float _brightness;
            float _contrast;
            float _dense_contrast;
            float _dense_brightness;
            float _lush_brightness;
            float _snow;
            float _snow_min_elev;
            float _snow_max_elev;
            bool _lifemap_direct;
            float _rugged_power;
            float _dense_power;
            float _lush_power;

            TextureSplattingLayerGUI() : ImGuiPanel("Splatting")
            {
                _installed = false;
                _blend_start = 2500.0f;
                _blend_end = 500.0f;
                _blend_rgbh_mix = 0.85f;
                _blend_normal_mix = 0.85f;
                _ao_power = 1.0f;
                _brightness = 1.0f;
                _contrast = 1.0f;
                _dense_contrast = 0.0f;
                _dense_brightness = 0.0f;
                _lush_brightness = 0.0f;
                _snow = 0.0f;
                _snow_min_elev = 0.0f;
                _snow_max_elev = 3500.0f;

                _lifemap_direct = false;
                _rugged_power = 1.0;
                _dense_power = 1.0;
                _lush_power = 1.0;
            }

            void load(const Config& conf) override
            {
                conf.get("brightness", _brightness);
                conf.get("contrast", _contrast);
                conf.get("ao_power", _ao_power);
                //conf.get("dense_contrast", _dense_contrast);
                //conf.get("dense_brightness", _dense_brightness);
                //conf.get("snow", _snow);
                //conf.get("snow_min_elev", _snow_min_elev);
                //conf.get("snow_max_elev", _snow_max_elev);
            }

            void save(Config& conf) override
            {
                conf.set("brightness", _brightness);
                conf.set("contrast", _contrast);
                conf.set("ao_power", _ao_power);
                //conf.set("dense_contrast", _dense_contrast);
                //conf.set("dense_brightness", _dense_brightness);
                //conf.set("snow", _snow);
                //conf.set("snow_min_elev", _snow_min_elev);
                //conf.set("snow_max_elev", _snow_max_elev);
            }

            void draw(osg::RenderInfo& ri) override
            {
                if (!isVisible())
                    return;

                if (!findLayerOrHide(_tslayer, ri))
                    return;

                if (!_installed)
                {
                    // activate tweakable uniforms
                    stateset(ri)->setDataVariance(osg::Object::DYNAMIC);
                    stateset(ri)->removeDefine("OE_TWEAKABLE");
                    stateset(ri)->setDefine("OE_TWEAKABLE", 0x7);

                    //stateset(ri)->setDefine("OE_SNOW", 0x7);
                    stateset(ri)->removeDefine("OE_LIFEMAP_DIRECT");
                    stateset(ri)->setDefine("OE_LIFEMAP_DIRECT", "0", 0x7);
                    _installed = true;
                }

                ImGui::Begin(name(), visible());
                {
                    if (ImGuiLTable::Begin("Splat"))
                    {
                        auto normalPower = _tslayer->getNormalMapPower();
                        if (ImGuiLTable::SliderFloat("Normal power", &normalPower, 0.0f, 16.0f, "%.1f", 0))
                            _tslayer->setNormalMapPower(normalPower);

                        auto dispacementDepth = _tslayer->getDisplacementDepth();
                        if (ImGuiLTable::SliderFloat("Displacement depth", &dispacementDepth, 0.0f, 1.0f, "%.2f", ImGuiSliderFlags_Logarithmic))
                            _tslayer->setDisplacementDepth(dispacementDepth);

                        auto lifemapthreshold = _tslayer->getLifeMapMaskThreshold();
                        if (ImGuiLTable::SliderFloat("LifeMap threshold", &lifemapthreshold, 0.0f, 10.0f, "%.1f", ImGuiSliderFlags_Logarithmic))
                            _tslayer->setLifeMapMaskThreshold(lifemapthreshold);

                        ImGuiLTable::SliderFloat("AO power", &_ao_power, 0.0f, 16.0f);
                        stateset(ri)->addUniform(new osg::Uniform("ao_power", _ao_power), 0x7);

                        if (_tslayer->options().numLevels() > 1)
                        {
                            ImGuiLTable::SliderFloat("Level blend start (m)", &_blend_start, 0.0f, 5000.0f);
                            stateset(ri)->addUniform(new osg::Uniform("oe_splat_blend_start", _blend_start), 0x7);

                            ImGuiLTable::SliderFloat("Level blend end (m)", &_blend_end, 0.0f, 5000.0f);
                            stateset(ri)->addUniform(new osg::Uniform("oe_splat_blend_end", _blend_end), 0x7);

                            ImGuiLTable::SliderFloat("RGBH mix", &_blend_rgbh_mix, 0.0f, 1.0f);
                            stateset(ri)->addUniform(new osg::Uniform("oe_splat_blend_rgbh_mix", _blend_rgbh_mix), 0x7);

                            ImGuiLTable::SliderFloat("Normal mix", &_blend_normal_mix, 0.0f, 1.0f);
                            stateset(ri)->addUniform(new osg::Uniform("oe_splat_blend_normal_mix", _blend_normal_mix), 0x7);
                        }

                        ImGuiLTable::SliderFloat("Global brightness", &_brightness, 0.0f, 4.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_splat_brightness", _brightness), 0x7);

                        ImGuiLTable::SliderFloat("Global contrast", &_contrast, 0.0f, 4.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_splat_contrast", _contrast), 0x7);

#if 0
                        ImGuiLTable::SliderFloat("Density contrast boost", &_dense_contrast, -1.0f, 1.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_dense_contrast", _dense_contrast), 0x7);

                        ImGuiLTable::SliderFloat("Density brightness boost", &_dense_brightness, -1.0f, 1.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_dense_brightness", _dense_brightness), 0x7);

                        ImGuiLTable::SliderFloat("Lush brightness adjust", &_lush_brightness, -1.0f, 1.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_lush_brightness", _lush_brightness), 0x7);

                        ImGuiLTable::SliderFloat("Snow", &_snow, 0.0f, 1.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_snow", _snow), 0x7);

                        ImGuiLTable::SliderFloat("Snow bottom elev", &_snow_min_elev, 0.0f, 2500.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_snow_min_elev", _snow_min_elev), 0x7);

                        ImGuiLTable::SliderFloat("Snow top elev", &_snow_max_elev, 2500.0f, 5000.0f);
                        stateset(ri)->addUniform(new osg::Uniform("oe_snow_max_elev", _snow_max_elev), 0x7);
#endif

                        ImGui::Separator();

                        bool hex_tiler = _tslayer->getUseHexTiler();
                        if (ImGuiLTable::Checkbox("Hex tile splatting", &hex_tiler))
                        {
                            _tslayer->setUseHexTiler(hex_tiler);
                        }

                        if (hex_tiler)
                        {
                            bool af = _tslayer->getEnableHexTilerAnisotropicFiltering();
                            if (ImGuiLTable::Checkbox("Enable anisotropic filtering", &af))
                            {
                                _tslayer->setEnableHexTilerAnisotropicFiltering(af);
                            }

                            if (af)
                            {
                                float bias = _tslayer->getHexTilerGradientBias();
                                if (ImGuiLTable::SliderFloat("Gradient bias", &bias, -3.0f, 0.0f))
                                {
                                    _tslayer->setHexTilerGradientBias(bias);
                                }
                            }
                        }
                        ImGuiLTable::End();
                    }

                    // lifemap adjusters
                    ImGui::Separator();

                    if (ImGui::Checkbox("LifeMap Direct Set", &_lifemap_direct))
                    {
                        if (_lifemap_direct) {
                            stateset(ri)->removeDefine("OE_LIFEMAP_DIRECT");
                            stateset(ri)->setDefine("OE_LIFEMAP_DIRECT", "1", 0x7);
                            _rugged_power = std::min(_rugged_power, 1.0f);
                            _dense_power = std::min(_dense_power, 1.0f);
                            _lush_power = std::min(_lush_power, 1.0f);
                        }
                        else {
                            stateset(ri)->removeDefine("OE_LIFEMAP_DIRECT");
                            stateset(ri)->setDefine("OE_LIFEMAP_DIRECT", "0", 0x7);
                        }
                    }

                    float lm_max = _lifemap_direct ? 1.0f : 4.0f;

                    if (ImGuiLTable::Begin("LifeMapAdjust"))
                    {
                        ImGuiLTable::SliderFloat(_lifemap_direct ? "Dense" : "Dense multiplier", &_dense_power, 0.0f, lm_max);
                        stateset(ri)->getOrCreateUniform("dense_power", osg::Uniform::FLOAT)->set(_dense_power);

                        ImGuiLTable::SliderFloat(_lifemap_direct ? "Rugged" : "Rugged multiplier", &_rugged_power, 0.0f, lm_max);
                        stateset(ri)->getOrCreateUniform("rugged_power", osg::Uniform::FLOAT)->set(_rugged_power);

                        ImGuiLTable::SliderFloat(_lifemap_direct ? "Lush" : "Lush multiplier", &_lush_power, 0.0f, lm_max);
                        stateset(ri)->getOrCreateUniform("lush_power", osg::Uniform::FLOAT)->set(_lush_power);

                        unsigned maxTex = _tslayer->getMaxTextureSize();
                        const char* maxTexItems[] = { "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384" };
                        unsigned maxTexIndex = 0;
                        for (maxTexIndex = 0; maxTex > (unsigned)::atoi(maxTexItems[maxTexIndex]) && maxTexIndex < IM_ARRAYSIZE(maxTexItems) - 1; ++maxTexIndex);
                        if (ImGuiLTable::BeginCombo("Max tex size", maxTexItems[maxTexIndex])) {
                            for (int n = 0; n < IM_ARRAYSIZE(maxTexItems); n++) {
                                const bool is_selected = (maxTexIndex == n);
                                if (ImGui::Selectable(maxTexItems[n], is_selected)) {
                                    maxTexIndex = n;
                                    _tslayer->setMaxTextureSize(atoi(maxTexItems[maxTexIndex]));
                                }
                                if (is_selected)
                                    ImGui::SetItemDefaultFocus();
                            }
                            ImGuiLTable::EndCombo();
                        }

                        ImGuiLTable::End();
                    }
                }
                ImGui::End();
            }
        };
    }
}
