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

#include "noise_texture.h"
#include "../../util/logger.h"

#ifdef DRAGONX_HAS_GLAD
#include <glad/gl.h>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>

namespace dragonx {
namespace ui {
namespace effects {

// ============================================================================
// NoiseTexture Implementation (GLEW Available)
// ============================================================================

NoiseTexture::NoiseTexture() = default;

NoiseTexture::~NoiseTexture()
{
    destroy();
}

float NoiseTexture::random(uint32_t& seed)
{
    // Xorshift32 PRNG
    seed ^= seed << 13;
    seed ^= seed >> 17;
    seed ^= seed << 5;
    return static_cast<float>(seed) / static_cast<float>(0xFFFFFFFF);
}

float NoiseTexture::noise2D(float x, float y, int wrap)
{
    // Simple value noise with smoothstep interpolation
    int xi = static_cast<int>(std::floor(x)) & (wrap - 1);
    int yi = static_cast<int>(std::floor(y)) & (wrap - 1);
    
    float xf = x - std::floor(x);
    float yf = y - std::floor(y);
    
    // Smoothstep
    float u = xf * xf * (3.0f - 2.0f * xf);
    float v = yf * yf * (3.0f - 2.0f * yf);
    
    // Hash function for corners
    auto hash = [wrap](int x, int y) -> float {
        uint32_t seed = static_cast<uint32_t>((x & (wrap-1)) * 374761393 + (y & (wrap-1)) * 668265263);
        seed ^= seed << 13;
        seed ^= seed >> 17;
        seed ^= seed << 5;
        return static_cast<float>(seed) / static_cast<float>(0xFFFFFFFF);
    };
    
    // Bilinear interpolation
    float n00 = hash(xi, yi);
    float n10 = hash(xi + 1, yi);
    float n01 = hash(xi, yi + 1);
    float n11 = hash(xi + 1, yi + 1);
    
    float nx0 = n00 * (1.0f - u) + n10 * u;
    float nx1 = n01 * (1.0f - u) + n11 * u;
    
    return nx0 * (1.0f - v) + nx1 * v;
}

bool NoiseTexture::generate(int size, float intensity)
{
    destroy();
    
    if (size <= 0 || (size & (size - 1)) != 0) {
        DEBUG_LOGF("NoiseTexture: Size must be power of 2, got %d\n", size);
        return false;
    }
    
    size_ = size;
    
    // Generate pure white noise — no coherent patterns, perfectly seamless
    // when tiled because every pixel is independent.
    std::vector<uint8_t> data(size * size * 4);
    uint32_t seed = 12345;
    
    for (int y = 0; y < size; y++) {
        for (int x = 0; x < size; x++) {
            float r = random(seed);
            // Apply intensity around mid-gray center
            float value = 0.5f + (r - 0.5f) * intensity;
            value = std::max(0.0f, std::min(1.0f, value));
            
            uint8_t gray = static_cast<uint8_t>(value * 255.0f);
            int idx = (y * size + x) * 4;
            data[idx + 0] = gray;
            data[idx + 1] = gray;
            data[idx + 2] = gray;
            data[idx + 3] = 255;
        }
    }
    
    // Create OpenGL texture
    glGenTextures(1, &texture_);
    glBindTexture(GL_TEXTURE_2D, texture_);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
    
    // NEAREST filtering preserves individual grain pixels;
    // REPEAT wrapping ensures seamless tiling.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    
    glBindTexture(GL_TEXTURE_2D, 0);
    
    DEBUG_LOGF("NoiseTexture: Generated %dx%d white noise texture (intensity=%.2f)\n", size, size, intensity);
    
    return texture_ != 0;
}

void NoiseTexture::destroy()
{
    if (texture_) {
        glDeleteTextures(1, &texture_);
        texture_ = 0;
    }
    size_ = 0;
}

// ============================================================================
// NoiseUtils Implementation
// ============================================================================

namespace NoiseUtils {

unsigned int createWhiteNoiseTexture(int size)
{
    std::vector<uint8_t> data(size * size * 4);
    
    uint32_t seed = 42;
    
    for (int i = 0; i < size * size; i++) {
        // Simple white noise
        seed ^= seed << 13;
        seed ^= seed >> 17;
        seed ^= seed << 5;
        
        uint8_t value = static_cast<uint8_t>((seed >> 24) & 0xFF);
        
        data[i * 4 + 0] = value;
        data[i * 4 + 1] = value;
        data[i * 4 + 2] = value;
        data[i * 4 + 3] = 255;
    }
    
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    
    glBindTexture(GL_TEXTURE_2D, 0);
    
    return texture;
}

unsigned int createBlueNoiseTexture(int size)
{
    // Blue noise has more even distribution - good for dithering
    // Using a simple void-and-cluster algorithm approximation
    
    std::vector<float> noise(size * size, 0.5f);
    std::vector<uint8_t> data(size * size * 4);
    
    uint32_t seed = 12345;
    
    // Start with white noise
    for (int i = 0; i < size * size; i++) {
        seed ^= seed << 13;
        seed ^= seed >> 17;
        seed ^= seed << 5;
        noise[i] = static_cast<float>(seed) / static_cast<float>(0xFFFFFFFF);
    }
    
    // Apply a simple high-pass filter to reduce low frequencies
    // (Very simplified blue noise approximation)
    std::vector<float> filtered(size * size);
    for (int y = 0; y < size; y++) {
        for (int x = 0; x < size; x++) {
            float center = noise[y * size + x];
            float sum = 0.0f;
            int count = 0;
            
            // Sample neighbors
            for (int dy = -2; dy <= 2; dy++) {
                for (int dx = -2; dx <= 2; dx++) {
                    if (dx == 0 && dy == 0) continue;
                    int nx = (x + dx + size) % size;
                    int ny = (y + dy + size) % size;
                    sum += noise[ny * size + nx];
                    count++;
                }
            }
            
            float avg = sum / count;
            filtered[y * size + x] = 0.5f + (center - avg) * 2.0f;
        }
    }
    
    // Convert to texture data
    for (int i = 0; i < size * size; i++) {
        float value = std::max(0.0f, std::min(1.0f, filtered[i]));
        uint8_t gray = static_cast<uint8_t>(value * 255.0f);
        
        data[i * 4 + 0] = gray;
        data[i * 4 + 1] = gray;
        data[i * 4 + 2] = gray;
        data[i * 4 + 3] = 255;
    }
    
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    
    glBindTexture(GL_TEXTURE_2D, 0);
    
    return texture;
}

unsigned int createAcrylicNoiseTexture(int size)
{
    // Specially tuned noise for Microsoft Acrylic material look
    // Very subtle, with a hint of blue-ish tint
    
    std::vector<uint8_t> data(size * size * 4);
    
    uint32_t seed = 98765;
    
    for (int y = 0; y < size; y++) {
        for (int x = 0; x < size; x++) {
            // Multiple noise layers
            float n1 = NoiseTexture::noise2D(x * 0.5f, y * 0.5f, size);
            float n2 = NoiseTexture::noise2D(x * 1.0f, y * 1.0f, size) * 0.5f;
            float n3 = NoiseTexture::noise2D(x * 2.0f, y * 2.0f, size) * 0.25f;
            
            // White noise component
            seed ^= seed << 13;
            seed ^= seed >> 17;
            seed ^= seed << 5;
            float white = static_cast<float>(seed) / static_cast<float>(0xFFFFFFFF);
            
            // Combine
            float combined = (n1 + n2 + n3) / 1.75f * 0.6f + white * 0.4f;
            
            // Very subtle intensity (0.02 - 0.04 typical for acrylic)
            float value = 0.5f + (combined - 0.5f) * 0.04f;
            value = std::max(0.0f, std::min(1.0f, value));
            
            uint8_t gray = static_cast<uint8_t>(value * 255.0f);
            
            // Slight cool tint (very subtle)
            int idx = (y * size + x) * 4;
            data[idx + 0] = static_cast<uint8_t>(gray * 0.98f);  // R slightly less
            data[idx + 1] = gray;                                 // G
            data[idx + 2] = static_cast<uint8_t>(std::min(255, gray + 1));  // B slightly more
            data[idx + 3] = 255;
        }
    }
    
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    
    glBindTexture(GL_TEXTURE_2D, 0);
    
    return texture;
}

} // namespace NoiseUtils

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

#else // !DRAGONX_HAS_GLAD

// ============================================================================
// Stub Implementation (No GLAD - Acrylic effects disabled)
// ============================================================================

#include <cmath>
#include <cstdint>

namespace dragonx {
namespace ui {
namespace effects {

NoiseTexture::NoiseTexture() = default;
NoiseTexture::~NoiseTexture() = default;

float NoiseTexture::random(uint32_t& seed)
{
    seed ^= seed << 13;
    seed ^= seed >> 17;
    seed ^= seed << 5;
    return static_cast<float>(seed) / static_cast<float>(0xFFFFFFFF);
}

float NoiseTexture::noise2D(float x, float y, int wrap)
{
    // Provide a basic implementation for potential use elsewhere
    int xi = static_cast<int>(std::floor(x)) & (wrap - 1);
    int yi = static_cast<int>(std::floor(y)) & (wrap - 1);
    uint32_t seed = static_cast<uint32_t>(xi * 374761393 + yi * 668265263);
    return random(seed);
}

bool NoiseTexture::generate(int, float) { return false; }
void NoiseTexture::destroy() {}

namespace NoiseUtils {
unsigned int createWhiteNoiseTexture(int) { return 0; }
unsigned int createBlueNoiseTexture(int) { return 0; }
unsigned int createAcrylicNoiseTexture(int) { return 0; }
} // namespace NoiseUtils

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

#endif // DRAGONX_HAS_GLAD
