#include <random>
#include <sled/synchronization/event.h>
#include <sled/system/fiber/wait_group.h>
#include <sled/system/thread_pool.h>

std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist(-5, 5);
auto rnd = std::bind(dist, mt);

void
simulate_hard_computation()
{
    // std::this_thread::sleep_for(std::chrono::milliseconds(10 + rnd()));
}

// Simple function that adds multiplies two numbers and prints the result
void
multiply(const int a, const int b)
{
    simulate_hard_computation();
    const int res = a * b;
}

// Same as before but now we have an output parameter
void
multiply_output(int &out, const int a, const int b)
{
    simulate_hard_computation();
    out = a * b;
}

// Same as before but now we have an output parameter
int
multiply_return(const int a, const int b)
{
    simulate_hard_computation();
    const int res = a * b;
    return res;
}

TEST_SUITE("ThreadPool")
{
    TEST_CASE("PostTask")
    {
        sled::ThreadPool *tp = new sled::ThreadPool();
        sled::Event waiter;
        tp->PostTask([&]() { waiter.Set(); });
        CHECK(waiter.Wait(sled::TimeDelta::Seconds(1)));
        delete tp;
    }

    TEST_CASE("PostDelayedTask")
    {
        sled::ThreadPool *tp = new sled::ThreadPool();
        sled::Event waiter;
        tp->PostDelayedTask([&]() { waiter.Set(); }, sled::TimeDelta::Millis(100));
        CHECK_FALSE(waiter.Wait(sled::TimeDelta::Millis(50)));
        CHECK(waiter.Wait(sled::TimeDelta::Millis(150)));
        delete tp;
    }

    TEST_CASE("10^5 task test")
    {
        sled::ThreadPool *tp = new sled::ThreadPool();
        const int task_num   = 1E5;
        sled::WaitGroup wg(task_num);
        for (int i = 0; i < task_num; i++) {
            tp->PostTask([wg] { wg.Done(); });
        }
        wg.Wait();
    }
}
