#include "tap.hpp"
#include "types/description.hpp"
#include "types/dispatch.hpp"
#include "types.hpp"
#include "vector.hpp"


///////////////////////////////////////////////////////////////////////////////
enum an_enum_type : i16 { AN_ENUM_VALUE };


//-----------------------------------------------------------------------------
struct does_enum_match {
    template <typename TagT>
    bool operator() (cruft::type_tag<TagT> const&)
    {
        return false;
    }

    template <typename UnderlyingT>
    bool operator() (cruft::type_tag<cruft::unknown_enum_tag<UnderlyingT>> const &tag)
    {
        auto const expected = cruft::types::make_description<an_enum_type> ();
        return expected == tag.desc;
    }
};


///////////////////////////////////////////////////////////////////////////////
int main ()
{
    cruft::TAP::logger tap;

    tap.expect_eq (
        cruft::types::make_description<int> (),
        cruft::types::description {
            .category   = cruft::types::category::INTEGER,
            .signedness = true,
            .width      = sizeof (int),
            .arity      = 1,
            .alignment  = alignof (int),
            .index      = cruft::typeidx<int> (),
        },
        "description: int"
    );

    tap.expect_neq (
        cruft::types::make_description<i16> (),
        cruft::types::make_description<u16> (),
        "different signedness is unequal"
    );

    tap.expect_eq (
        cruft::types::make_description<an_enum_type> ().category,
        cruft::types::category::ENUM,
        "enums are described as such"
    );

    {
        auto const a = cruft::types::make_description<an_enum_type> ();
        auto const b = cruft::types::make_description<std::underlying_type_t<an_enum_type>> ();

        tap.expect_neq (a, b, "enum and underlying_type_t raw equality");

        cruft::types::underlying_comparator const cmp {};
        tap.expect (cmp (a, b), "enum and underlying_type equality");

        auto const c = cruft::types::make_description<u32> ();
        static_assert (!std::is_same_v<u32, std::underlying_type_t<an_enum_type>>);
        tap.expect (!cmp (a, c), "enum and underlying_type inequality");
    }

    auto const enum_type_descriptor = cruft::types::make_description<an_enum_type> ();
    tap.expect (
        cruft::types::visit (enum_type_descriptor, does_enum_match {}),
        "enum type descriptor dispatches correctly"
    );

    {
        auto const vector_descriptor = cruft::types::make_description<cruft::vector2f> ();

        tap.expect_eq (
            vector_descriptor.category,
            cruft::types::category_traits_v<float>,
            "vector2f type is float category"
        );

        tap.expect_eq (vector_descriptor.signedness, true, "vector2f type is signed");
        tap.expect_eq (vector_descriptor.width, sizeof (float), "vector2f width is sizeof(float)");
        tap.expect_eq (vector_descriptor.arity, 2u, "vector2f arity is 2");
        tap.expect_eq (vector_descriptor.alignment, alignof (cruft::vector2f), "vector2f alignment matches");
        tap.expect_eq (vector_descriptor.index, cruft::typeidx<cruft::vector2f> (), "vector2f index matches");
        tap.expect_eq (vector_descriptor.bytes (), sizeof (cruft::vector2f), "vector2f bytes matches");

    }

    return tap.status ();
}