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

#pragma once

#include "../colors.h"
#include "../typography.h"
#include "../layout.h"
#include "imgui.h"
#include "imgui_internal.h"

namespace dragonx {
namespace ui {
namespace material {

// ============================================================================
// Material Design Progress Indicators
// ============================================================================
// Based on https://m2.material.io/components/progress-indicators
//
// Progress indicators express an unspecified wait time or display the length
// of a process.

// ============================================================================
// Linear Progress
// ============================================================================

/**
 * @brief Determinate linear progress bar
 * 
 * @param fraction Progress value 0.0 to 1.0
 * @param width Width of bar (0 = full available width)
 */
void LinearProgress(float fraction, float width = 0);

/**
 * @brief Indeterminate linear progress bar (animated)
 * 
 * @param width Width of bar (0 = full available width)
 */
void LinearProgressIndeterminate(float width = 0);

/**
 * @brief Buffer linear progress bar
 * 
 * @param fraction Primary progress 0.0 to 1.0
 * @param buffer Buffer progress 0.0 to 1.0
 * @param width Width of bar (0 = full available width)
 */
void LinearProgressBuffer(float fraction, float buffer, float width = 0);

// ============================================================================
// Circular Progress
// ============================================================================

/**
 * @brief Determinate circular progress indicator
 * 
 * @param fraction Progress value 0.0 to 1.0
 * @param radius Radius of circle (default 20dp)
 */
void CircularProgress(float fraction, float radius = 20.0f);

/**
 * @brief Indeterminate circular progress (spinner)
 * 
 * @param radius Radius of circle (default 20dp)
 */
void CircularProgressIndeterminate(float radius = 20.0f);

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

inline void LinearProgress(float fraction, float width) {
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems)
        return;
    
    const float barHeight = 4.0f;  // Material spec: 4dp height
    float barWidth = width > 0 ? width : ImGui::GetContentRegionAvail().x;
    
    ImVec2 pos = window->DC.CursorPos;
    ImRect bb(pos, ImVec2(pos.x + barWidth, pos.y + barHeight));
    
    ImGui::ItemSize(bb);
    if (!ImGui::ItemAdd(bb, 0))
        return;
    
    ImDrawList* drawList = window->DrawList;
    
    // Track (background)
    ImU32 trackColor = WithAlpha(Primary(), 64);  // Primary at 25%
    drawList->AddRectFilled(bb.Min, bb.Max, trackColor, 0);
    
    // Progress indicator
    float progressWidth = barWidth * ImClamp(fraction, 0.0f, 1.0f);
    if (progressWidth > 0) {
        drawList->AddRectFilled(
            bb.Min,
            ImVec2(bb.Min.x + progressWidth, bb.Max.y),
            Primary(), 0
        );
    }
}

inline void LinearProgressIndeterminate(float width) {
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems)
        return;
    
    const float barHeight = 4.0f;
    float barWidth = width > 0 ? width : ImGui::GetContentRegionAvail().x;
    
    ImVec2 pos = window->DC.CursorPos;
    ImRect bb(pos, ImVec2(pos.x + barWidth, pos.y + barHeight));
    
    ImGui::ItemSize(bb);
    if (!ImGui::ItemAdd(bb, 0))
        return;
    
    ImDrawList* drawList = window->DrawList;
    
    // Track
    ImU32 trackColor = WithAlpha(Primary(), 64);
    drawList->AddRectFilled(bb.Min, bb.Max, trackColor, 0);
    
    // Animated indicator - sliding back and forth
    float time = (float)ImGui::GetTime();
    float cycleTime = fmodf(time, 2.0f);  // 2 second cycle
    
    // Two bars: primary and secondary with different phases
    float indicatorWidth = barWidth * 0.3f;  // 30% of track
    
    // Primary indicator
    float primaryPhase = fmodf(time * 1.2f, 2.0f);
    float primaryPos;
    if (primaryPhase < 1.0f) {
        // Accelerating from left
        primaryPos = primaryPhase * primaryPhase * (barWidth + indicatorWidth) - indicatorWidth;
    } else {
        // Continue off right (reset happens at 2.0)
        primaryPos = (2.0f - primaryPhase) * (2.0f - primaryPhase) * -(barWidth + indicatorWidth) + barWidth;
    }
    
    float primaryStart = ImMax(bb.Min.x, bb.Min.x + primaryPos);
    float primaryEnd = ImMin(bb.Max.x, bb.Min.x + primaryPos + indicatorWidth);
    
    if (primaryEnd > primaryStart) {
        drawList->AddRectFilled(
            ImVec2(primaryStart, bb.Min.y),
            ImVec2(primaryEnd, bb.Max.y),
            Primary(), 0
        );
    }
}

inline void LinearProgressBuffer(float fraction, float buffer, float width) {
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems)
        return;
    
    const float barHeight = 4.0f;
    float barWidth = width > 0 ? width : ImGui::GetContentRegionAvail().x;
    
    ImVec2 pos = window->DC.CursorPos;
    ImRect bb(pos, ImVec2(pos.x + barWidth, pos.y + barHeight));
    
    ImGui::ItemSize(bb);
    if (!ImGui::ItemAdd(bb, 0))
        return;
    
    ImDrawList* drawList = window->DrawList;
    
    // Track
    ImU32 trackColor = WithAlpha(Primary(), 38);  // Primary at 15%
    drawList->AddRectFilled(bb.Min, bb.Max, trackColor, 0);
    
    // Buffer (lighter than progress)
    float bufferWidth = barWidth * ImClamp(buffer, 0.0f, 1.0f);
    if (bufferWidth > 0) {
        drawList->AddRectFilled(
            bb.Min,
            ImVec2(bb.Min.x + bufferWidth, bb.Max.y),
            WithAlpha(Primary(), 102), 0  // Primary at 40%
        );
    }
    
    // Progress
    float progressWidth = barWidth * ImClamp(fraction, 0.0f, 1.0f);
    if (progressWidth > 0) {
        drawList->AddRectFilled(
            bb.Min,
            ImVec2(bb.Min.x + progressWidth, bb.Max.y),
            Primary(), 0
        );
    }
}

inline void CircularProgress(float fraction, float radius) {
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems)
        return;
    
    const float thickness = 4.0f;  // Stroke width
    float diameter = radius * 2;
    
    ImVec2 pos = window->DC.CursorPos;
    ImVec2 center(pos.x + radius, pos.y + radius);
    ImRect bb(pos, ImVec2(pos.x + diameter, pos.y + diameter));
    
    ImGui::ItemSize(bb);
    if (!ImGui::ItemAdd(bb, 0))
        return;
    
    ImDrawList* drawList = window->DrawList;
    
    // Track circle
    ImU32 trackColor = WithAlpha(Primary(), 64);
    drawList->AddCircle(center, radius - thickness * 0.5f, trackColor, 0, thickness);
    
    // Progress arc
    float clampedFraction = ImClamp(fraction, 0.0f, 1.0f);
    if (clampedFraction > 0) {
        float startAngle = -IM_PI * 0.5f;  // Start at top (12 o'clock)
        float endAngle = startAngle + IM_PI * 2.0f * clampedFraction;
        
        // Draw arc as line segments
        const int segments = (int)(32 * clampedFraction) + 1;
        float angleStep = (endAngle - startAngle) / segments;
        
        for (int i = 0; i < segments; i++) {
            float a1 = startAngle + angleStep * i;
            float a2 = startAngle + angleStep * (i + 1);
            
            ImVec2 p1(center.x + cosf(a1) * (radius - thickness * 0.5f),
                      center.y + sinf(a1) * (radius - thickness * 0.5f));
            ImVec2 p2(center.x + cosf(a2) * (radius - thickness * 0.5f),
                      center.y + sinf(a2) * (radius - thickness * 0.5f));
            
            drawList->AddLine(p1, p2, Primary(), thickness);
        }
    }
}

inline void CircularProgressIndeterminate(float radius) {
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if (window->SkipItems)
        return;
    
    const float thickness = 4.0f;
    float diameter = radius * 2;
    
    ImVec2 pos = window->DC.CursorPos;
    ImVec2 center(pos.x + radius, pos.y + radius);
    ImRect bb(pos, ImVec2(pos.x + diameter, pos.y + diameter));
    
    ImGui::ItemSize(bb);
    if (!ImGui::ItemAdd(bb, 0))
        return;
    
    ImDrawList* drawList = window->DrawList;
    
    float time = (float)ImGui::GetTime();
    
    // Rotation animation
    float rotation = fmodf(time * 2.0f * IM_PI / 1.4f, IM_PI * 2.0f);  // ~1.4s rotation
    
    // Arc length animation (grows and shrinks)
    float cycleTime = fmodf(time, 1.333f);  // ~1.333s cycle
    float arcLength;
    if (cycleTime < 0.666f) {
        // Growing phase
        arcLength = (cycleTime / 0.666f) * 0.75f + 0.1f;  // 10% to 85%
    } else {
        // Shrinking phase
        arcLength = ((1.333f - cycleTime) / 0.666f) * 0.75f + 0.1f;
    }
    
    float startAngle = rotation - IM_PI * 0.5f;
    float endAngle = startAngle + IM_PI * 2.0f * arcLength;
    
    // Draw arc
    const int segments = (int)(32 * arcLength) + 1;
    float angleStep = (endAngle - startAngle) / segments;
    
    for (int i = 0; i < segments; i++) {
        float a1 = startAngle + angleStep * i;
        float a2 = startAngle + angleStep * (i + 1);
        
        ImVec2 p1(center.x + cosf(a1) * (radius - thickness * 0.5f),
                  center.y + sinf(a1) * (radius - thickness * 0.5f));
        ImVec2 p2(center.x + cosf(a2) * (radius - thickness * 0.5f),
                  center.y + sinf(a2) * (radius - thickness * 0.5f));
        
        drawList->AddLine(p1, p2, Primary(), thickness);
    }
}

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