#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <cmath>

int f( int x, int y )
{
    std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) );
    return std::pow( x,y );
}

int main()
{
    // future from a packaged_task
    std::packaged_task<int()> task( []() {
        std::this_thread::sleep_for( std::chrono::milliseconds( 400 ) );
        return 7;
    } ); // wrap the function

    std::future<int> f1 = task.get_future();  // get a future
    std::thread( std::move( task ) ).detach(); // launch on a thread, no need to join

    std::cout << "Doing stuff..." << std::flush;
    
    // is it ready yet
    if( f1.wait_for( std::chrono::seconds( 0 ) ) == std::future_status::ready ) {
        std::cout << "f1 is ready\n";
    } else {
        std::cout << "f1 is not ready\n";
    }
   
    // future from an async()
    std::future<int> f2 = std::async( std::launch::async, []() {
        std::this_thread::sleep_for( std::chrono::milliseconds( 900 ) );
        return 8;
    } );
   
    // regular thread
    std::packaged_task<int( int,int )> task_pow( f );
    std::future<int> f3 = task_pow.get_future();
    std::thread task_td( std::move( task_pow ), 2, 10 );
  
    std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();

    //wait for futures
#if 1 // are we there yet, are we there yet, are we there yet, are we there yet, are we there yet, 
    bool all_ready = false;
    while( ! all_ready ) {
        all_ready = true;
        // is it ready yet
        if( f1.wait_for( std::chrono::seconds( 0 ) ) == std::future_status::ready ) {
            std::cout << "f1 is ready\n";
        } else {
            std::cout << "f1 is not ready\n";
            all_ready = false;
        }
        if( f2.wait_for( std::chrono::seconds( 0 ) ) == std::future_status::ready ) {
            std::cout << "f2 is ready\n";
        } else {
            std::cout << "f2 is not ready\n";
            all_ready = false;
        }
        if( f3.wait_for( std::chrono::seconds( 0 ) ) == std::future_status::ready ) {
            std::cout << "f3 is ready\n";
        } else {
            std::cout << "f3 is not ready\n";
            all_ready = false;
        }
        std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
    }
#else // wake me up when ready
    f1.wait();
    f2.wait();
    f2.wait();
#endif
    std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "Wait time " << elapsed_seconds.count() << std::endl;

    task_td.join();
    std::cout << "Done!\nResults are: "
              << f1.get() << ' '
              << f2.get() << ' '
              << f3.get()
              << '\n';
}