/// @file not_null.cpp
/// @brief Test for fl::not_null<T> pointer wrapper

#include "test.h"
#include "fl/stl/not_null.h"
#include "fl/stl/static_assert.h"

using namespace fl;

FL_TEST_CASE("not_null - construct from non-null pointer") {
    int value = 42;
    not_null<int*> ptr(&value);
    FL_CHECK(ptr.get() == &value);
}

FL_TEST_CASE("not_null - dereference operator") {
    int value = 42;
    not_null<int*> ptr(&value);
    FL_CHECK(*ptr == 42);
}

FL_TEST_CASE("not_null - arrow operator") {
    struct Point {
        int x;
        int y;
    };
    Point p{10, 20};
    not_null<Point*> ptr(&p);
    FL_CHECK(ptr->x == 10);
    FL_CHECK(ptr->y == 20);
}

FL_TEST_CASE("not_null - implicit conversion to raw pointer") {
    int value = 42;
    not_null<int*> ptr(&value);
    int* raw = ptr;  // Should compile (implicit conversion)
    FL_CHECK(raw == &value);
}

FL_TEST_CASE("not_null - copy construction") {
    int value = 42;
    not_null<int*> ptr1(&value);
    not_null<int*> ptr2(ptr1);
    FL_CHECK(ptr2.get() == &value);
}

FL_TEST_CASE("not_null - move construction") {
    int value = 42;
    not_null<int*> ptr1(&value);
    not_null<int*> ptr2(fl::move(ptr1));
    FL_CHECK(ptr2.get() == &value);
}

FL_TEST_CASE("not_null - assign non-null pointer") {
    int val1 = 10;
    int val2 = 20;
    not_null<int*> ptr(&val1);
    ptr = &val2;
    FL_CHECK(ptr.get() == &val2);
}

FL_TEST_CASE("not_null - compare with raw pointer") {
    int value = 42;
    not_null<int*> ptr(&value);
    FL_CHECK(ptr == &value);
}

FL_TEST_CASE("not_null - compare with another not_null") {
    int val1 = 10;
    int val2 = 20;
    not_null<int*> ptr1(&val1);
    not_null<int*> ptr2(&val2);
    not_null<int*> ptr3(&val1);

    FL_CHECK(ptr1 != ptr2);
    FL_CHECK(ptr1 == ptr3);
}

FL_TEST_CASE("not_null - const pointer") {
    const int value = 42;
    not_null<const int*> ptr(&value);
    FL_CHECK(*ptr == 42);
}

FL_TEST_CASE("not_null - array subscript operator") {
    int arr[5] = {1, 2, 3, 4, 5};
    not_null<int*> ptr(arr);
    FL_CHECK(ptr[0] == 1);
    FL_CHECK(ptr[2] == 3);
    FL_CHECK(ptr[4] == 5);
}

FL_TEST_CASE("not_null - polymorphic pointer") {
    struct Base {
        virtual int get() const { return 1; }
        virtual ~Base() = default;
    };

    struct Derived : Base {
        int get() const override { return 2; }
    };

    Derived d;
    not_null<Base*> ptr(&d);
    FL_CHECK(ptr->get() == 2);
}

FL_TEST_CASE("not_null - const conversion") {
    int value = 42;
    not_null<int*> ptr1(&value);
    not_null<const int*> ptr2(ptr1);  // Should compile
    FL_CHECK(*ptr2 == 42);
}

FL_TEST_CASE("not_null - modify through pointer") {
    int value = 42;
    not_null<int*> ptr(&value);
    *ptr = 100;
    FL_CHECK(value == 100);
    FL_CHECK(*ptr == 100);
}

FL_TEST_CASE("not_null - copy assignment") {
    int val1 = 10;
    int val2 = 20;
    not_null<int*> ptr1(&val1);
    not_null<int*> ptr2(&val2);
    ptr1 = ptr2;
    FL_CHECK(ptr1.get() == &val2);
}

FL_TEST_CASE("not_null - move assignment") {
    int val1 = 10;
    int val2 = 20;
    not_null<int*> ptr1(&val1);
    not_null<int*> ptr2(&val2);
    ptr1 = fl::move(ptr2);
    FL_CHECK(ptr1.get() == &val2);
}

FL_TEST_CASE("not_null - converting constructor") {
    struct Base {};
    struct Derived : Base {};

    Derived d;
    not_null<Derived*> ptr1(&d);
    not_null<Base*> ptr2(ptr1);  // Should compile
    FL_CHECK(static_cast<Base*>(&d) == ptr2.get());
}

FL_TEST_CASE("not_null - ordering comparisons") {
    int arr[3] = {1, 2, 3};
    not_null<int*> ptr1(&arr[0]);
    not_null<int*> ptr2(&arr[1]);
    not_null<int*> ptr3(&arr[2]);

    FL_CHECK(ptr1 < ptr2);
    FL_CHECK(ptr2 < ptr3);
    FL_CHECK(ptr1 <= ptr1);
    FL_CHECK(ptr3 > ptr2);
    FL_CHECK(ptr3 >= ptr3);
}

FL_TEST_CASE("not_null - function pointer") {
    auto func = +[](int x) { return x * 2; };
    not_null<int(*)(int)> ptr(func);
    FL_CHECK((*ptr)(5) == 10);
}

// Smart pointer integration tests
#include "fl/stl/unique_ptr.h"
#include "fl/stl/shared_ptr.h"

FL_TEST_CASE("not_null - works with unique_ptr") {
    auto uptr = fl::make_unique<int>(42);
    not_null<int*> ptr(uptr.get());
    FL_CHECK(*ptr == 42);
}

FL_TEST_CASE("not_null - works with shared_ptr") {
    auto sptr = fl::make_shared<int>(42);
    not_null<int*> ptr(sptr.get());
    FL_CHECK(*ptr == 42);
}

FL_TEST_CASE("not_null - modify through smart pointer") {
    auto uptr = fl::make_unique<int>(42);
    not_null<int*> ptr(uptr.get());
    *ptr = 100;
    FL_CHECK(*uptr == 100);
    FL_CHECK(*ptr == 100);
}

// ============================================================================
// Edge Case Tests: FastLED-specific types
// ============================================================================

#include "fl/gfx/crgb.h"
#include "fl/stl/move.h"
#include "fl/stl/type_traits.h"

FL_TEST_FILE(FL_FILEPATH) {

FL_TEST_CASE("not_null - works with CRGB pointer") {
    fl::CRGB pixel(255, 128, 64);
    not_null<fl::CRGB*> ptr(&pixel);

    FL_CHECK(ptr->r == 255);
    FL_CHECK(ptr->g == 128);
    FL_CHECK(ptr->b == 64);
}

FL_TEST_CASE("not_null - array of CRGB pixels") {
    fl::CRGB pixels[3];
    pixels[0] = fl::CRGB(255, 0, 0);    // Red
    pixels[1] = fl::CRGB(0, 255, 0);    // Green
    pixels[2] = fl::CRGB(0, 0, 255);    // Blue

    not_null<fl::CRGB*> ptr(pixels);

    FL_CHECK(ptr[0].r == 255);
    FL_CHECK(ptr[1].g == 255);
    FL_CHECK(ptr[2].b == 255);
}

FL_TEST_CASE("not_null - modify CRGB through pointer") {
    fl::CRGB pixel(0, 0, 0);
    not_null<fl::CRGB*> ptr(&pixel);

    ptr->r = 255;
    ptr->g = 128;
    ptr->b = 64;

    FL_CHECK(pixel.r == 255);
    FL_CHECK(pixel.g == 128);
    FL_CHECK(pixel.b == 64);
}

FL_TEST_CASE("not_null - const CRGB pointer") {
    const fl::CRGB pixel(255, 128, 64);
    not_null<const fl::CRGB*> ptr(&pixel);

    FL_CHECK(ptr->r == 255);
    FL_CHECK(ptr->g == 128);
    FL_CHECK(ptr->b == 64);
}

// ============================================================================
// Edge Case Tests: Function-like usage patterns
// ============================================================================

// Helper function demonstrating typical FastLED usage pattern
static void setPixelsRed(not_null<fl::CRGB*> leds, int count) {
    // No need to check for null - guaranteed by type
    for (int i = 0; i < count; i++) {
        leds[i] = fl::CRGB(255, 0, 0);
    }
}

FL_TEST_CASE("not_null - function parameter pattern") {
    fl::CRGB pixels[5];
    setPixelsRed(pixels, 5);  // Implicit conversion from array to not_null

    for (int i = 0; i < 5; i++) {
        FL_CHECK(pixels[i].r == 255);
        FL_CHECK(pixels[i].g == 0);
        FL_CHECK(pixels[i].b == 0);
    }
}

// ============================================================================
// Edge Case Tests: Member pointer (data member pointer - not function member pointer)
// ============================================================================

FL_TEST_CASE("not_null - struct with multiple pointers") {
    struct LedStrip {
        fl::CRGB* mPixels;
        int mCount;
    };

    fl::CRGB pixels[10];
    LedStrip strip{pixels, 10};

    not_null<LedStrip*> ptr(&strip);
    FL_CHECK(ptr->mPixels == pixels);
    FL_CHECK(ptr->mCount == 10);
}

// ============================================================================
// Edge Case Tests: Pointer arithmetic
// ============================================================================

FL_TEST_CASE("not_null - pointer arithmetic remains valid") {
    int arr[5] = {10, 20, 30, 40, 50};
    not_null<int*> ptr(arr);

    // Get raw pointer for arithmetic (implicit conversion)
    int* raw = ptr;
    int* next = raw + 2;

    FL_CHECK(*next == 30);
}

// ============================================================================
// Edge Case Tests: Comparison symmetry
// ============================================================================

FL_TEST_CASE("not_null - comparison symmetry with raw pointer") {
    int value = 42;
    not_null<int*> ptr(&value);

    // Both directions should work (member and non-member operators)
    FL_CHECK(ptr == &value);
    FL_CHECK(&value == ptr);  // Non-member operator

    int other = 100;
    FL_CHECK(ptr != &other);
    FL_CHECK(&other != ptr);  // Non-member operator
}

// ============================================================================
// Compile-Time Constraint Tests
// ============================================================================

// These tests verify that not_null enforces correct type constraints at compile time
// by using type traits and static_assert where appropriate

FL_TEST_CASE("not_null - compile-time type constraints") {
    // Verify that not_null works with pointer types
    // (this test compiles = constraint satisfied)
    int value = 42;
    not_null<int*> ptr1(&value);
    FL_CHECK(ptr1.get() == &value);

    // Verify works with const pointers
    const int cvalue = 100;
    not_null<const int*> ptr2(&cvalue);
    FL_CHECK(ptr2.get() == &cvalue);

    // Verify works with pointer to const
    not_null<const int*> ptr3(&value);
    FL_CHECK(ptr3.get() == &value);

    // Verify works with smart pointer get()
    auto uptr = fl::make_unique<int>(42);
    not_null<int*> ptr4(uptr.get());
    FL_CHECK(*ptr4 == 42);
}

// Test that verifies the type traits are working correctly
FL_TEST_CASE("not_null - type traits verification") {
    // Verify is_comparable_to_nullptr works
    FL_STATIC_ASSERT(fl::detail::is_comparable_to_nullptr<int*>::value,
                  "int* should be comparable to nullptr");
    FL_STATIC_ASSERT(fl::detail::is_comparable_to_nullptr<const int*>::value,
                  "const int* should be comparable to nullptr");
    FL_STATIC_ASSERT(fl::detail::is_comparable_to_nullptr<fl::CRGB*>::value,
                  "CRGB* should be comparable to nullptr");

    // Verify is_reference works
    FL_STATIC_ASSERT(!fl::detail::is_reference<int>::value,
                  "int should not be a reference");
    FL_STATIC_ASSERT(!fl::detail::is_reference<int*>::value,
                  "int* should not be a reference");
    FL_STATIC_ASSERT(fl::detail::is_reference<int&>::value,
                  "int& should be a reference");
    FL_STATIC_ASSERT(fl::detail::is_reference<int&&>::value,
                  "int&& should be a reference");

    // Verify is_dereferenceable works
    FL_STATIC_ASSERT(fl::detail::is_dereferenceable<int*>::value,
                  "int* should be dereferenceable");
    FL_STATIC_ASSERT(fl::detail::is_dereferenceable<fl::CRGB*>::value,
                  "CRGB* should be dereferenceable");

    // This test passes if all static_assert succeed at compile time
    FL_CHECK(true);
}

// Test deleted constructors and assignment operators
FL_TEST_CASE("not_null - deleted operations are compile-time enforced") {
    // These operations should NOT compile (commented out to prevent compilation errors):
    // not_null<int*> ptr1;                  // Error: default constructor deleted
    // not_null<int*> ptr2(nullptr);         // Error: nullptr constructor deleted
    // not_null<int*> ptr3(&value);
    // ptr3 = nullptr;                       // Error: nullptr assignment deleted

    // This test verifies the implementation has deleted operations
    // The fact that the above lines are commented out and would fail to compile
    // is the test itself. We just need a passing check to keep the test framework happy.
    FL_CHECK(true);
}

} // FL_TEST_FILE
