// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#pragma once

#include "framebuffer.h"
#include "blur_shader.h"
#include "noise_texture.h"
#include "imgui.h"

#include <memory>
#include <string>

#ifdef DRAGONX_USE_DX11
#include <d3d11.h>
#endif

namespace dragonx {
namespace ui {
namespace effects {

/**
 * @brief Acrylic material parameters
 * 
 * These parameters control the appearance of the acrylic effect,
 * matching Microsoft's Fluent Design system.
 */
struct AcrylicParams {
    // Tint color (RGBA, pre-multiplied alpha)
    ImVec4 tintColor = ImVec4(0.1f, 0.1f, 0.12f, 1.0f);
    
    // Tint opacity (0.0 = fully transparent, 1.0 = fully opaque)
    float tintOpacity = 0.75f;
    
    // Luminosity opacity (controls saturation pass-through)
    // Lower values = more desaturated/milky appearance
    float luminosityOpacity = 0.5f;
    
    // Blur radius in pixels (typical: 20-60)
    float blurRadius = 30.0f;

    // When true, blurRadius is used AS-IS — NOT scaled by the user's blur-strength slider
    // (blurRadiusMultiplier). The modal backdrop sets this so its blur is a fixed value, independent
    // of the global acrylic slider.
    bool absoluteBlurRadius = false;

    // Noise texture opacity (typical: 0.02-0.04)
    float noiseOpacity = 0.02f;
    
    // Fallback color when acrylic is disabled.
    // The alpha channel also controls the glass opacity of the blurred
    // background when acrylic IS active (lower alpha = more see-through).
    ImVec4 fallbackColor = ImVec4(0.15f, 0.15f, 0.18f, 0.5f);
    
    // Whether acrylic is enabled (false = use fallback)
    bool enabled = true;
};

/**
 * @brief Acrylic quality levels
 */
enum class AcrylicQuality {
    Off,      // Solid fallback color only
    Low,      // Single blur pass, downsampled
    Medium,   // Two blur passes, slight downsample
    High      // Full quality blur
};

/**
 * @brief Acrylic fallback modes for graceful degradation
 */
enum class AcrylicFallback {
    None,         // Full acrylic (blur + tint + noise)
    TintedOnly,   // No blur, just semi-transparent tint overlay
    Solid         // Opaque fallback color only
};

/**
 * @brief Acrylic settings for accessibility and performance
 */
struct AcrylicSettings {
    bool enabled = true;              // Master toggle
    bool reducedTransparency = false; // Accessibility: use solid colors
    float uiOpacity = 1.0f;          // Card/sidebar opacity multiplier (0.3–1.0)
    AcrylicQuality quality = AcrylicQuality::Medium;  // Medium default for balance of quality/performance
    float blurRadiusMultiplier = 1.0f; // Scales all blur radii (0.5 - 2.0)
    float noiseOpacityMultiplier = 1.0f; // Scales noise opacity (0.0 - 2.0)
    
    // Auto-disable conditions (checked each frame)
    bool disableOnBattery = false;    // Disable when on battery power
    bool disableOnLowFPS = false;     // Disable if FPS drops below threshold
    float lowFPSThreshold = 30.0f;    // FPS threshold for auto-disable
};

/**
 * @brief System capabilities for acrylic effects
 */
struct AcrylicCapabilities {
    bool hasFramebufferSupport = false;
    bool hasShaderSupport = false;
    bool hasTextureSupport = false;
    int maxTextureSize = 0;
    std::string glVersion;
    std::string glRenderer;
    bool isLowEndGPU = false;
    bool isOnBattery = false;        // Linux: check /sys/class/power_supply
};

/**
 * @brief Main acrylic material rendering system
 * 
 * Implements Microsoft Fluent Design's acrylic material effect:
 * - Gaussian blur of background content
 * - Tint color overlay
 * - Luminosity blend for depth
 * - Subtle noise texture for grain
 */
class AcrylicMaterial {
public:
    AcrylicMaterial();
    ~AcrylicMaterial();
    
    // Non-copyable
    AcrylicMaterial(const AcrylicMaterial&) = delete;
    AcrylicMaterial& operator=(const AcrylicMaterial&) = delete;
    
    /**
     * @brief Initialize the acrylic system
     * @return true if successful
     */
    bool init();
    
    /**
     * @brief Release all resources
     */
    void shutdown();
    
    /**
     * @brief Check if system is initialized
     */
    bool isInitialized() const { return initialized_; }
    
    /**
     * @brief Update internal buffers for new viewport size
     * @param width Viewport width
     * @param height Viewport height
     */
    void resize(int width, int height);
    
    /**
     * @brief Capture current screen content for blur source
     * 
     * Call this BEFORE rendering any acrylic surfaces.
     * Only recaptures when the background is marked dirty.
     */
    void captureBackground();

    /**
     * @brief Capture background directly from the current framebuffer.
     *
     * Intended to be called from an ImGui draw callback inserted at
     * the end of the BackgroundDrawList.  At that point during
     * RenderDrawData(), only the background (gradient / image / noise)
     * has been rasterized — no UI elements are in the framebuffer yet.
     * This gives a clean background capture for acrylic blur.
     *
     * Respects the dirtyFrames_ counter so the capture and blur
     * are only recomputed when the background actually changes
     * (resize, theme change, etc.).
     */
    void captureBackgroundDirect();

    /**
     * @brief Capture the LIVE framebuffer (whatever has been drawn so far this
     * frame), bypassing the dirtyFrames_ gate. Used by a full-window modal
     * overlay to blur the live UI behind it: insert GetLiveCaptureCallback()
     * at the start of the overlay window's draw list so it fires mid-pass,
     * after the app UI is rasterized and before the overlay's own backdrop.
     * Always forces a re-blur of the fresh capture.
     */
    void captureLiveFramebuffer();

    /**
     * @brief Returns true once a valid background capture has been made.
     *
     * drawRect() checks this and falls back to a tinted fill on the
     * very first frame before the callback has fired.
     */
    bool hasValidCapture() const { return hasValidCapture_; }

    /// Mark background as needing recapture (call on resize, theme change, etc.)
    /// Uses a 2-frame counter so the capture re-runs after the new background
    /// has actually been rendered (theme switch happens mid-frame, before the
    /// BackgroundDrawList is rebuilt with new colors/images).
    void markBackgroundDirty() { dirtyFrames_ = 2; }
    
    /**
     * @brief Render an acrylic-filled rectangle
     * 
     * @param drawList ImGui draw list to add commands to
     * @param pMin Top-left corner in screen coordinates
     * @param pMax Bottom-right corner in screen coordinates
     * @param params Acrylic parameters
     * @param rounding Corner rounding
     */
    void drawRect(ImDrawList* drawList, const ImVec2& pMin, const ImVec2& pMax,
                  const AcrylicParams& params, float rounding = 0.0f);
    
    /**
     * @brief Get the blurred background texture for manual rendering
     */
    ImTextureID getBlurredTexture() const;
    
    /**
     * @brief Get the noise texture
     */
    ImTextureID getNoiseTexture() const;
    
    // ========================================================================
    // Settings
    // ========================================================================
    
    /**
     * @brief Set global acrylic quality
     */
    void setQuality(AcrylicQuality quality);
    AcrylicQuality getQuality() const { return settings_.quality; }
    
    /**
     * @brief Enable/disable acrylic globally
     */
    void setEnabled(bool enabled) { settings_.enabled = enabled; }
    bool isEnabled() const { return settings_.enabled; }
    
    /**
     * @brief Set blur radius multiplier (scales all blur radii)
     * @param multiplier Value from 0.0 to 5.0
     */
    void setBlurMultiplier(float multiplier) { 
        float clamped = std::max(0.0f, std::min(5.0f, multiplier));
        settings_.blurRadiusMultiplier = clamped;
    }
    float getBlurMultiplier() const { return settings_.blurRadiusMultiplier; }
    
    /**
     * @brief Set reduced transparency mode (accessibility)
     */
    void setReducedTransparency(bool reduced) { settings_.reducedTransparency = reduced; }
    bool getReducedTransparency() const { return settings_.reducedTransparency; }

    /**
     * @brief Set UI opacity multiplier for cards/sidebar (0.3–1.0, 1=opaque)
     */
    void setUIOpacity(float opacity) {
        settings_.uiOpacity = std::max(0.3f, std::min(1.0f, opacity));
    }
    float getUIOpacity() const { return settings_.uiOpacity; }

    /**
     * @brief Set noise opacity multiplier (0.0 = no noise, 1.0 = default, 2.0 = double)
     */
    void setNoiseOpacityMultiplier(float m) {
        settings_.noiseOpacityMultiplier = std::max(0.0f, std::min(5.0f, m));
    }
    float getNoiseOpacityMultiplier() const { return settings_.noiseOpacityMultiplier; }
    
    /**
     * @brief Get full settings struct
     */
    const AcrylicSettings& getSettings() const { return settings_; }
    void setSettings(const AcrylicSettings& settings) { settings_ = settings; }
    
    // ========================================================================
    // Fallback System
    // ========================================================================
    
    /**
     * @brief Detect system capabilities and determine fallback mode
     * 
     * Checks OpenGL capabilities, GPU info, and user preferences to
     * determine the appropriate rendering mode.
     * 
     * @return The recommended fallback mode
     */
    AcrylicFallback detectFallback() const;
    
    /**
     * @brief Get current fallback mode (cached)
     */
    AcrylicFallback getCurrentFallback() const { return currentFallback_; }
    
    /**
     * @brief Force a specific fallback mode
     */
    void setForcedFallback(AcrylicFallback fallback) { 
        forcedFallback_ = fallback; 
        hasForcedFallback_ = true;
    }
    
    /**
     * @brief Clear forced fallback and use auto-detection
     */
    void clearForcedFallback() { hasForcedFallback_ = false; }
    
    /**
     * @brief Get detected system capabilities
     */
    const AcrylicCapabilities& getCapabilities() const { return capabilities_; }
    
    /**
     * @brief Refresh capability detection (call after context changes)
     */
    void refreshCapabilities();
    
    // ========================================================================
    // Preset Parameters
    // ========================================================================
    
    /**
     * @brief Get dark theme acrylic preset
     */
    static AcrylicParams getDarkPreset();
    
    /**
     * @brief Get light theme acrylic preset
     */
    static AcrylicParams getLightPreset();
    
    /**
     * @brief Get DragonX branded acrylic preset
     */
    static AcrylicParams getDragonXPreset();
    
    /**
     * @brief Get popup/dialog acrylic preset
     */
    static AcrylicParams getPopupPreset();
    
    /**
     * @brief Get sidebar acrylic preset
     */
    static AcrylicParams getSidebarPreset();

private:
    /**
     * @brief Apply blur passes to captured content
     */
    void applyBlur(float radius, bool ignoreMultiplier = false);
    
    /**
     * @brief Composite final acrylic appearance
     */
    void compositeAcrylic(const ImVec2& pMin, const ImVec2& pMax,
                          const AcrylicParams& params);
    
    /**
     * @brief Check if effect should be skipped (settings/quality)
     */
    bool shouldSkipEffect() const;
    
    /**
     * @brief Draw tinted-only fallback (no blur)
     */
    void drawTintedRect(ImDrawList* drawList, const ImVec2& pMin, const ImVec2& pMax,
                        const AcrylicParams& params, float rounding);
    
    bool initialized_ = false;
    AcrylicSettings settings_;  // All settings in one struct
    
    // Fallback system
    AcrylicCapabilities capabilities_;
    AcrylicFallback currentFallback_ = AcrylicFallback::None;
    AcrylicFallback forcedFallback_ = AcrylicFallback::None;
    bool hasForcedFallback_ = false;
    
    // Screen dimensions
    int viewportWidth_ = 0;
    int viewportHeight_ = 0;
    
    // Noise texture config
    int noiseTextureSize_ = 256;  // Texture resolution (larger = less repetition)

    // Caching for performance
    float lastBlurRadius_ = 0.0f;
    bool blurCacheValid_ = false;
    int  dirtyFrames_ = 2;  // >0 means recapture needed; counts down each frame
    bool hasValidCapture_ = false; // true once a clean BG capture exists
    
    // Capture and blur framebuffers
    Framebuffer captureBuffer_;
    FramebufferPingPong blurBuffers_;
    
    // Shaders
    BlurShader blurShader_;
    
    // Fullscreen quad for post-processing
    FullscreenQuad quad_;
    
    // Noise texture
    NoiseTexture noiseTexture_;
    
    // Composite shader (tint + noise overlay)
    GLuint compositeShader_ = 0;
    GLint uCompositeTintColor_ = -1;
    GLint uCompositeTintOpacity_ = -1;
    GLint uCompositeLuminosity_ = -1;
    GLint uCompositeNoiseOpacity_ = -1;
    GLint uCompositeBlurTex_ = -1;
    GLint uCompositeNoiseTex_ = -1;
    GLint uCompositeTexScale_ = -1;

#ifdef DRAGONX_USE_DX11
    // ---- DX11 acrylic resources (used instead of GL objects above) ----
    ID3D11Device*              dx_device_       = nullptr;
    ID3D11DeviceContext*       dx_context_      = nullptr;

    // Capture buffer (full viewport resolution)
    ID3D11Texture2D*           dx_captureTex_   = nullptr;
    ID3D11ShaderResourceView*  dx_captureSRV_   = nullptr;
    ID3D11RenderTargetView*    dx_captureRTV_   = nullptr;

    // Blur ping-pong buffers (possibly downscaled)
    ID3D11Texture2D*           dx_blurTex_[2]   = {};
    ID3D11ShaderResourceView*  dx_blurSRV_[2]   = {};
    ID3D11RenderTargetView*    dx_blurRTV_[2]   = {};
    int dx_blurWidth_ = 0;
    int dx_blurHeight_ = 0;
    int dx_blurCurrent_ = 0;   // which buffer is "source"

    // Shaders & pipeline objects
    ID3D11VertexShader*  dx_blurVS_       = nullptr;
    ID3D11PixelShader*   dx_blurPS_        = nullptr;
    ID3D11InputLayout*   dx_inputLayout_  = nullptr;
    ID3D11Buffer*        dx_blurCB_       = nullptr;
    ID3D11Buffer*        dx_vertexBuf_    = nullptr;
    ID3D11SamplerState*        dx_sampler_      = nullptr;
    ID3D11RasterizerState*      dx_blurRS_       = nullptr;
    ID3D11BlendState*           dx_blurBS_       = nullptr;
    ID3D11DepthStencilState*    dx_blurDSS_      = nullptr;

    // Noise texture (DX11)
    ID3D11Texture2D*           dx_noiseTex_  = nullptr;
    ID3D11ShaderResourceView*  dx_noiseSRV_  = nullptr;

    // Internal helpers
    bool dx_initPipeline();
    void dx_releasePipeline();
    void dx_createRenderTarget(ID3D11Texture2D*& tex,
                               ID3D11ShaderResourceView*& srv,
                               ID3D11RenderTargetView*& rtv,
                               int w, int h,
                               DXGI_FORMAT format);
    void dx_releaseRenderTarget(ID3D11Texture2D*& tex,
                                ID3D11ShaderResourceView*& srv,
                                ID3D11RenderTargetView*& rtv);
#endif // DRAGONX_USE_DX11
};

// ============================================================================
// Global Acrylic Instance
// ============================================================================

/**
 * @brief Get the global acrylic material instance
 */
AcrylicMaterial& getAcrylicMaterial();

} // namespace effects
} // namespace ui
} // namespace dragonx
