// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// dvxPal.h  -- 8-bit mode palette definition for DVX GUI
//
// Defines the 256-color palette used in 8-bit (VGA Mode 13h / VESA 8bpp)
// video modes. The palette layout follows the same strategy as the X11
// default colormap and Netscape's web-safe colors:
//
//   0-215:   6x6x6 color cube  -- 6 levels per channel (0, 51, 102, 153,
//            204, 255), giving 216 uniformly distributed colors. The index
//            formula (r*36 + g*6 + b) enables O(1) color lookup without
//            searching.
//   216-231: 16-step grey ramp for smooth gradients in window chrome.
//   232-239: Dedicated UI chrome colors (highlight, shadow, active title,
//            etc.) that need exact values not available in the color cube.
//   240-255: Reserved for future use (currently black).
//
// This partition gives good general-purpose color reproduction while
// guaranteeing the UI has pixel-perfect colors for its chrome elements.
#ifndef DVX_PAL_H
#define DVX_PAL_H

#include <stdint.h>

// 256-entry palette for 8-bit mode:
//   0-215:   6x6x6 color cube (index = r*36 + g*6 + b)
//   216-231: 16-step grey ramp
//   232-239: UI chrome colors
//   240-255: reserved

// Chrome color indices for 8-bit mode
#define PAL_CHROME_HIGHLIGHT    232
#define PAL_CHROME_SHADOW       233
#define PAL_CHROME_ACTIVE_BG    234
#define PAL_CHROME_INACTIVE_BG  235
#define PAL_CHROME_DESKTOP      236
#define PAL_CHROME_SELECTION    237
#define PAL_CHROME_TEXT         238
#define PAL_CHROME_WHITE        239

// Generate the default 8-bit palette into a 768-byte buffer (256 * 3, RGB)
static inline void dvxGeneratePalette(uint8_t *pal)
{
    int32_t idx = 0;

    // Entries 0-215: 6x6x6 color cube
    for (int32_t r = 0; r < 6; r++) {
        for (int32_t g = 0; g < 6; g++) {
            for (int32_t b = 0; b < 6; b++) {
                pal[idx * 3 + 0] = (uint8_t)(r * 51);
                pal[idx * 3 + 1] = (uint8_t)(g * 51);
                pal[idx * 3 + 2] = (uint8_t)(b * 51);
                idx++;
            }
        }
    }

    // Entries 216-231: 16-step grey ramp
    for (int32_t i = 0; i < 16; i++) {
        uint8_t v = (uint8_t)(i * 17);
        pal[idx * 3 + 0] = v;
        pal[idx * 3 + 1] = v;
        pal[idx * 3 + 2] = v;
        idx++;
    }

    // Entries 232-239: UI chrome colors
    // highlight (white)
    pal[PAL_CHROME_HIGHLIGHT * 3 + 0] = 255;
    pal[PAL_CHROME_HIGHLIGHT * 3 + 1] = 255;
    pal[PAL_CHROME_HIGHLIGHT * 3 + 2] = 255;

    // shadow (dark grey)
    pal[PAL_CHROME_SHADOW * 3 + 0] = 80;
    pal[PAL_CHROME_SHADOW * 3 + 1] = 80;
    pal[PAL_CHROME_SHADOW * 3 + 2] = 80;

    // active title bg (navy blue)
    pal[PAL_CHROME_ACTIVE_BG * 3 + 0] = 0;
    pal[PAL_CHROME_ACTIVE_BG * 3 + 1] = 0;
    pal[PAL_CHROME_ACTIVE_BG * 3 + 2] = 128;

    // inactive title bg (grey)
    pal[PAL_CHROME_INACTIVE_BG * 3 + 0] = 128;
    pal[PAL_CHROME_INACTIVE_BG * 3 + 1] = 128;
    pal[PAL_CHROME_INACTIVE_BG * 3 + 2] = 128;

    // desktop (steel blue)
    pal[PAL_CHROME_DESKTOP * 3 + 0] = 51;
    pal[PAL_CHROME_DESKTOP * 3 + 1] = 102;
    pal[PAL_CHROME_DESKTOP * 3 + 2] = 153;

    // selection (navy)
    pal[PAL_CHROME_SELECTION * 3 + 0] = 0;
    pal[PAL_CHROME_SELECTION * 3 + 1] = 0;
    pal[PAL_CHROME_SELECTION * 3 + 2] = 128;

    // text (black)
    pal[PAL_CHROME_TEXT * 3 + 0] = 0;
    pal[PAL_CHROME_TEXT * 3 + 1] = 0;
    pal[PAL_CHROME_TEXT * 3 + 2] = 0;

    // bright white
    pal[PAL_CHROME_WHITE * 3 + 0] = 255;
    pal[PAL_CHROME_WHITE * 3 + 1] = 255;
    pal[PAL_CHROME_WHITE * 3 + 2] = 255;

    // Entries 240-255: reserved (black)
    for (int32_t i = 240; i < 256; i++) {
        pal[i * 3 + 0] = 0;
        pal[i * 3 + 1] = 0;
        pal[i * 3 + 2] = 0;
    }
}

// Find the nearest palette entry for an RGB color using minimum Euclidean
// distance in RGB space. The two-phase approach avoids a full 256-entry
// linear scan in the common case:
//   1. Fast path: snap to the nearest 6x6x6 color cube entry via integer
//      division (O(1), no branching).
//   2. Refinement: linearly scan only the 24 grey ramp and chrome entries
//      (indices 216-239) to see if any is closer than the cube match.
// Entries 240-255 are reserved (black) and skipped to avoid false matches.
// This is called by packColor() in 8-bit mode, so it needs to be fast.
static inline uint8_t dvxNearestPalEntry(const uint8_t *pal, uint8_t r, uint8_t g, uint8_t b)
{
    // Snap to nearest cube vertex: +25 rounds to nearest 51-step level
    int32_t ri = (r + 25) / 51;
    int32_t gi = (g + 25) / 51;
    int32_t bi = (b + 25) / 51;

    if (ri > 5) { ri = 5; }
    if (gi > 5) { gi = 5; }
    if (bi > 5) { bi = 5; }

    uint8_t cubeIdx   = (uint8_t)(ri * 36 + gi * 6 + bi);
    int32_t cubeR     = ri * 51;
    int32_t cubeG     = gi * 51;
    int32_t cubeB     = bi * 51;
    int32_t cubeDist  = (r - cubeR) * (r - cubeR) + (g - cubeG) * (g - cubeG) + (b - cubeB) * (b - cubeB);

    // Check grey ramp and chrome colors for a closer match
    int32_t bestDist = cubeDist;
    uint8_t bestIdx  = cubeIdx;

    for (int32_t i = 216; i < 240; i++) {
        int32_t dr = (int32_t)r - (int32_t)pal[i * 3 + 0];
        int32_t dg = (int32_t)g - (int32_t)pal[i * 3 + 1];
        int32_t db = (int32_t)b - (int32_t)pal[i * 3 + 2];
        int32_t dist = dr * dr + dg * dg + db * db;

        if (dist < bestDist) {
            bestDist = dist;
            bestIdx  = (uint8_t)i;
        }
    }

    return bestIdx;
}

#endif // DVX_PAL_H
