
// g++ --std=c++11 test.cpp

// Test Coverage Summary:
// =======================
// This test suite provides comprehensive coverage of fl::sstream and fl::sstream_noop
// for all integer types across different platforms.
//
// Integer Types Tested:
// - fl:: types (13): i8, u8, i16, u16, i32, u32, i64, u64, size, uptr, iptr, ptrdiff, uint
// - Fundamental types (12): char, signed/unsigned char, short, unsigned short,
//                           int, unsigned int, long, unsigned long,
//                           long long, unsigned long long, bool
// - Fractional types (12): fract8, sfract7, fract16, sfract15, fract32, sfract31,
//                          accum88, saccum78, accum1616, saccum1516, accum124, saccum114
//
// Test Categories:
// 1. Individual type testing for compilation and basic output
// 2. Mixed type chains (multiple types in sequence)
// 3. Edge values (min/max values for each type)
// 4. Type qualifiers (const, volatile, const volatile)
// 5. Reference types (lvalue references, const references)
// 6. Template type deduction
// 7. fl::sstream_noop compatibility with all types
// 8. Platform-specific type aliasing (e.g., int==i16 on AVR)
//
// Platform Considerations:
// - Type aliasing varies by platform (e.g., Windows uses LLP64, Linux uses LP64)
// - signed char/fl::i8 may display as characters or numbers depending on implementation
// - INT32_MIN requires special handling to avoid negation issues in itoa
//
// All tests designed to compile without ambiguous overload errors and produce
// valid output across all supported platforms (AVR, ARM, ESP32, Desktop, WebAssembly).


#include "fl/stl/strstream.h"
#include "crgb.h"
#include "test.h"
#include "fl/math/fixed_point.h"
#include "fl/stl/int.h"
#include "fl/stl/cstring.h"
#include "fl/stl/ios.h"
#include "fl/stl/string.h"

FL_TEST_FILE(FL_FILEPATH) {

FL_TEST_CASE("sstream basic operations") {
    FL_SUBCASE("Construction and assignment") {
        fl::sstream s;
        FL_CHECK(s.str().size() == 0);
        FL_CHECK(s.str().c_str()[0] == '\0');

        fl::sstream s2("hello");
        FL_CHECK(s2.str().size() == 5);
        FL_CHECK(fl::strcmp(s2.str().c_str(), "hello") == 0);

        fl::sstream s3 = s2;
        FL_CHECK(s3.str().size() == 5);
        FL_CHECK(fl::strcmp(s3.str().c_str(), "hello") == 0);

        s = "world";
        FL_CHECK(s.str().size() == 5);
        FL_CHECK(fl::strcmp(s.str().c_str(), "world") == 0);
    }

    FL_SUBCASE("Comparison operators") {
        fl::sstream s1("hello");
        fl::sstream s2("hello");
        fl::sstream s3("world");

        FL_CHECK(s1.str() == s2.str());
        FL_CHECK(s1.str() != s3.str());
    }

    FL_SUBCASE("Indexing") {
        fl::sstream s("hello");
        FL_CHECK(s.str()[0] == 'h');
        FL_CHECK(s.str()[4] == 'o');
        FL_CHECK(s.str()[5] == '\0');  // Null terminator
    }

    FL_SUBCASE("Append") {
        fl::sstream s("hello");
        s << " world";
        FL_CHECK(s.str().size() == 11);
        FL_CHECK(fl::strcmp(s.str().c_str(), "hello world") == 0);
    }

    FL_SUBCASE("CRGB to fl::sstream") {
        CRGB c(255, 0, 0);
        fl::sstream s;
        s << c;
        FL_CHECK(s.str().size() == 13);  // "CRGB(255,0,0)" is 13 chars
        FL_CHECK(fl::strcmp(s.str().c_str(), "CRGB(255,0,0)") == 0);
    }
}

FL_TEST_CASE("sstream integer type handling") {
    FL_SUBCASE("uint8_t displays as number") {
        fl::sstream s;
        fl::u8 val = 65;  // ASCII 'A'
        s << val;
        // Should display as "65" (number), not "A" (character)
        FL_CHECK(fl::strcmp(s.str().c_str(), "65") == 0);
    }

    FL_SUBCASE("unsigned char displays as number") {
        fl::sstream s;
        unsigned char val = 65;
        s << val;
        // Should display as "65" (number) due to 1-byte unsigned → u16 cast
        FL_CHECK(fl::strcmp(s.str().c_str(), "65") == 0);
    }

    FL_SUBCASE("char and integer types compile") {
        // The main goal is to ensure all types can be used without ambiguous overloads
        // Actual behavior is tested more thoroughly in compile_test.cpp
        fl::sstream s;
        char c = 'A';
        signed char sc = 65;
        unsigned char uc = 66;

        // These should all compile without ambiguity
        s << c << sc << uc;

        // Basic sanity check - string should have some content
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("integer types that are NOT char display as numbers") {
        // unsigned char and other integer types should display as numbers
        fl::sstream s;

        // Test a type that's definitely not char
        short val = 65;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "65") == 0);
    }

    FL_SUBCASE("char with mTreatCharAsInt") {
        fl::sstream s;
        s.setTreatCharAsInt(true);
        char c = 65;
        s << c;
        // With mTreatCharAsInt=true, char should display as number
        FL_CHECK(fl::strcmp(s.str().c_str(), "65") == 0);
    }

    FL_SUBCASE("all fundamental integer types") {
        fl::sstream s;

        // Test various fundamental types produce correct output
        signed char sc = -10;
        unsigned char uc = 200;
        short sh = -1000;
        unsigned short us = 50000;
        int i = -100000;
        unsigned int ui = 4000000;
        long l = -1000000L;
        unsigned long ul = 4000000000UL;

        s << sc << " " << uc << " " << sh << " " << us << " ";
        s << i << " " << ui << " " << l << " " << ul;

        // Verify output contains expected values
        fl::string result_str = s.str(); // Store string before accessing c_str()
        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "-10") != nullptr);
        FL_CHECK(fl::strstr(result, "200") != nullptr);
        FL_CHECK(fl::strstr(result, "-1000") != nullptr);
        FL_CHECK(fl::strstr(result, "50000") != nullptr);
    }

    FL_SUBCASE("fl:: types work correctly") {
        fl::sstream s;

        fl::i8 i8v = -10;
        fl::u8 u8v = 200;
        fl::i16 i16v = -1000;
        fl::u16 u16v = 50000;
        fl::i32 i32v = -100000;
        fl::u32 u32v = 4000000;

        s << i8v << " " << u8v << " " << i16v << " " << u16v << " ";
        s << i32v << " " << u32v;

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "-10") != nullptr);
        FL_CHECK(fl::strstr(result, "200") != nullptr);
        FL_CHECK(fl::strstr(result, "-1000") != nullptr);
        FL_CHECK(fl::strstr(result, "50000") != nullptr);
    }

    FL_SUBCASE("chaining multiple types") {
        fl::sstream s;
        s << (short)1 << (long)2 << (unsigned char)3 << (int)4;

        // Should produce "1234"
        FL_CHECK(fl::strcmp(s.str().c_str(), "1234") == 0);
    }
}

FL_TEST_CASE("sstream comprehensive fl:: integer types") {
    FL_SUBCASE("fl::i8") {
        fl::sstream s;
        fl::i8 val = -10;
        s << val;
        // fl::i8 is signed char, which displays as a number due to fl::sstream's handling
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("fl::u8") {
        fl::sstream s;
        fl::u8 val = 200;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "200") == 0);
    }

    FL_SUBCASE("fl::i16") {
        fl::sstream s;
        fl::i16 val = -1000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "-1000") == 0);
    }

    FL_SUBCASE("fl::u16") {
        fl::sstream s;
        fl::u16 val = 50000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "50000") == 0);
    }

    FL_SUBCASE("fl::i32") {
        fl::sstream s;
        fl::i32 val = -100000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "-100000") == 0);
    }

    FL_SUBCASE("fl::u32") {
        fl::sstream s;
        fl::u32 val = 4000000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "4000000") == 0);
    }

    FL_SUBCASE("fl::i64") {
        fl::sstream s;
        fl::i64 val = -1000000000LL;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "-1000000000") == 0);
    }

    FL_SUBCASE("fl::u64") {
        fl::sstream s;
        fl::u64 val = 1000000000ULL;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "1000000000") == 0);
    }

    FL_SUBCASE("fl::size") {
        fl::sstream s;
        fl::size val = 12345;
        s << val;
        FL_CHECK(fl::strstr(s.str().c_str(), "12345") != nullptr);
    }

    FL_SUBCASE("fl::uptr") {
        fl::sstream s;
        fl::uptr val = 0x1234;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("fl::iptr") {
        fl::sstream s;
        fl::iptr val = -5000;
        s << val;
        FL_CHECK(fl::strstr(s.str().c_str(), "-5000") != nullptr);
    }

    FL_SUBCASE("fl::ptrdiff") {
        fl::sstream s;
        fl::ptrdiff val = -1234;
        s << val;
        FL_CHECK(fl::strstr(s.str().c_str(), "-1234") != nullptr);
    }

    FL_SUBCASE("fl::uint") {
        fl::sstream s;
        fl::uint val = 999999;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "999999") == 0);
    }
}

FL_TEST_CASE("sstream comprehensive fundamental integer types") {
    FL_SUBCASE("char") {
        fl::sstream s;
        char val = 'A';
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("signed char") {
        fl::sstream s;
        signed char val = -10;
        s << val;
        // signed char may display as character or number depending on fl::sstream implementation
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("unsigned char") {
        fl::sstream s;
        unsigned char val = 200;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "200") == 0);
    }

    FL_SUBCASE("short") {
        fl::sstream s;
        short val = -1000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "-1000") == 0);
    }

    FL_SUBCASE("unsigned short") {
        fl::sstream s;
        unsigned short val = 50000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "50000") == 0);
    }

    FL_SUBCASE("int") {
        fl::sstream s;
        int val = -100000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "-100000") == 0);
    }

    FL_SUBCASE("unsigned int") {
        fl::sstream s;
        unsigned int val = 4000000;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "4000000") == 0);
    }

    FL_SUBCASE("long") {
        fl::sstream s;
        long val = -1000000L;
        s << val;
        FL_CHECK(fl::strstr(s.str().c_str(), "-1000000") != nullptr);
    }

    FL_SUBCASE("unsigned long") {
        fl::sstream s;
        unsigned long val = 4000000000UL;
        s << val;
        FL_CHECK(fl::strstr(s.str().c_str(), "4000000000") != nullptr);
    }

    FL_SUBCASE("long long") {
        fl::sstream s;
        long long val = -1000000000LL;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "-1000000000") == 0);
    }

    FL_SUBCASE("unsigned long long") {
        fl::sstream s;
        unsigned long long val = 1000000000ULL;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "1000000000") == 0);
    }

    FL_SUBCASE("bool") {
        fl::sstream s;
        bool val_true = true;
        bool val_false = false;
        s << val_true << " " << val_false;
        FL_CHECK(s.str().size() > 0);
    }
}

FL_TEST_CASE("sstream fractional types") {
    FL_SUBCASE("fract8") {
        fl::sstream s;
        fract8 val = 128;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("sfract7") {
        fl::sstream s;
        sfract7 val = 64;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("fract16") {
        fl::sstream s;
        fract16 val = 30000;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("sfract15") {
        fl::sstream s;
        sfract15 val = -1000;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("fract32") {
        fl::sstream s;
        fract32 val = 2000000;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("sfract31") {
        fl::sstream s;
        sfract31 val = -100000;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("accum88") {
        fl::sstream s;
        accum88 val = 12800;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("saccum78") {
        fl::sstream s;
        saccum78 val = -6400;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("accum1616") {
        fl::sstream s;
        accum1616 val = 2000000;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("saccum1516") {
        fl::sstream s;
        saccum1516 val = -100000;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("accum124") {
        fl::sstream s;
        accum124 val = 4096;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("saccum114") {
        fl::sstream s;
        saccum114 val = -2048;
        s << val;
        FL_CHECK(s.str().size() > 0);
    }
}

FL_TEST_CASE("sstream mixed type chains") {
    FL_SUBCASE("mixed fl:: types") {
        fl::sstream s;
        s << fl::i8(-10) << " "
          << fl::u16(50000) << " "
          << fl::i32(-100000) << " "
          << fl::u64(1000000000ULL);

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "-10") != nullptr);
        FL_CHECK(fl::strstr(result, "50000") != nullptr);
        FL_CHECK(fl::strstr(result, "-100000") != nullptr);
        FL_CHECK(fl::strstr(result, "1000000000") != nullptr);
    }

    FL_SUBCASE("mixed fundamental types") {
        fl::sstream s;
        s << (signed char)(-10) << " "
          << (unsigned short)(50000) << " "
          << (int)(-100000) << " "
          << (unsigned long long)(1000000000ULL);

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "-10") != nullptr);
        FL_CHECK(fl::strstr(result, "50000") != nullptr);
        FL_CHECK(fl::strstr(result, "-100000") != nullptr);
        FL_CHECK(fl::strstr(result, "1000000000") != nullptr);
    }

    FL_SUBCASE("fl:: and fundamental types mixed") {
        fl::sstream s;
        s << fl::i8(-10) << " "
          << (short)(-1000) << " "
          << fl::u32(4000000) << " "
          << (unsigned long)(4000000000UL);

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "-10") != nullptr);
        FL_CHECK(fl::strstr(result, "-1000") != nullptr);
        FL_CHECK(fl::strstr(result, "4000000") != nullptr);
    }
}

FL_TEST_CASE("sstream edge value testing") {
    FL_SUBCASE("8-bit edge values") {
        fl::sstream s;
        s << fl::i8(-128) << " ";    // min i8
        s << fl::i8(127) << " ";     // max i8
        s << fl::u8(0) << " ";       // min u8
        s << fl::u8(255);            // max u8

        // Just check that output was generated, values depend on char handling
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("16-bit edge values") {
        fl::sstream s;
        s << fl::i16(-32768) << " ";  // min i16
        s << fl::i16(32767) << " ";   // max i16
        s << fl::u16(0) << " ";       // min u16
        s << fl::u16(65535);          // max u16

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strcmp(result, "-32768 32767 0 65535") == 0);
    }

    FL_SUBCASE("32-bit edge values") {
        fl::sstream s;
        // Avoid testing MIN_INT32 directly as it can cause issues with negation in itoa
        s << fl::i32(-2147483647) << " ";       // close to min i32
        s << fl::i32(2147483647) << " ";        // max i32
        s << fl::u32(0) << " ";                 // min u32
        s << fl::u32(4294967295U);              // max u32

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "-2147483647") != nullptr);
        FL_CHECK(fl::strstr(result, "2147483647") != nullptr);
        FL_CHECK(fl::strstr(result, "0") != nullptr);
        FL_CHECK(fl::strstr(result, "4294967295") != nullptr);
    }
}

FL_TEST_CASE("sstream const and volatile qualifiers") {
    FL_SUBCASE("const integer types") {
        fl::sstream s;
        const fl::i32 ci32 = 100;
        const fl::u32 cu32 = 200;
        const fl::i16 ci16 = 300;

        s << ci32 << " " << cu32 << " " << ci16;
        FL_CHECK(s.str().size() > 0);
        fl::string result_str = s.str(); // Store string before accessing c_str()

        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "100") != nullptr);
        FL_CHECK(fl::strstr(result, "200") != nullptr);
        FL_CHECK(fl::strstr(result, "300") != nullptr);
    }

    FL_SUBCASE("volatile integer types") {
        fl::sstream s;
        volatile fl::u32 vu32 = 200;
        volatile fl::i16 vi16 = -300;

        s << vu32 << " " << vi16;
        FL_CHECK(s.str().size() > 0);
        fl::string result_str = s.str(); // Store string before accessing c_str()

        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "200") != nullptr);
        FL_CHECK(fl::strstr(result, "-300") != nullptr);
    }

    FL_SUBCASE("const volatile integer types") {
        fl::sstream s;
        const volatile fl::i16 cvi16 = 300;
        const volatile fl::u8 cvu8 = 255;

        s << cvi16 << " " << cvu8;
        FL_CHECK(s.str().size() > 0);
        fl::string result_str = s.str(); // Store string before accessing c_str()

        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "300") != nullptr);
        FL_CHECK(fl::strstr(result, "255") != nullptr);
    }
}

FL_TEST_CASE("sstream reference types") {
    FL_SUBCASE("non-const references") {
        fl::sstream s;
        fl::i32 val = 100;
        fl::i32& ref = val;

        s << ref;
        FL_CHECK(fl::strcmp(s.str().c_str(), "100") == 0);
    }

    FL_SUBCASE("const references") {
        fl::sstream s;
        fl::i32 val = 100;
        const fl::i32& cref = val;

        s << cref;
        FL_CHECK(fl::strcmp(s.str().c_str(), "100") == 0);
    }

    FL_SUBCASE("mixed references") {
        fl::sstream s;
        fl::i32 val1 = 100;
        fl::u16 val2 = 200;
        fl::i32& ref1 = val1;
        const fl::u16& cref2 = val2;

        s << ref1 << " " << cref2;
        FL_CHECK(fl::strcmp(s.str().c_str(), "100 200") == 0);
    }
}

// Template helper function for type deduction testing
template<typename T>
bool test_template_type(T val) {
    fl::sstream s;
    s << val;
    return s.str().size() > 0;
}

FL_TEST_CASE("sstream template deduction") {
    FL_SUBCASE("fl:: types") {
        FL_CHECK(test_template_type(fl::i8(10)));
        FL_CHECK(test_template_type(fl::u16(1000)));
        FL_CHECK(test_template_type(fl::i32(-50000)));
        FL_CHECK(test_template_type(fl::u64(1000000ULL)));
    }

    FL_SUBCASE("fundamental types") {
        FL_CHECK(test_template_type((short)(100)));
        FL_CHECK(test_template_type((unsigned long)(1000000UL)));
        FL_CHECK(test_template_type((int)(-500)));
        FL_CHECK(test_template_type((unsigned char)(255)));
    }
}

FL_TEST_CASE("sstream_noop integer types") {
    FL_SUBCASE("all fl:: types") {
        fl::sstream_noop s;
        s << fl::i8(-10);
        s << fl::u8(200);
        s << fl::i16(-1000);
        s << fl::u16(50000);
        s << fl::i32(-100000);
        s << fl::u32(4000000);
        s << fl::i64(-1000000000LL);
        s << fl::u64(1000000000ULL);
        s << fl::size(12345);
        s << fl::uint(999999);
        // Should compile without errors
        FL_CHECK(true);
    }

    FL_SUBCASE("all fundamental types") {
        fl::sstream_noop s;
        s << (signed char)(-10);
        s << (unsigned char)(200);
        s << (short)(-1000);
        s << (unsigned short)(50000);
        s << (int)(-100000);
        s << (unsigned int)(4000000);
        s << (long)(-1000000L);
        s << (unsigned long)(4000000000UL);
        s << (long long)(-1000000000LL);
        s << (unsigned long long)(1000000000ULL);
        s << true;
        s << false;
        // Should compile without errors
        FL_CHECK(true);
    }

    FL_SUBCASE("fractional types") {
        fl::sstream_noop s;
        s << fract8(128);
        s << sfract7(64);
        s << fract16(30000);
        s << sfract15(-1000);
        s << fract32(2000000);
        s << sfract31(-100000);
        s << accum88(12800);
        s << saccum78(-6400);
        s << accum1616(2000000);
        s << saccum1516(-100000);
        s << accum124(4096);
        s << saccum114(-2048);
        // Should compile without errors
        FL_CHECK(true);
    }

    FL_SUBCASE("mixed type chains") {
        fl::sstream_noop s;
        s << fl::i8(-10) << " "
          << (short)(-1000) << " "
          << fl::u32(4000000) << " "
          << (unsigned long)(4000000000UL);
        // Should compile without errors
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream platform-specific aliased types") {
    FL_SUBCASE("type aliasing compatibility") {
        // Test that types which may alias on certain platforms work correctly
        fl::sstream s;
        int i = 100;
        long l = 100000L;
        fl::i16 i16 = 100;
        fl::i32 i32 = 100000;

        s << i << " " << l << " " << i16 << " " << i32;
        FL_CHECK(s.str().size() > 0);

        fl::string result_str = s.str(); // Store string before accessing c_str()


        const char* result = result_str.c_str();
        FL_CHECK(fl::strstr(result, "100") != nullptr);
        FL_CHECK(fl::strstr(result, "100000") != nullptr);
    }

    FL_SUBCASE("pointer-sized types") {
        // Test that pointer-sized types work correctly
        fl::sstream s;
        fl::size sz = 1234;
        fl::uptr up = 5678;
        fl::iptr ip = -999;
        fl::ptrdiff pd = -123;

        s << sz << " " << up << " " << ip << " " << pd;
        FL_CHECK(s.str().size() > 0);
    }
}

//=============================================================================
// COMPILE-TIME TYPE COMPATIBILITY TESTS (from test_sstring.cpp)
// Tests that sstream_noop and sstream support the exact same types
// ensuring API compatibility for compile-time checks
//=============================================================================

// Template-based stream operation tests - ensure both types accept same operations
template<typename StreamType>
void test_string_types(StreamType& s) {
    s << "hello" << " " << "world";
}

template<typename StreamType>
void test_char_types(StreamType& s) {
    s << char('A');
    s << (signed char)(-10);
    s << (unsigned char)(200);
}

template<typename StreamType>
void test_fundamental_int_types(StreamType& s) {
    s << (short)(-1000);
    s << (unsigned short)(50000);
    s << (int)(-100000);
    s << (unsigned int)(4000000);
    s << (long)(-1000000L);
    s << (unsigned long)(4000000000UL);
    s << (long long)(-1000000000LL);
    s << (unsigned long long)(1000000000ULL);
}

template<typename StreamType>
void test_fl_int_types(StreamType& s) {
    s << fl::i8(-10);
    s << fl::u8(200);
    s << fl::i16(-1000);
    s << fl::u16(50000);
    s << fl::i32(-100000);
    s << fl::u32(4000000);
    s << fl::i64(-1000000000LL);
    s << fl::u64(1000000000ULL);
    s << fl::size(12345);
    s << fl::uint(999999);
}

template<typename StreamType>
void test_floating_point_types(StreamType& s) {
    s << 3.14f;
    s << 2.71828;
}

template<typename StreamType>
void test_bool_type(StreamType& s) {
    s << true;
    s << false;
}

template<typename StreamType>
void test_crgb_type(StreamType& s) {
    CRGB rgb(255, 0, 0);
    s << rgb;
}

template<typename StreamType>
void test_cpp_string_type(StreamType& s) {
    fl::string str("test");
    s << str;
}

template<typename StreamType>
void test_mixed_types(StreamType& s) {
    s << "Value: " << fl::i32(42) << " Flag: " << true << " Float: " << 3.14f;
    s << " Char: " << char('X') << " Int: " << (short)100;
}

template<typename StreamType>
void test_const_types(StreamType& s) {
    const fl::i32 ci32 = 100;
    const fl::u32 cu32 = 200;
    const char* cstr = "const";
    s << ci32 << cu32 << cstr;
}

template<typename StreamType>
void test_reference_types(StreamType& s) {
    fl::i32 val = 100;
    fl::i32& ref = val;
    const fl::u16& cref = static_cast<const fl::u16&>(fl::u16(200));
    s << ref << cref;
}

template<typename StreamType>
void test_operator_chaining(StreamType& s) {
    s << "Start" << " " << fl::i32(42) << " " << true << " " << 3.14f << " End";
}

FL_TEST_CASE("sstream and sstream_noop - string types compatibility") {
    FL_SUBCASE("sstream handles strings") {
        fl::sstream stream;
        test_string_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles strings") {
        fl::sstream_noop stream;
        test_string_types(stream);
        FL_CHECK(true);  // sstream_noop is no-op but must compile
    }
}

FL_TEST_CASE("sstream and sstream_noop - character types compatibility") {
    FL_SUBCASE("sstream handles character types") {
        fl::sstream stream;
        test_char_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles character types") {
        fl::sstream_noop stream;
        test_char_types(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - fundamental integer types compatibility") {
    FL_SUBCASE("sstream handles all fundamental integer types") {
        fl::sstream stream;
        test_fundamental_int_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles all fundamental integer types") {
        fl::sstream_noop stream;
        test_fundamental_int_types(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - fl:: integer types compatibility") {
    FL_SUBCASE("sstream handles all fl:: integer types") {
        fl::sstream stream;
        test_fl_int_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles all fl:: integer types") {
        fl::sstream_noop stream;
        test_fl_int_types(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - floating point types compatibility") {
    FL_SUBCASE("sstream handles floating point types") {
        fl::sstream stream;
        test_floating_point_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles floating point types") {
        fl::sstream_noop stream;
        test_floating_point_types(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - bool type compatibility") {
    FL_SUBCASE("sstream handles bool") {
        fl::sstream stream;
        test_bool_type(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles bool") {
        fl::sstream_noop stream;
        test_bool_type(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - CRGB type compatibility") {
    FL_SUBCASE("sstream handles CRGB") {
        fl::sstream stream;
        test_crgb_type(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles CRGB") {
        fl::sstream_noop stream;
        test_crgb_type(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - fl::string type compatibility") {
    FL_SUBCASE("sstream handles fl::string") {
        fl::sstream stream;
        test_cpp_string_type(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles fl::string") {
        fl::sstream_noop stream;
        test_cpp_string_type(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - mixed types compatibility") {
    FL_SUBCASE("sstream handles mixed types") {
        fl::sstream stream;
        test_mixed_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles mixed types") {
        fl::sstream_noop stream;
        test_mixed_types(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("Template function instantiation with both stream types") {
    FL_SUBCASE("sstream instantiation") {
        fl::sstream s;
        test_string_types(s);
        test_char_types(s);
        test_fundamental_int_types(s);
        test_fl_int_types(s);
        test_floating_point_types(s);
        test_bool_type(s);
        test_crgb_type(s);
        test_cpp_string_type(s);
        test_mixed_types(s);
        FL_CHECK(s.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop instantiation") {
        fl::sstream_noop s;
        test_string_types(s);
        test_char_types(s);
        test_fundamental_int_types(s);
        test_fl_int_types(s);
        test_floating_point_types(s);
        test_bool_type(s);
        test_crgb_type(s);
        test_cpp_string_type(s);
        test_mixed_types(s);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - const and reference types compatibility") {
    FL_SUBCASE("sstream handles const and references") {
        fl::sstream stream;
        test_const_types(stream);
        test_reference_types(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop handles const and references") {
        fl::sstream_noop stream;
        test_const_types(stream);
        test_reference_types(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream and sstream_noop - operator chaining compatibility") {
    FL_SUBCASE("sstream supports operator chaining") {
        fl::sstream stream;
        test_operator_chaining(stream);
        FL_CHECK(stream.str().size() > 0);
    }

    FL_SUBCASE("sstream_noop supports operator chaining") {
        fl::sstream_noop stream;
        test_operator_chaining(stream);
        FL_CHECK(true);
    }
}

FL_TEST_CASE("sstream hex formatting") {
    FL_SUBCASE("hex manipulator for unsigned integers") {
        fl::sstream s;
        s << fl::hex << fl::u32(255);
        FL_CHECK(fl::strcmp(s.str().c_str(), "ff") == 0);
    }

    FL_SUBCASE("hex manipulator for signed integers") {
        fl::sstream s;
        s << fl::hex << fl::i32(255);
        FL_CHECK(fl::strcmp(s.str().c_str(), "ff") == 0);
    }

    FL_SUBCASE("hex manipulator with multiple values") {
        fl::sstream s;
        s << fl::hex << fl::u32(16) << " " << fl::u32(255) << " " << fl::u32(4096);
        FL_CHECK(fl::strcmp(s.str().c_str(), "10 ff 1000") == 0);
    }

    FL_SUBCASE("switching between dec and hex") {
        fl::sstream s;
        s << fl::u32(255);  // decimal by default
        s << " " << fl::hex << fl::u32(255);  // switch to hex
        s << " " << fl::dec << fl::u32(255);  // switch back to decimal
        FL_CHECK(fl::strcmp(s.str().c_str(), "255 ff 255") == 0);
    }

    FL_SUBCASE("hex with 8-bit values") {
        fl::sstream s;
        s << fl::hex << fl::u8(255);
        FL_CHECK(fl::strcmp(s.str().c_str(), "ff") == 0);
    }

    FL_SUBCASE("hex with 16-bit values") {
        fl::sstream s;
        s << fl::hex << fl::u16(0xABCD);
        FL_CHECK(fl::strcmp(s.str().c_str(), "abcd") == 0);
    }

    FL_SUBCASE("hex with 64-bit values") {
        fl::sstream s;
        s << fl::hex << fl::u64(0xDEADBEEF);
        FL_CHECK(fl::strcmp(s.str().c_str(), "deadbeef") == 0);
    }

    FL_SUBCASE("hex persists across multiple insertions") {
        fl::sstream s;
        s << fl::hex;
        s << fl::u32(10);
        s << " ";
        s << fl::u32(20);
        s << " ";
        s << fl::u32(30);
        FL_CHECK(fl::strcmp(s.str().c_str(), "a 14 1e") == 0);
    }

    FL_SUBCASE("getBase returns correct value") {
        fl::sstream s;
        FL_CHECK(s.getBase() == 10);  // default is decimal
        s << fl::hex;
        FL_CHECK(s.getBase() == 16);
        s << fl::oct;
        FL_CHECK(s.getBase() == 8);
        s << fl::dec;
        FL_CHECK(s.getBase() == 10);
    }
}

FL_TEST_CASE("sstream octal formatting") {
    FL_SUBCASE("oct manipulator for unsigned integers") {
        fl::sstream s;
        s << fl::oct << fl::u32(64);
        FL_CHECK(fl::strcmp(s.str().c_str(), "100") == 0);
    }

    FL_SUBCASE("oct manipulator with multiple values") {
        fl::sstream s;
        s << fl::oct << fl::u32(8) << " " << fl::u32(64) << " " << fl::u32(512);
        FL_CHECK(fl::strcmp(s.str().c_str(), "10 100 1000") == 0);
    }

    FL_SUBCASE("switching between dec, hex, and oct") {
        fl::sstream s;
        s << fl::u32(64);  // decimal by default
        s << " " << fl::hex << fl::u32(64);  // switch to hex
        s << " " << fl::oct << fl::u32(64);  // switch to octal
        s << " " << fl::dec << fl::u32(64);  // switch back to decimal
        FL_CHECK(fl::strcmp(s.str().c_str(), "64 40 100 64") == 0);
    }
}

FL_TEST_CASE("string append fixed_point types") {
    FL_SUBCASE("s16x16 append") {
        fl::fixed_point<16, 16, fl::Sign::SIGNED> val(1.5f);
        fl::string s;
        s.append(val);
        // 1.5 should produce "1.50"
        FL_CHECK(fl::strcmp(s.c_str(), "1.50") == 0);
    }

    FL_SUBCASE("u8x8 append") {
        fl::fixed_point<8, 8, fl::Sign::UNSIGNED> val(3.25f);
        fl::string s;
        s.append(val);
        FL_CHECK(fl::strcmp(s.c_str(), "3.25") == 0);
    }

    FL_SUBCASE("s8x8 negative append") {
        fl::fixed_point<8, 8, fl::Sign::SIGNED> val(-2.5f);
        fl::string s;
        s.append(val);
        FL_CHECK(fl::strcmp(s.c_str(), "-2.50") == 0);
    }

    FL_SUBCASE("sstream operator<< with fixed_point") {
        fl::fixed_point<16, 16, fl::Sign::SIGNED> val(4.75f);
        fl::sstream s;
        s << val;
        FL_CHECK(fl::strcmp(s.str().c_str(), "4.75") == 0);
    }
}

} // FL_TEST_FILE
