/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


//
// Include the needed Linux header files
//
#include <math.h>
#include <stdint.h>
#include <stdio.h>


//
// Convert temperature (Kelvin) to RGB
//
// Based on  https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html
//       and http://www.zombieprototypes.com/?p=210
//
static uint8_t k2r(double k_)
{
    double k = k_ / 100.0;
    if(k < 66.0) return 255;

    k -= 55.0;
    const double v = 351.97690566805693 + 0.114206453784165 * k - 40.25366309332127 * log(k);

    if(v <   0) return   0;
    if(v > 255) return 255;
    return round(v);
}


static uint8_t k2g(double k_)
{
    double k = k_ / 100.0;

    double v;
    if(k < 66.0) {
        k -=  2.0;
        v  = -155.25485562709179 - 0.44596950469579133 * k + 104.49216199393888 * log(k);
    }
    else {
        k -= 50.0;
        v  = 325.4494125711974 + 0.07943456536662342 * k - 28.0852963507957 * log(k);
    }

    if(v <   0) return   0;
    if(v > 255) return 255;
    return round(v);
}


static uint8_t k2b(double k_)
{
    double k = k_ / 100.0;
    if(k < 20.0) return   0;
    if(k >= 66.0) return 255;

    k -= 10.0;
    const double v = -254.76935184120902 + 0.8274096064007395 * k + 115.67994401066147 * log(k);

    if(v <   0) return   0;
    if(v > 255) return 255;
    return round(v);
}


//
// Generate LUT
//
static void genLUT(const char* idm, const char* ids, double mul, double add, uint8_t (*genf)(double))
{
    printf("\n    static const uint8_t PROGMEM %s%s[256] = {\n        ", idm, ids);

    uint8_t val0 = 0;
    uint8_t val1 = 0;

    double  kMin =  1.0e6;
    double  kMax = -1.0e6;

    for(uint16_t i = 0; i < 256; ++i) {
        // Get the current Kelvin value
        const double kCur = i * mul + add;
        // Get the converted valued
        const uint8_t vCur = (*genf)(kCur);
        if(i ==   0) val0 = vCur;
        if(i == 255) val1 = vCur;
        // Update the minimum and maximum values
        if(kCur < kMin) kMin = kCur;
        if(kCur > kMax) kMax = kCur;
        // Convert to red
        printf( "%3d", static_cast<int>(vCur) );
        // Print separators and new lines as needed
        if(i < 255) {
            printf(", ");
            if( !( (i + 1) % 32) ) printf("\n        ");
        }
    }

    printf("    \n    };\n");

    // Calculate the interval
    const double kItv = kMax - kMin;

    // Print the minimum and maximum values
    printf( "    constexpr uint16_t %sMinKelvin%s = %5d;\n", idm, ids, static_cast<int>( round(kMin) ) );
    printf( "    constexpr uint16_t %sMaxKelvin%s = %5d;\n", idm, ids, static_cast<int>( round(kMax) ) );
    printf( "    constexpr uint16_t %sItvKelvin%s = %5d;\n", idm, ids, static_cast<int>( round(kItv) ) );

    printf( "    constexpr uint16_t %sIdx0Value%s = %5d;\n", idm, ids, static_cast<int>( val0 ) );
    printf( "    constexpr uint16_t %sIdx1Value%s = %5d;\n", idm, ids, static_cast<int>( val1 ) );
}


//
// The main program
//
int main(void)
{
    double k0;
    double k1;

    // Generate some K->RGB Look-Up-Tables
    k0 =  6600;
    k1 = 12000;
    genLUT( "K2R", "_R1", (k1 - k0) / 255.0, k0, &k2r );

    k0 = 12000;
    k1 = 40000;
    genLUT( "K2R", "_R2", (k1 - k0) / 255.0, k0, &k2r );

    k0 =  630;
    k1 = 6600;
    genLUT( "K2G", "_L1", (k1 - k0) / 255.0, k0, &k2g );

    k0 =  6600;
    k1 = 12000;
    genLUT( "K2G", "_R1", (k1 - k0) / 255.0, k0, &k2g );

    k0 = 12000;
    k1 = 40000;
    genLUT( "K2G", "_R2", (k1 - k0) / 255.0, k0, &k2g );

    k0 = 1990;
    k1 = 6510;
    genLUT( "K2B", "_L1", (k1 - k0) / 255.0, k0, &k2b );

    printf("\n");

    return 0;
}
