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

#pragma once

#include "colors.h"
#include "../effects/low_spec.h"
#include "../schema/ui_schema.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <cmath>

namespace dragonx {
namespace ui {
namespace material {

// ============================================================================
// Material Design Elevation and Shadow System
// ============================================================================
// Based on https://m2.material.io/design/environment/elevation.html
//
// Material Design uses two light sources to create shadows:
// - Key light: Creates sharper, directional shadows
// - Ambient light: Creates softer, omnidirectional shadows
//
// In dark themes, elevation is primarily shown through surface color overlays
// rather than shadows. However, shadows can still enhance depth perception.

// ============================================================================
// Shadow Specifications
// ============================================================================

/**
 * @brief Individual shadow layer specification
 * 
 * Material shadows are composed of multiple layers with different
 * blur radii and offsets to simulate real-world lighting.
 */
struct ShadowLayer {
    float offsetX;      // Horizontal offset (typically 0)
    float offsetY;      // Vertical offset (key light from above)
    float blurRadius;   // Blur spread
    float spreadRadius; // Size adjustment
    float opacity;      // Alpha 0.0-1.0
};

/**
 * @brief Complete shadow specification for an elevation level
 */
struct ShadowSpec {
    ShadowLayer umbra;     // Darkest part, sharp edge
    ShadowLayer penumbra;  // Mid-tone, softer
    ShadowLayer ambient;   // Lightest, most diffuse
};

/**
 * @brief Get shadow specification for elevation level
 * 
 * @param elevationDp Elevation in dp (0, 1, 2, 3, 4, 6, 8, 12, 16, 24)
 * @return ShadowSpec for the elevation
 */
ShadowSpec GetShadowSpec(int elevationDp);

// ============================================================================
// Shadow Rendering
// ============================================================================

/**
 * @brief Draw Material Design shadow for a rectangle
 * 
 * Uses multi-layer soft shadow rendering to approximate Material shadows.
 * 
 * @param drawList ImGui draw list
 * @param rect Rectangle bounds
 * @param elevationDp Elevation in dp
 * @param cornerRadius Corner radius for rounded rectangles
 */
void DrawShadow(ImDrawList* drawList, const ImRect& rect, int elevationDp, float cornerRadius = 0);

/**
 * @brief Draw shadow with position/size parameters
 */
void DrawShadow(ImDrawList* drawList, const ImVec2& pos, const ImVec2& size, 
                int elevationDp, float cornerRadius = 0);

/**
 * @brief Draw soft shadow (single layer, for custom effects)
 * 
 * @param drawList ImGui draw list
 * @param rect Rectangle bounds
 * @param color Shadow color with alpha
 * @param blurRadius Blur amount
 * @param offset Shadow offset
 * @param cornerRadius Corner radius
 */
void DrawSoftShadow(ImDrawList* drawList, const ImRect& rect, ImU32 color,
                    float blurRadius, const ImVec2& offset = ImVec2(0, 0), 
                    float cornerRadius = 0);

// ============================================================================
// Elevation Transition Helper
// ============================================================================

/**
 * @brief Animated elevation value
 * 
 * Use this to smoothly transition between elevation levels (e.g., card hover)
 */
class ElevationAnimator {
public:
    ElevationAnimator(int initialElevation = 0);
    
    /**
     * @brief Set target elevation (will animate towards it)
     */
    void setTarget(int targetElevation);
    
    /**
     * @brief Update animation (call each frame)
     * @param deltaTime Frame delta time
     */
    void update(float deltaTime);
    
    /**
     * @brief Get current animated elevation value
     */
    float getCurrent() const { return m_current; }
    
    /**
     * @brief Get current elevation as integer (for shadow lookup)
     */
    int getCurrentInt() const { return static_cast<int>(m_current + 0.5f); }
    
    /**
     * @brief Check if currently animating
     */
    bool isAnimating() const { return m_current != m_target; }
    
private:
    float m_current;
    float m_target;
    float m_animationSpeed = 16.0f;  // dp per second
};

// ============================================================================
// Implementation
// ============================================================================

inline ShadowSpec GetShadowSpec(int elevationDp) {
    // Material Design shadow values adapted from the spec
    // These approximate the CSS box-shadow values from material.io
    
    switch (elevationDp) {
        case 0:
            return {
                {0, 0, 0, 0, 0},     // No shadow
                {0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0}
            };
        case 1:
            return {
                {0, 2, 1, -1, 0.2f},   // Umbra
                {0, 1, 1, 0, 0.14f},   // Penumbra
                {0, 1, 3, 0, 0.12f}    // Ambient
            };
        case 2:
            return {
                {0, 3, 1, -2, 0.2f},
                {0, 2, 2, 0, 0.14f},
                {0, 1, 5, 0, 0.12f}
            };
        case 3:
            return {
                {0, 3, 3, -2, 0.2f},
                {0, 3, 4, 0, 0.14f},
                {0, 1, 8, 0, 0.12f}
            };
        case 4:
            return {
                {0, 2, 4, -1, 0.2f},
                {0, 4, 5, 0, 0.14f},
                {0, 1, 10, 0, 0.12f}
            };
        case 6:
            return {
                {0, 3, 5, -1, 0.2f},
                {0, 6, 10, 0, 0.14f},
                {0, 1, 18, 0, 0.12f}
            };
        case 8:
            return {
                {0, 5, 5, -3, 0.2f},
                {0, 8, 10, 1, 0.14f},
                {0, 3, 14, 2, 0.12f}
            };
        case 12:
            return {
                {0, 7, 8, -4, 0.2f},
                {0, 12, 17, 2, 0.14f},
                {0, 5, 22, 4, 0.12f}
            };
        case 16:
            return {
                {0, 8, 10, -5, 0.2f},
                {0, 16, 24, 2, 0.14f},
                {0, 6, 30, 5, 0.12f}
            };
        case 24:
            return {
                {0, 11, 15, -7, 0.2f},
                {0, 24, 38, 3, 0.14f},
                {0, 9, 46, 8, 0.12f}
            };
        default:
            // Interpolate for non-standard elevations
            if (elevationDp < 0) return GetShadowSpec(0);
            if (elevationDp > 24) return GetShadowSpec(24);
            
            // Find nearest standard elevation
            int lower = 0, upper = 1;
            int standards[] = {0, 1, 2, 3, 4, 6, 8, 12, 16, 24};
            for (int i = 0; i < 9; i++) {
                if (standards[i] <= elevationDp && standards[i + 1] >= elevationDp) {
                    lower = standards[i];
                    upper = standards[i + 1];
                    break;
                }
            }
            
            // Use nearest
            return GetShadowSpec((elevationDp - lower < upper - elevationDp) ? lower : upper);
    }
}

inline void DrawSoftShadow(ImDrawList* drawList, const ImRect& rect, ImU32 color,
                           float blurRadius, const ImVec2& offset, float cornerRadius) {
    if (blurRadius <= 0 || (color & IM_COL32_A_MASK) == 0)
        return;
    
    // For ImGui, we'll simulate soft shadows using multiple semi-transparent layers
    // This is a performance-friendly approximation
    
    // In low-spec mode use only 1 layer instead of up to 8
    const int numLayers = dragonx::ui::effects::isLowSpecMode()
        ? 1
        : ImClamp((int)(blurRadius / 2), 2, 8);
    const float layerStep = blurRadius / numLayers;
    
    // Extract base alpha
    float baseAlpha = ((color >> IM_COL32_A_SHIFT) & 0xFF) / 255.0f;
    ImU32 baseColor = color & ~IM_COL32_A_MASK;
    
    for (int i = numLayers - 1; i >= 0; i--) {
        float expansion = layerStep * (i + 1);
        float alpha = baseAlpha * (1.0f - (float)i / numLayers) / numLayers;
        
        ImU32 layerColor = baseColor | (((ImU32)(alpha * 255)) << IM_COL32_A_SHIFT);
        
        ImRect expandedRect(
            rect.Min.x - expansion + offset.x,
            rect.Min.y - expansion + offset.y,
            rect.Max.x + expansion + offset.x,
            rect.Max.y + expansion + offset.y
        );
        
        drawList->AddRectFilled(expandedRect.Min, expandedRect.Max, layerColor, 
                               cornerRadius + expansion * 0.5f);
    }
}

inline void DrawShadow(ImDrawList* drawList, const ImRect& rect, int elevationDp, float cornerRadius) {
    if (elevationDp <= 0)
        return;
    
    ShadowSpec spec = GetShadowSpec(elevationDp);
    
    // Shadow multiplier: light themes need stronger shadows for card depth,
    // dark themes rely more on surface color overlay for elevation.
    // Configurable via ui.toml [style] shadow-multiplier / shadow-multiplier-light.
    const float shadowMultiplier = schema::UI().isDarkTheme()
        ? schema::UI().drawElement("style", "shadow-multiplier").sizeOr(0.6f)
        : schema::UI().drawElement("style", "shadow-multiplier-light").sizeOr(1.0f);
    
    // Draw ambient shadow (largest, most diffuse)
    if (spec.ambient.opacity > 0) {
        ImU32 ambientColor = IM_COL32(0, 0, 0, (int)(spec.ambient.opacity * shadowMultiplier * 255));
        ImRect ambientRect = rect;
        ambientRect.Expand(spec.ambient.spreadRadius);
        DrawSoftShadow(drawList, ambientRect, ambientColor, spec.ambient.blurRadius,
                      ImVec2(spec.ambient.offsetX, spec.ambient.offsetY), cornerRadius);
    }
    
    // Draw penumbra (medium)
    if (spec.penumbra.opacity > 0) {
        ImU32 penumbraColor = IM_COL32(0, 0, 0, (int)(spec.penumbra.opacity * shadowMultiplier * 255));
        ImRect penumbraRect = rect;
        penumbraRect.Expand(spec.penumbra.spreadRadius);
        DrawSoftShadow(drawList, penumbraRect, penumbraColor, spec.penumbra.blurRadius,
                      ImVec2(spec.penumbra.offsetX, spec.penumbra.offsetY), cornerRadius);
    }
    
    // Draw umbra (sharpest, darkest)
    if (spec.umbra.opacity > 0) {
        ImU32 umbraColor = IM_COL32(0, 0, 0, (int)(spec.umbra.opacity * shadowMultiplier * 255));
        ImRect umbraRect = rect;
        umbraRect.Expand(spec.umbra.spreadRadius);
        DrawSoftShadow(drawList, umbraRect, umbraColor, spec.umbra.blurRadius,
                      ImVec2(spec.umbra.offsetX, spec.umbra.offsetY), cornerRadius);
    }
}

inline void DrawShadow(ImDrawList* drawList, const ImVec2& pos, const ImVec2& size, 
                       int elevationDp, float cornerRadius) {
    ImRect rect(pos, ImVec2(pos.x + size.x, pos.y + size.y));
    DrawShadow(drawList, rect, elevationDp, cornerRadius);
}

inline ElevationAnimator::ElevationAnimator(int initialElevation)
    : m_current(static_cast<float>(initialElevation))
    , m_target(static_cast<float>(initialElevation))
{
}

inline void ElevationAnimator::setTarget(int targetElevation) {
    m_target = static_cast<float>(targetElevation);
}

inline void ElevationAnimator::update(float deltaTime) {
    if (m_current == m_target)
        return;
    
    float diff = m_target - m_current;
    float change = m_animationSpeed * deltaTime;
    
    if (std::abs(diff) <= change) {
        m_current = m_target;
    } else {
        m_current += (diff > 0 ? 1 : -1) * change;
    }
}

} // namespace material
} // namespace ui
} // namespace dragonx
