#include "msgqueue.h"

#include <iostream.h>

#include <unistd.h>        // usleep
#include <sys/resource.h>  // PRIO_PROCESS

// --------------------------------------------------------------------------
//
// This creates the Message queue thread,
//
MsgQueue::MsgQueue() : fBreak(0)
{
    fMp = new unsigned char;
    pthread_create(&fThread, NULL, MapThread, this);
}

// --------------------------------------------------------------------------
//
// The destructor terminates the thread.
//
MsgQueue::~MsgQueue()
{
    cout << "~MsgQueue::MsgQueue" << endl;
    pthread_cancel(fThread);
    delete (unsigned char*)fMp;
}

// --------------------------------------------------------------------------
//
// This is the function which must be overloaded.
// Here you process the messages. mp is a pointer which you can
// specify when posting the message.
//
// If a new messages is posted while the old one is not yet
// finished the fBreak flag is set. Please test this flag with
// Break() and try to finish (or stop) the current action as soon
// as possible. This makes sure, that before a new action is started
// the old action can be finished correctly by the user.
//
void *MsgQueue::Proc(int msg, void *mp)
{
    return NULL;
}

// --------------------------------------------------------------------------
//
// This is the thread mapper.
//
void *MsgQueue::MapThread(void *arg)
{
    pthread_detach(pthread_self());

    setpriority(PRIO_PROCESS, 0, -5);

    ((MsgQueue*)arg)->Thread();

    return NULL;
}

// --------------------------------------------------------------------------
//
// This is the thread which handles the processing.
// As soon as a message is posted the fBreak flag is set (see PostMsg)
// And as soon as the current action is finished the new action is executed
// in this thread. This makes sure, that the calling program is not stalled.
//
void MsgQueue::Thread()
{
    //
    // Tell the poster that processing is done
    //
    fStart = 0;
    while (!fBreak)
        usleep(1);

    while(1)
    {
        while (!fStart) usleep(1);
        fStart = 0;

        //
        // This makes sure that also a very fast Break() after
        // a PostMsg is processed correctly
        //
        pthread_mutex_lock(&fMuxMsg);
        fBreak = 0;
        pthread_mutex_unlock(&fMuxMsg);

        cout << "Processing Msg 0x" << hex << fMsg << endl;
        // --- bool quit = fMsg==WM_QUIT;
        fRc=Proc(fMsg, fMp);
        cout << "Msg 0x" << hex << fMsg << " processed." << endl;

        // --- if (quit)
        // ---    break;
    }

    // --- fStart = 0;
    // --- cout << "WM_QUIT posted... leaving msg queue." << endl;
}

// --------------------------------------------------------------------------
//
// Use this function to post a message.
// mp can be a pointer to a data structure. size should be the size of it.
// size bytes of this structure are copied and a pointer to the copy
// is forwarded to the Proc function.
//
void *MsgQueue::PostMsg(int msg, void *mp, int size)
{
    //
    // Lock Mutex, put msg on stack and tell thread to process message
    //

    //
    // Make sure that only one Proc() is running and can be stopped
    // stopped and the messages are processed serialized
    //
    cout << "Locking MsgQueue mutex..." << flush;
    pthread_mutex_lock(&fMuxMsg);
    cout << "done." << endl;

    //
    // Set break state and wait until Proc() returned (break state deleted)
    // This means, that a new command is invoked and (if forseen) the
    // running command should stop execution.
    //
    // This is some kind of controlled user break without using signals
    //
    fBreak = 1;

    //
    // copy return code from Proc() and set new message
    //
    void *rc = fRc;

    fMsg = msg;

    delete (unsigned char*)fMp;
    fMp = new unsigned char[size];

    memcpy(fMp, mp, size);

    //
    // Start Proc()
    //
    fStart = 1;
    cout << "Releasing MsgQueue mutex..." << flush;
    pthread_mutex_unlock(&fMuxMsg);
    cout << "done." << endl;
    while (fStart)
        usleep(1);

    cout << "Returning rc = 0x" << hex << rc << endl;
    return rc;
}

