/*
 * File: gthread.h
 * ---------------
 *
 * This file contains code related to multithreading.
 * Qt requires at least two threads to run: a main Qt GUI thread,
 * and a separate student code thread.
 * The student's main() function runs in this latter student thread.
 * You can also run code in a new thread using the static method
 * GThread::runInNewThread or GThread::runInNewThreadAsync.
 *
 * @version 2019/04/13
 * - reimplement GThread to wrap either QThread or std::thread
 * - add GThread abstract base class for thread abstractions
 * - add GThreadQt and GThreadStd subclasses
 * - rename GFunctionThread to QFunctionThread to reduce name confusion
 * - remove GStudentThread subclass and combine functionality into GThread
 * @version 2018/10/18
 * - improved thread names
 * @version 2018/09/08
 * - added doc comments for new documentation generation
 * @version 2018/08/23
 * - renamed to gthread.h to replace Java version
 * @version 2018/07/28
 * - initial version
 */

#ifndef _gthread_h
#define _gthread_h

#include <QThread>
#include <atomic>
#include <thread>

#include "gtypes.h"
#include "map.h"

class QtGui;

/**
 * A QFunctionThread is an object that runs a function in its own
 * thread of execution.
 *
 * You construct it, passing a void function to run as a parameter,
 * and then call its <code>run()</code> method to run that function in its
 * own thread.
 *
 * Clients generally do not need to access this class directly.
 * To use threads with our library, use the static methods
 * <code>GThread::runInNewThread</code> and
 * <code>GThread::runInNewThreadAsync</code>.
 * @private
 */
class QFunctionThread : public QThread {
public:
    /**
     * Constructs a new thread to execute with optional threadName.
     */
    QFunctionThread(GThunk func, const std::string& threadName = "");

    /**
     * Constructs a new thread to execute function returning int.
     */
    QFunctionThread(GThunkInt func, const std::string& threadName = "");

    /**
     * Returns the value returned by the function, if any, else 0.
     */
    int returnValue() const;

    /* @inherit */
    void start();


protected:
    /**
     * Executes the function passed to the constructor in its own thread.
     */
    void run();

private:
    Q_DISABLE_COPY(QFunctionThread)

    GThunk _func;
    GThunkInt _funcInt;
    bool _hasReturn;
    int _returnValue;
    std::string _name;
};

/**
 * The GThread class is a utility class containing static methods that allow
 * you to run code on various system threads.
 * The library has the following two standard threads running at all times:
 *
 * <ol>
 *     <li> The Qt GUI thread, which runs Qt's master exec() loop,
 *          handles all GUI object creation and events
 *          (this is technically the program's main thread)
 *
 *     <li> The student thread, which runs the student's main() function and
 *          any sub-functions called by main
 * </ol>
 *
 * Students and clients normally do not need to worry about threading issues.
 * These methods are called internally by many of the graphical interactors
 * to make sure that all internal Qt GUI widgets are initialized on the Qt GUI
 * thread.  This is required for them to function properly.
 *
 * If you want to run a piece of code in its own thread, use static methods
 * <code>GThread::runInNewThread</code> and
 * <code>GThread::runInNewThreadAsync</code>.
 */
class GThread {
public:

    /**
     * Returns true if the given thread is currently actively running.
     */
    virtual bool isRunning() const = 0;

    /**
     * Waits for this thread to finish.
     * Will wait indefinitely as needed.
     */
    virtual void join() = 0;

    /**
     * Waits for this thread to finish.
     * Will wait up to the given number of milliseconds.
     * Returns true if the thread has finished or false if it is still running.
     */
    virtual bool join(long ms) = 0;

    /**
     * Tells the thread to start running.
     */
    virtual void start() = 0;

    // TODO: methods to set a top-level exception handler


    /**
     * Generates an error if the caller is not running on the Qt GUI thread.
     * An optional error detail message can be passed.
     */
    static void ensureThatThisIsTheQtGuiThread(const std::string& message = "");

    /**
     * Returns true if the caller is running on the Qt GUI thread.
     */
    static bool iAmRunningOnTheQtGuiThread();

    /**
     * Returns true if the Qt GUI thread has been created.
     * This will happen right before the student's main() function runs.
     */
    static bool qtGuiThreadExists();

    /**
     * Causes current thread to pause itself for the given number of milliseconds.
     * @throw ErrorException if ms is negative
     */
    static void msleep(double ms);

    /**
     * Runs the given void function in its own new thread,
     * blocking the current thread to wait until it is done.
     * You can pass an optional name for the thread which can help when looking
     * through the list of threads in a debugger.
     *
     * Any uncaught exceptions or errors in the new thread will crash the
     * program and cannot be caught by the calling thread.
     *
     * If you want the new thread to run in the background,
     * use the <code>runInNewThreadAsync</code> function instead.
     */
    static void runInNewThread(GThunk func, const std::string& threadName = "");

    /**
     * Runs the given void function in its own new thread in the background;
     * the current thread does not block and keeps going.
     * You can pass an optional name for the thread which can help when looking
     * through the list of threads in a debugger.
     * Returns a pointer to the given thread in case you want to wait a given
     * amount of time for the thread to do its work.
     *
     * Any uncaught exceptions or errors in the new thread will crash the
     * program and cannot be caught by the calling thread.
     *
     * If you want the caller to wait for the new thread to finish running,
     * use the <code>runInNewThread</code> function instead.
     */
    static GThread* runInNewThreadAsync(GThunk func, const std::string& threadName = "");

    /**
     * Runs the given void function on the Qt GUI thread,
     * blocking the current thread to wait until it is done.
     * This function is called heavily by the internal GUI widgets and
     * interactors of the library, because all Qt GUI operations are required
     * to be done on the application's main thread.
     *
     * Any uncaught exceptions or errors in the Qt GUI thread will crash the
     * program and cannot be caught by the calling thread.
     *
     * If you want the new thread to run in the background,
     * use the <code>runOnQtGuiThreadAsync</code> function instead.
     */
    static void runOnQtGuiThread(GThunk func);

    /**
     * Runs the given void function on the Qt GUI thread in the background;
     * the current thread does not block and keeps going.
     *
     * Any uncaught exceptions or errors in the Qt GUI thread will crash the
     * program and cannot be caught by the calling thread.
     *
     * If you want the caller to wait for the Qt GUI thread code to finish running,
     * use the <code>runOnQtGuiThread</code> function instead.
     */
    static void runOnQtGuiThreadAsync(GThunk func);

    /**
     * Starts the student's thread, telling it to run the given function,
     * which accepts no arguments and returns an int.
     */
    static void startStudentThread(GThunkInt mainFunc);

    /**
     * Returns true if the student's thread has already been created.
     */
    static bool studentThreadExists();

    /**
     * Waits the given number of milliseconds for the given thread to finish.
     * @return true if the entire amount of ms was elapsed without the thread finishing
     */
    static bool wait(GThread* thread, long ms);

    /**
     * Indicates that the current thread is willing to yield execution to any
     * other threads that want to run.
     * This differs slightly from msleep() in that msleep() mandates to pause the
     * current thread for a given amount of time, while yield() is more of an
     * offer to other threads that they may run now if they so choose.
     */
    static void yield();

    /**
     * Sets the current thread to be the "Gui" thread for the application.
     * This is called by our library initialization code to inform the GThread
     * class what thread is the Gui thread.
     * Clients do not need to call this method directly.
     * @private
     */
    static void setCurrentThreadAsGuiThread();

protected:
    // forbid construction
    GThread();
    virtual ~GThread() = default;

    // pointers to the two core library threads
    static GThread* _qtGuiThread;
    static GThread* _studentThread;

private:
    friend class QtGui;
};


/**
 * A GThreadQt is an object that runs a function in its own
 * Qt thread of execution.
 * You construct it, passing a void function to run as a parameter,
 * and then call its <code>run()</code> method to run that function in its
 * own thread.
 *
 * This is provided so that you don't need to subclass QThread yourself
 * just to run a given piece of code.
 *
 * Clients generally do not need to access this class directly.
 * To use threads with our library, use the static methods
 * <code>GThread::runInNewThread</code> and
 * <code>GThread::runInNewThreadAsync</code>.
 * @private
 */
class GThreadQt : public GThread {
public:
    /**
     * Constructs a new thread to execute, with an optional thread name.
     */
    GThreadQt(GThunk func, const std::string& threadName = "");

    /**
     * Constructs a new thread to execute, with an optional thread name.
     */
    GThreadQt(GThunkInt func, const std::string& threadName = "");

    /**
     * Constructs a new thread to wrap the given existing Qt thread.
     */
    GThreadQt(QThread* qthread, const std::string& threadName = "");

    virtual ~GThreadQt();

    /* @inherit */
    bool isRunning() const override;

    /* @inherit */
    void join() override;

    /* @inherit */
    bool join(long ms) override;

    /* @inherit */
    void start() override;

private:
    Q_DISABLE_COPY(GThreadQt)

    QThread* _qThread;   // underlying real Qt thread
};

// Platform-specific way to set the name of current thread for display in debugger
void native_set_thread_name(const char *name);

// Platform-specific way to exit current thread
[[noreturn]] void native_thread_exit();

#endif // _gthread_h
