#include "signal.hpp"

#include "except.hpp"
#include "tap.hpp"


//-----------------------------------------------------------------------------
void
test_null (cruft::TAP::logger &tap)
{
    tap.expect_nothrow ([] {
        cruft::signal<void(void)> void_signal;
        void_signal ();
    }, "void signal");
}


///////////////////////////////////////////////////////////////////////////////
void
increment_uint (unsigned int &val)
{
    ++val;
}


//-----------------------------------------------------------------------------
void
test_single (cruft::TAP::logger &tap)
{
    unsigned int val = 0;
    cruft::signal<void(unsigned int&)> void_signal;

    auto cookie = void_signal.connect (increment_uint);
    void_signal (val);

    tap.expect_eq (val, 1u, "single listener");
}


//-----------------------------------------------------------------------------
void
test_double (cruft::TAP::logger &tap)
{
    unsigned int val = 0;
    cruft::signal<void(unsigned int&)> void_signal;

    auto cookie0 = void_signal.connect (increment_uint);
    auto cookie1 = void_signal.connect (increment_uint);
    void_signal (val);

    tap.expect_eq (val, 2u, "double listener");
}


///////////////////////////////////////////////////////////////////////////////
void
test_value_signal (cruft::TAP::logger &tap)
{
    cruft::value_signal<unsigned> val;
    unsigned passed = 0;

    auto cookie = val.connect ([&] (unsigned v) { passed = v; });

    val = 42u;
    
    tap.expect_eq (passed, 42u, "value signal, passed value");

    unsigned check = val;
    tap.expect_eq (check, 42u, "value signal, read value");
}


///////////////////////////////////////////////////////////////////////////////
void
test_combiner (cruft::TAP::logger &tap)
{
    {
        cruft::signal<bool(void), cruft::combine::logical_and> sig;

        unsigned count = 0;
        auto cookie0 = sig.connect ([&] (void) { ++count; return true; });
        auto cookie1 = sig.connect ([&] (void) { ++count; return true; });
        auto cookie2 = sig.connect ([&] (void) { ++count; return true; });

        tap.expect (sig (), "bool signal, success");
        tap.expect_eq (count, 3u, "bool signal, success, count");
    }

    {
        cruft::signal<bool(void), cruft::combine::logical_and> sig;

        unsigned count = 0;
        auto cookie0 = sig.connect ([&] (void) { ++count; return true; });
        auto cookie1 = sig.connect ([&] (void) { ++count; return false; });
        auto cookie2 = sig.connect ([&] (void) { ++count; return true; });

        tap.expect (!sig (), "bool signal, failure");

        // ordering of signals is not guaranteed so we can't say for sure how
        // many callbacks will be triggered; it will _probably_ be in order
        // though.
        tap.expect_le (count, 3u, "bool signal, failure, count");
    }
}


///////////////////////////////////////////////////////////////////////////////
void
test_disconnect (cruft::TAP::logger &tap)
{
    tap.expect_nothrow ([] {
        cruft::signal<void(void)> sig;

        cruft::signal<void(void)>::cookie a = sig.connect ([&] (void) { sig.disconnect (a); });
        cruft::signal<void(void)>::cookie b = sig.connect ([&] (void) { sig.disconnect (b); });
        cruft::signal<void(void)>::cookie c = sig.connect ([&] (void) { sig.disconnect (c); });
        cruft::signal<void(void)>::cookie d = sig.connect ([&] (void) { sig.disconnect (d); });

        sig ();
    }, "parallel disconnect in invocation");
}


///////////////////////////////////////////////////////////////////////////////
int
main (int, char **)
{
    return cruft::TAP::logger::run ([] (auto &tap) {
        test_null   (tap);
        test_single (tap);
        test_double (tap);
        test_value_signal (tap);
        test_combiner (tap);
        test_disconnect (tap);
    });
}