#include <MThread.h>

#include <iostream>

#include <pthread.h>
#include <sys/resource.h>  // PRIO_PROCESS

#undef DEBUG
//#define DEBUG

using namespace std;

// ----------------------------------------------------------------------
//
// Constructor
//
// Starts the derived thread if you don't specify false.
//
MThread::MThread(bool start, int prio) : fIsRunning(false), fIsDetached(false), fThread(NULL), fReturn(NULL)
{
#ifdef DEBUG
    cout << "MThread::MThread" << endl;
#endif
    if (!start)
        return;

    SetPriority(prio);
    Start();
}

// ----------------------------------------------------------------------
//
// Destructor
//
// Stops the derived thread if it is still running.
//
MThread::~MThread()
{
#ifdef DEBUG
    cout << "~MThread::MThread" << endl;
#endif
    Stop();
}

// ----------------------------------------------------------------------
//
// Detach the derived thread. This means, that if the thread ends the
// needed resources (eg. for storing the return code) are freed.
// In other word you cannot get any value back from the Thread.
//
void MThread::Detach()
{
    if (fIsRunning)
        pthread_detach(*fThread);

    fIsDetached = true;
}

// ----------------------------------------------------------------------
//
// Sets the priority of the thread.
//  -20 highest priority
//    0 standard
//  +20 lowest priority
// This can only be done before the thread is started.
//
bool MThread::SetPriority(int prio)
{
    if (fIsRunning)
        return false;

    fPriority = prio;
    return true;
}

// ----------------------------------------------------------------------
//
// Now we are back in a class instance, but running in new thread.
// All class members can be accessed like before.
// Set the flag for a running thread. Check if the thread should get
// detached. Set the priority of the thread. Now reset the stop flag and
// execute the thread. After the thread stopped it's execution reset the
// running flag and exit.
//
void *MThread::RunThread()
{
    fIsRunning = true;

    if (fIsDetached)
        pthread_detach(pthread_self());

    setpriority(PRIO_PROCESS, 0, fPriority); //lowest priority

    fStop = false;

#ifdef DEBUG
    cout << "MThread::RunThread" << endl;
#endif

    void *rc = Thread();

#ifdef DEBUG
    cout << "MThread::RunThread...done." << endl;
#endif

    fIsRunning = false;

    return rc;
}

// ----------------------------------------------------------------------
//
// Get the Instance back from the thread argument and call the
// RunThread member function, which handles all MThread bspecific stuff.
//
void *MThread::MapThread(void *arg)
{
    MThread *thread = (MThread*)arg;
#ifdef DEBUG
    cout << "MThread::MapThread" << endl;
#endif
    return thread->RunThread();
}

// ----------------------------------------------------------------------
//
// A thread is created and started (MapThread).
// As an argument the actual instance is used.
//
void MThread::Start()
{
    fThread = new pthread_t;
    pthread_create(fThread, NULL, MapThread, this);
}

// ----------------------------------------------------------------------
//
// Check if a thread is existing and running.
// If the thread is detached, cancel the thread. Otherwise set the stop
// flag and wait for the thread to exit.
//
void MThread::Stop()
{
#ifdef DEBUG
    cout << "MThread::Stop: fThread=" << fThread << ", fIsRunning=" << (int)fIsRunning << endl;
#endif

    if (!fThread || !fIsRunning)
        return;

    if (fIsDetached)
    {
#ifdef DEBUG
        cout << "Stopping detached thread..." << flush;
#endif
        pthread_cancel(*fThread);
        fIsRunning = false;
    }
    else
    {
#ifdef DEBUG
        cout << "Stopping thread..." << flush;
#endif
        fStop = true;
        pthread_join(*fThread, &fReturn);
    }
#ifdef DEBUG
    cout << "done." << endl;
#endif

    delete fThread;
    fThread = NULL;

#ifdef DEBUG
    cout << "MThread::Stop() done." << endl;
#endif
}
