#include "thread/flag.hpp"
#include "thread/thread.hpp"
#include "parallel/stack.hpp"

#include "tap.hpp"



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

    {
        // Ensure trivial success/failure notifications work
        cruft::parallel::stack<int> values (1);
        tap.expect_eq (values.push (0), true,  "uncontested empty push succeeds");
        tap.expect_eq (values.push (0), false, "uncontested full push fails");

        int out;
        tap.expect_eq (values.pop (&out), true, "uncontested full pop succeeds");

        // Use a nullptr to test this case so that we know if the output
        // variable is dereferenced before the capacity is tested.
        tap.expect_eq (values.pop (nullptr), false, "uncontested empty pop fails");
    }

    {
        static constexpr int COUNT = 4;
        cruft::parallel::stack<int> values (COUNT);
        for (int i = 0; i < COUNT; ++i)
            values.push (i);

        bool success = true;
        for (int i = COUNT - 1; i >= 0; --i) {
            int res = -1;
            values.pop (&res);

            success = success && res == i;
        }

        tap.expect (success, "simple popped value sequence matches expected");
    }

    {
        auto fight = [] (cruft::parallel::stack<int> &store, cruft::thread::flag &ev, int iterations) {
            ev.wait ();

            while (iterations--) {
                while (!store.push (iterations))
                    ;

                for (int res; !store.pop (&res); )
                    ;
            }
        };

        static int constexpr ITERATIONS = 8 * 1024;
        cruft::parallel::stack<int> store (8);
        cruft::thread::flag ev;
        std::vector<cruft::thread::thread> contestants;
        for (unsigned i = 0; i < cruft::thread::thread::hardware_concurrency () + 1; ++i)
            contestants.emplace_back (fight, std::ref (store), std::ref (ev), ITERATIONS);

        ev.notify_all ();

        for (auto &t: contestants)
            t.join ();

        tap.expect (true, "n-way fight, %! contestants", contestants.size ());
    }

    return tap.status ();
}