ColorWheel.c
//
//  ColorWheel.c
//  
//
//  Created by Chris Galzerano on 2/22/26.
//

#include "ColorWheel.h"
#include <math.h>

// The untouchable 100% brightness original in PSRAM
static Framebuffer* g_masterColorWheelFb = NULL;

// The mutated version sent to the UI
Framebuffer* g_displayColorWheelFb = NULL;


CCView* colorPickerView(int x, int y) {
    // 1. Main Container Background
    CCRect* containerRect = ccRect(x, y, 240, 320);
    CCView* container = viewWithFrame(containerRect);
    container->backgroundColor = color(0.15, 0.15, 0.15, 1.0); // Dark Gray
    
    // 2. Ensure the master and display buffers are built in PSRAM
    initColorWheelIfNeeded();

    // 3. Create our generic Framebuffer View and give it the Display buffer
    CCRect* wheelRect = ccRect(20, 20, 200, 200);
    CCFramebufferView* wheelFbView = framebufferViewWithFrame(wheelRect);
    framebufferViewSetFramebuffer(wheelFbView, g_displayColorWheelFb);
    
    viewAddSubview(container, wheelFbView);

    // 4. The Brightness Slider Track
    int trackX = 20;
    int trackY = 250;
    int trackW = 200;
    int trackH = 4;
    
    CCRect* trackRect = ccRect(trackX, trackY, trackW, trackH);
    CCView* sliderTrack = viewWithFrame(trackRect);
    sliderTrack->backgroundColor = color(0.5, 0.5, 0.5, 1.0);
    viewAddSubview(container, sliderTrack);

    // 5. The 10 Tick Marks (Spaced evenly across the track)
    int tickSpacing = trackW / 9; // 9 gaps for 10 ticks
    for (int i = 0; i < 10; i++) {
        int tickX = trackX + (i * tickSpacing);
        // Draw tick slightly taller than the track
        CCRect* tickRect = ccRect(tickX - 1, trackY - 4, 2, 12);
        CCView* tick = viewWithFrame(tickRect);
        tick->backgroundColor = color(0.7, 0.7, 0.7, 1.0);
        viewAddSubview(container, tick);
    }

    // 6. The Draggable Slider Thumb
    // Default position at far right (1.0 brightness)
    CCRect* thumbRect = ccRect(trackX + trackW - 10, trackY - 10, 20, 24);
    CCView* sliderThumb = viewWithFrame(thumbRect);
    sliderThumb->backgroundColor = color(1.0, 1.0, 1.0, 1.0);
    sliderThumb->tag = TAG_CCCOLOR_PICKER_THUMB;
    viewAddSubview(container, sliderThumb);

    return container;
}

// Fast C function to convert HSV to RGB
static void hsv_to_rgb(float h, float s, float v, float *r, float *g, float *b) {
    if (s == 0.0f) { *r = *g = *b = v; return; }
    int i = (int)(h * 6.0f);
    float f = (h * 6.0f) - i;
    float p = v * (1.0f - s);
    float q = v * (1.0f - s * f);
    float t = v * (1.0f - s * (1.0f - f));
    i = i % 6;
    switch (i) {
        case 0: *r = v; *g = t; *b = p; break;
        case 1: *r = q; *g = v; *b = p; break;
        case 2: *r = p; *g = v; *b = t; break;
        case 3: *r = p; *g = q; *b = v; break;
        case 4: *r = t; *g = p; *b = v; break;
        case 5: *r = v; *g = p; *b = q; break;
    }
}

// Internal helper to build the initial circle
static Framebuffer* createColorWheelFramebuffer(int width, int height) {
    Framebuffer* fb = (Framebuffer*)cc_safe_alloc(1, sizeof(Framebuffer));
    fb->displayWidth = width;
    fb->displayHeight = height;
    fb->colorMode = COLOR_MODE_BGR888;
    fb->pixelData = heap_caps_malloc(width * height * 3, MALLOC_CAP_SPIRAM);
    
    uint8_t* pixels = (uint8_t*)fb->pixelData;
    float cx = width / 2.0f;
    float cy = height / 2.0f;
    float maxRadius = (cx < cy) ? cx : cy;

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            float dx = x - cx;
            float dy = y - cy;
            float distance = sqrt(dx*dx + dy*dy);
            
            int idx = (y * width + x) * 3;
            
            if (distance <= maxRadius) {
                float theta = atan2(dy, dx);
                float hue = (theta + M_PI) / (2.0f * M_PI);
                float saturation = distance / maxRadius;
                
                float r, g, b;
                hsv_to_rgb(hue, saturation, 1.0f, &r, &g, &b);
                
                pixels[idx]   = (uint8_t)(r * 255.0f); // R
                pixels[idx+1] = (uint8_t)(g * 255.0f); // G
                pixels[idx+2] = (uint8_t)(b * 255.0f); // B
            } else {
                // Transparent corners
                pixels[idx] = 0; pixels[idx+1] = 0; pixels[idx+2] = 0;
            }
        }
    }
    return fb;
}

void initColorWheelIfNeeded(void) {
    if (!g_masterColorWheelFb) {
        // 1. Create the pristine master copy
        g_masterColorWheelFb = createColorWheelFramebuffer(200, 200);
        
        // 2. Allocate the display copy
        g_displayColorWheelFb = (Framebuffer*)cc_safe_alloc(1, sizeof(Framebuffer));
        g_displayColorWheelFb->displayWidth = 200;
        g_displayColorWheelFb->displayHeight = 200;
        g_displayColorWheelFb->colorMode = COLOR_MODE_BGR888;
        g_displayColorWheelFb->pixelData = heap_caps_malloc(200 * 200 * 3, MALLOC_CAP_SPIRAM);
        
        // 3. Populate the display copy at 100% brightness
        updateColorWheelBrightness(1.0f);
    }
}

void updateColorWheelBrightness(float brightness) {
    if (!g_masterColorWheelFb || !g_displayColorWheelFb) return;
    
    uint8_t* src = (uint8_t*)g_masterColorWheelFb->pixelData;
    uint8_t* dst = (uint8_t*)g_displayColorWheelFb->pixelData;
    
    int totalBytes = g_masterColorWheelFb->displayWidth * g_masterColorWheelFb->displayHeight * 3;
    
    // Fast 1D array loop for instant dimming
    for (int i = 0; i < totalBytes; i++) {
        dst[i] = (uint8_t)(src[i] * brightness);
    }
}

CCColor* getColorFromWheelTouch(int localX, int localY, int width, int height) {
    float cx = width / 2.0f;
    float cy = height / 2.0f;
    float maxRadius = (cx < cy) ? cx : cy;
    
    float dx = localX - cx;
    float dy = localY - cy;
    float distance = sqrt(dx*dx + dy*dy);
    
    // Check if they tapped inside the circle
    if (distance <= maxRadius) {
        float theta = atan2(dy, dx);
        float hue = (theta + M_PI) / (2.0f * M_PI);
        float saturation = distance / maxRadius;
        
        float r, g, b;
        hsv_to_rgb(hue, saturation, 1.0f, &r, &g, &b);
        return color(r, g, b, 1.0f);
    }
    
    return color(0, 0, 0, 0); // Transparent if they missed
}