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


#include <eCxx/eCxx_ColorAnimator2D.h>

#if defined(__x86_64__) || defined(__i386__)
    #define SIMULATE_LED
    #include <eCxx/eCxx_SDF.h>
#endif

#include "TestUtility.h"


//
// Test application
//
#ifdef SIMULATE_LED
int main(const int argc, const char** argv)
#else
int main()
#endif
{
    // ===== Instantiate the buffers =====
    eCxx::ColorFX2D<eCxx::WColor,    8,  8, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> colorFX2D_rawW_88;
    eCxx::ColorFX2D<eCxx::RGBColor,  8,  8, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> colorFX2D_rawRGB_88;
    eCxx::ColorFX2D<eCxx::RGBColor,  8,  8, eCxx::CBP::RowMajor                      > colorFX2D_encRGB_88;
    eCxx::ColorFX2D<eCxx::RGBColor, 16, 16, eCxx::CBP::RowMajor                      > colorFX2D_encRGB_1616;

#ifdef SIMULATE_LED
    // ===== Print some information as needed =====

    // Get the program argument to check which simulation groups to be actually run
    // using the LED simulator
    const eCxx::uint8_t simGroup = (argc == 2 && argv[1][1] == 0) ? (argv[1][0] - '0') : 0;

    // If no simulation group specified, print the list of available groups
    if(!simGroup || simGroup > 2) {
        printf("\n"                                                                       );
        printf("Available simulation groups:\n"                                           );
        printf("\n"                                                                       );
        printf("    #1 :  8 x  8 bit random           white                low  density\n");
        printf("          8 x  8 bit random           white                high density\n");
        printf("          8 x  8 bit random           red/green/blue/white high density\n");
        printf("          8 x  8 bit random starlight white\n"                            );
        printf("\n"                                                                       );
        printf("    #2 : 16 x 16 bit vertical   fire  red\n"                              );
        printf("         16 x 16 bit horizontal fire  green/blue\n"                       );
        printf("         16 x 16 bit down       rain  white\n"                            );
        printf("         16 x 16 bit right/left rain  white\n"                            );
        printf("\n"                                                                       );
    }

    // SDF file names
    constexpr const char* TMP_SDF_RAW_FILE_NAME = "/tmp/sdf_raw.txt";
    constexpr const char* TMP_SDF_ENC_FILE_NAME = "/tmp/sdf_enc.txt";

    // SDF file handle
    eCxx::SDFFile sdf;
#endif



    // =============================================================================================
    // ========================================== Group 1 ==========================================
    // =============================================================================================

    // ===== Instantiate the animators and initialize the segments - random =====
    auto carRandom1 = eCxx::makeColorAnimator2D<1>(colorFX2D_rawW_88  );
    auto carRandom2 = eCxx::makeColorAnimator2D<1>(colorFX2D_rawRGB_88);
    auto caeRandom1 = eCxx::makeColorAnimator2D<4>(colorFX2D_encRGB_88);

    eCxx::Animation2DProc_RandomTwoColors< decltype(carRandom1) > randomW_rawW  (eCxx::Ext::W::black,   eCxx::Ext::W::white,    8);
    eCxx::Animation2DProc_RandomTwoColors< decltype(carRandom2) > randomW_rawRGB(eCxx::Ext::RGB::black, eCxx::Ext::RGB::white, 64);

    eCxx::Animation2DProc_RandomTwoColors< decltype(caeRandom1) > randomR_encRGB(eCxx::Ext::RGB::black, eCxx::Ext::RGB::red,   64);
    eCxx::Animation2DProc_RandomTwoColors< decltype(caeRandom1) > randomG_encRGB(eCxx::Ext::RGB::black, eCxx::Ext::RGB::green, 64);
    eCxx::Animation2DProc_RandomTwoColors< decltype(caeRandom1) > randomB_encRGB(eCxx::Ext::RGB::black, eCxx::Ext::RGB::blue,  64);
    eCxx::Animation2DProc_RandomTwoColors< decltype(caeRandom1) > randomX_encRGB(eCxx::Ext::RGB::black, eCxx::Ext::RGB::white, 64);

    carRandom1.initSegment(0, randomW_rawW  );
    carRandom2.initSegment(0, randomW_rawRGB);

    caeRandom1.initSegment(0, 0, 0, 3, 3, 0, randomR_encRGB);
    caeRandom1.initSegment(1, 4, 0, 7, 3, 0, randomG_encRGB);
    caeRandom1.initSegment(2, 0, 3, 3, 7, 0, randomB_encRGB);
    caeRandom1.initSegment(3, 4, 4, 7, 7, 0, randomX_encRGB);

    // ===== Instantiate the animators and initialize the segments - random starlight =====
    auto caeStarlight = eCxx::makeColorAnimator2D<1>(colorFX2D_encRGB_88);

    eCxx::Animation2DProc_RandomStarlightTwoColors< decltype(caeStarlight), 5 > starlightW_encRGB(eCxx::Ext::RGB::black, eCxx::Ext::RGB::white);

    caeStarlight.initSegment(0, starlightW_encRGB);

#ifdef SIMULATE_LED

    // ===== Test the results using LED simulator =====
#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_W(TMP_SDF_RAW_FILE_NAME, colorFX2D_rawW_88, 5, -1);
    carRandom1.reset();
    for(eCxx::CBP::QUInt i = 0; i < carRandom1.estimatedTotalAnimationFrameCount(); ++i) {
        carRandom1.update();
        eCxx::writeSDF_2D_W(sdf, colorFX2D_rawW_88);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 1) eCxx::execSDF(TMP_SDF_RAW_FILE_NAME);
#endif

#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_RAW_FILE_NAME, colorFX2D_rawRGB_88, 5, -1);
    carRandom2.reset();
    for(eCxx::CBP::QUInt i = 0; i < carRandom2.estimatedTotalAnimationFrameCount(); ++i) {
        carRandom2.update();
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_rawRGB_88);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 1) eCxx::execSDF(TMP_SDF_RAW_FILE_NAME);
#endif

#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_ENC_FILE_NAME, colorFX2D_encRGB_88, 5, -1);
    caeRandom1.reset();
    for(eCxx::CBP::QUInt i = 0; i < caeRandom1.estimatedTotalAnimationFrameCount(); ++i) {
        caeRandom1.update();
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_encRGB_88);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 1) eCxx::execSDF(TMP_SDF_ENC_FILE_NAME);
#endif

#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_ENC_FILE_NAME, colorFX2D_encRGB_88, 5, -1);
    caeStarlight.reset();
    for(eCxx::CBP::QUInt i = 0; i < caeStarlight.estimatedTotalAnimationFrameCount(); ++i) {
        caeStarlight.update();
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_encRGB_88);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 1) eCxx::execSDF(TMP_SDF_ENC_FILE_NAME);
#endif

#else // SIMULATE_LED

    // ===== Test the function calls only =====
    carRandom1  .reset(); carRandom1  .update();
    carRandom2  .reset(); carRandom2  .update();

    caeRandom1  .reset(); caeRandom1  .update();

    caeStarlight.reset(); caeStarlight.update();

#endif // SIMULATE_LED



    // =============================================================================================
    // ========================================== Group 2 ==========================================
    // =============================================================================================

    // ===== Instantiate the animators and initialize the segments - fire =====
    auto caeFire1 = eCxx::makeColorAnimator2D<1>(colorFX2D_encRGB_1616);
    auto caeFire2 = eCxx::makeColorAnimator2D<2>(colorFX2D_encRGB_1616);

    eCxx::Animation2DProc_Fire< decltype(caeFire1),
                                eCxx::Fire2DConfig::Flow::BottomToTop,
                                eCxx::Fire2DConfig::Dist::Uniform,
                                false
                              > fire1b_encRGB( 16, eCxx::Fire2DConfig::redRGBFireLUT_16() );

    eCxx::Animation2DProc_Fire< decltype(caeFire2),
                                eCxx::Fire2DConfig::Flow::LeftToRight,
                                eCxx::Fire2DConfig::Dist::TowardsFront,
                                true
                              > fire2l_encRGB( 16, eCxx::Fire2DConfig::greenRGBFireLUT_16() );

    eCxx::Animation2DProc_Fire< decltype(caeFire2),
                                eCxx::Fire2DConfig::Flow::RightToLeft,
                                eCxx::Fire2DConfig::Dist::TowardsBack,
                                true
                              > fire2r_encRGB( 16, eCxx::Fire2DConfig::blueRGBFireLUT_16() );

    caeFire1.initSegment(0,                  fire1b_encRGB);

    caeFire2.initSegment(0, 0, 0, 15, 15, 0, fire2l_encRGB);
    caeFire2.initSegment(1, 0, 0, 15, 15, 0, fire2r_encRGB);

    // ===== Instantiate the animators and initialize the segments - rain =====
    auto caeRain1 = eCxx::makeColorAnimator2D<1>(colorFX2D_encRGB_1616);
    auto caeRain2 = eCxx::makeColorAnimator2D<2>(colorFX2D_encRGB_1616);

    const eCxx::uint8_t value1 = eCxx::cie1931lut58( 8); // Use CIE-1931 lightness LUT so that color correction
    const eCxx::uint8_t value2 = eCxx::cie1931lut58(12); // will not be needed for this animation
    const eCxx::uint8_t value3 = eCxx::cie1931lut58(16);

    eCxx::Animation2DProc_Rain< decltype(caeRain1), 32, eCxx::Rain2DConfig::Dir::Down > rain1d_encRGB(
        eCxx::Ext::RGB::black,
        eCxx::RGBColor(value1, value1, value1),
        eCxx::RGBColor(value2, value2, value2),
        eCxx::RGBColor(value3, value3, value3)
    );

    eCxx::Animation2DProc_Rain< decltype(caeRain2), 16, eCxx::Rain2DConfig::Dir::Right > rain2r_encRGB(
        eCxx::Ext::RGB::black,
        eCxx::RGBColor(value1, value1, value1),
        eCxx::RGBColor(value2, value2, value2),
        eCxx::RGBColor(value3, value3, value3)
    );

    eCxx::Animation2DProc_Rain< decltype(caeRain2), 16, eCxx::Rain2DConfig::Dir::Left > rain2l_encRGB(
        eCxx::Ext::RGB::black,
        eCxx::RGBColor(value1, value1, value1),
        eCxx::RGBColor(value2, value2, value2),
        eCxx::RGBColor(value3, value3, value3)
    );

    caeRain1.initSegment(0, rain1d_encRGB);

    caeRain2.initSegment(0, 0, 0, 15,  7, 0, rain2r_encRGB);
    caeRain2.initSegment(1, 0, 8, 15, 15, 0, rain2l_encRGB);

#ifdef SIMULATE_LED

    // ===== Test the results using LED simulator =====
#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_ENC_FILE_NAME, colorFX2D_encRGB_1616, 10, -1);
    caeFire1.reset();
    for(eCxx::CBP::QUInt i = 0; i < caeFire1.estimatedTotalAnimationFrameCount(); ++i) {
        caeFire1.update();
        colorFX2D_encRGB_1616.cie1931lcc58(); // Perform color correction to make the result visually better
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_encRGB_1616);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 2) eCxx::execSDF(TMP_SDF_ENC_FILE_NAME);
#endif

#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_ENC_FILE_NAME, colorFX2D_encRGB_1616, 10, -1);
    caeFire2.reset();
    for(eCxx::CBP::QUInt i = 0; i < caeFire2.estimatedTotalAnimationFrameCount(); ++i) {
        colorFX2D_encRGB_1616.clear();
        caeFire2.update();
        colorFX2D_encRGB_1616.cie1931lcc58(); // Perform color correction to make the result visually better
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_encRGB_1616);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 2) eCxx::execSDF(TMP_SDF_ENC_FILE_NAME);
#endif

#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_ENC_FILE_NAME, colorFX2D_encRGB_1616, 5, -1);
    caeRain1.reset();
    for(eCxx::CBP::QUInt i = 0; i < caeRain1.estimatedTotalAnimationFrameCount(); ++i) {
        caeRain1.update();
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_encRGB_1616);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 2) eCxx::execSDF(TMP_SDF_ENC_FILE_NAME);
#endif

#if 1
    sdf = eCxx::beginSDF_2D_RowMajor_RGB(TMP_SDF_ENC_FILE_NAME, colorFX2D_encRGB_1616, 5, -1);
    caeRain2.reset();
    for(eCxx::CBP::QUInt i = 0; i < caeRain2.estimatedTotalAnimationFrameCount(); ++i) {
        caeRain2.update();
        eCxx::writeSDF_2D_RGB(sdf, colorFX2D_encRGB_1616);
    }
    eCxx::endSDF(sdf);
    if(simGroup == 2) eCxx::execSDF(TMP_SDF_ENC_FILE_NAME);
#endif

#else // SIMULATE_LED

    // ===== Test the function calls only =====
    caeFire1.reset(); caeFire1.update();
    caeFire2.reset(); caeFire2.update();

    caeRain1.reset(); caeRain1.update();
    caeRain2.reset(); caeRain2.update();

#endif // SIMULATE_LED



    // Done
    return 0;
}
