/**
 * Copyright (c) 2016, Adrien Devresse <adrien.devresse@epfl.ch>
 *
 * Boost Software License - Version 1.0
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */


#include <future>
#include <mutex>
#include <thread>

#include <boost/test/floating_point_comparison.hpp>


#include <boost/chrono.hpp>
#include <boost/random.hpp>

#include <hadoken/format/format.hpp>

#include <hadoken/executor/simple_thread_executor.hpp>
#include <hadoken/executor/system_executor.hpp>
#include <hadoken/executor/thread_pool_executor.hpp>


using namespace boost::chrono;

typedef system_clock::time_point tp;
typedef system_clock cl;


template <typename Executor>
std::size_t executor_test(std::size_t n_exec, const std::string& executor_name) {

    tp t1, t2;

    int val = 0;

    std::plus<int> add;



    Executor executor;

    t1 = cl::now();

    for (std::size_t i = 0; i < n_exec; ++i) {

        std::packaged_task<int(int, int)> task(add);
        auto f = task.get_future();

        executor.execute([&]() { task(40, 2); });

        val += f.get();
    }

    t2 = cl::now();

    std::cout << executor_name << ": " << double(boost::chrono::duration_cast<microseconds>(t2 - t1).count()) / n_exec
              << std::endl;

    return val;
}


template <typename Executor>
std::size_t executor_test_twoway(std::size_t n_exec, const std::string& executor_name) {

    tp t1, t2;

    int val = 0;

    std::plus<int> add;



    Executor executor;

    t1 = cl::now();

    for (std::size_t i = 0; i < n_exec; ++i) {

        auto f = executor.twoway_execute(std::function<int()>([&add, i]() { return add(i, i); }));

        val += f.get();
    }

    t2 = cl::now();

    std::cout << executor_name << ": " << double(boost::chrono::duration_cast<microseconds>(t2 - t1).count()) / n_exec
              << std::endl;

    return val;
}



int main() {

    const std::size_t n_exec = 20000;
    std::size_t junk = 0;

    hadoken::format::scat(std::cout, "test executors for ", n_exec, " iterations ");

    junk += executor_test<hadoken::thread_pool_executor>(n_exec, "pool_executor");

    junk += executor_test<hadoken::simple_thread_executor>(n_exec, "simple_executor");

    junk += executor_test<hadoken::system_executor>(n_exec, "system_executor");

    junk += executor_test_twoway<hadoken::thread_pool_executor>(n_exec, "pool_executor_twoway");

    std::cout << "end junk " << junk << std::endl;
}
