#include "Thread.h"

#include "Globals.h"

using namespace Framework;

// inhalt der Thread Klasse aus Thread.h
// Konstruktor
Thread::Thread()
    : ReferenceCounter()
{
    threadHandleSys = 0;
    threadHandle = 0;
    threadId = 0;
    thRegister->add(this);
    run = 0;
    lockCount = 0;
    name = 0;
}

// Destruktor
Thread::~Thread()
{
    thRegister->remove(this);
#ifdef WIN32
    if (threadHandle != 0 && GetCurrentThreadId() == GetThreadId(threadHandle))
        return;
#else
    if (pthread_self() == threadHandle) return;
#endif
    if (run) warteAufThread(100000);
    if (run) ende();
}

// nicht constant
void Thread::start() // startet den Thread
{
    if (!run)
    {
        getThis();
#ifdef WIN32
        threadHandle = CreateThread(0, 0, threadStart, this, 0, &threadId);
#else
        pthread_create(&threadHandle, NULL, threadStart, this);
#endif
    }
    run = 1;
}
#ifdef WIN32
void Thread::pause() // pausiert den Thread
{
    if (run) SuspendThread(threadHandle);
    run = 0;
}

void Thread::fortsetzen() // pausiert den Thread
{
    if (!run) ResumeThread(threadHandle);
    run = 1;
}
#endif
void Thread::ende() // beendet den Thread
{
    if (run)
    {
        while (lockCount > 0)
            Sleep(100);
        bool rel = run;
#ifdef WIN32
#    pragma warning(suppress : 6258)
        TerminateThread(threadHandle, 0);
#else
        if (pthread_self() == threadHandle)
        {
            thRegister->addClosedThread(threadHandle);
            run = 0;
        }
        pthread_cancel(threadHandle);
#endif
        if (rel)
        {
            run = 0;
            release();
            return;
        }
    }
    run = 0;
}

void Thread::thread() // Thread
{}

void Thread::threadEnd()
{
    run = 0;
    release();
}

// constant
bool Thread::isRunning() const // prüft, ob der Thrad aktiv ist
{
    return run;
}

int Thread::warteAufThread(int zeit) // wartet zeit lang auf den Thread
{
#ifdef WIN32
    if (!run) return WAIT_OBJECT_0;
    if (GetCurrentThreadId() == GetThreadId(threadHandle)) return WAIT_OBJECT_0;
    return WaitForSingleObject(threadHandle, zeit);
#else
    if (!run) return 0;
    if (pthread_self() == threadHandle) return 0;
    timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 1000 * zeit;
    if (threadHandleSys) *threadHandleSys = 0;
    int ret = pthread_timedjoin_np(threadHandle, 0, &ts);
    if (!ret)
        threadHandle = 0;
    else
        *threadHandleSys = threadHandle;
    return ret;
#endif
}

// Legt einen Frameworkpointer auf ein Threadhandle fest, der auf 0 gesetzt
// wird, falls die Ressourcen des Threads bereits follstänfig aufgeräumt wurden
//  ths: Ein Zeiger auf ein Threadhandle, das verändert werden soll
void Thread::setSystemHandlePointer(pthread_t* ths)
{
    threadHandleSys = ths;
    *threadHandleSys = threadHandle;
}

pthread_t Thread::getThreadHandle() const
{
    return threadHandle;
}

// set the name of the thread
void Thread::setName(const char* name)
{
    this->name = name;
}

// get the name of the thread
const char* Thread::getName() const
{
    return name ? name : typeid(*this).name();
}

void Thread::addCriticalLock()
{
    lockCount++;
}

void Thread::removeCriticalLock()
{
    lockCount--;
}

// funktionen
#ifdef WIN32
unsigned long __stdcall Framework::threadStart(void* param)
{
    if (istThreadOk((Thread*)param))
    {
        const char* name = ((Thread*)param)->getName();
        wchar_t* wc = new wchar_t[textLength(name) + 1];
        size_t l;
        mbstowcs_s(&l, wc, textLength(name) + 1, name, textLength(name));
        SetThreadDescription(((Thread*)param)->getThreadHandle(), wc);
        delete[] wc;
        ((Thread*)param)->thread();
    }
    if (istThreadOk((Thread*)param)) ((Thread*)param)->threadEnd();
    return 0;
}
#else
void* Framework::threadStart(void* param)
{
    pthread_t handle = 0;
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
    if (istThreadOk((Thread*)param))
    {
        pthread_setname_np(
            ((Thread*)param)->getThreadHandle(), ((Thread*)param)->getName());
        ((Thread*)param)->setSystemHandlePointer(&handle);
        ((Thread*)param)->thread();
    }
    if (istThreadOk((Thread*)param)) ((Thread*)param)->threadEnd();
    thRegister->addClosedThread(handle);
    pthread_exit(0);
    return 0;
}
#endif

// Konstruktor
ThreadRegister::ThreadRegister()
{
    InitializeCriticalSection(&cs);
}

// Destruktor
ThreadRegister::~ThreadRegister()
{
    EnterCriticalSection(&cs);
    while (threads.hat(0))
    {
        LeaveCriticalSection(&cs);
        Sleep(1000); // wait until all threads are done
        EnterCriticalSection(&cs);
    }
    cleanUpClosedThreads();
    LeaveCriticalSection(&cs);
    DeleteCriticalSection(&cs);
}

// Inhalt der ThreadRegister Klasse aus Thread.h
void ThreadRegister::add(Thread* t)
{
    EnterCriticalSection(&cs);
    threads.add(t);
    LeaveCriticalSection(&cs);
}

void ThreadRegister::remove(Thread* t)
{
    EnterCriticalSection(&cs);
    threads.remove(threads.getWertIndex(t));
    LeaveCriticalSection(&cs);
}

bool ThreadRegister::isThread(Thread* t)
{
    EnterCriticalSection(&cs);
    bool ret = threads.hat(threads.getWertIndex(t));
    LeaveCriticalSection(&cs);
    return ret;
}

void ThreadRegister::addClosedThread(pthread_t handle)
{
    EnterCriticalSection(&cs);
    if (handle) closedThreads.add(handle);
    LeaveCriticalSection(&cs);
}

// Sucht nach einem bestimmten Thread und gibt das zugehörige Objekt zurück
// handle: Ein handle zu dem gesuchten Thread
Thread* ThreadRegister::zThread(pthread_t handle)
{
    EnterCriticalSection(&cs);
    for (auto i : threads)
    {
        if (i->getThreadHandle()
            && GetThreadId(i->getThreadHandle()) == GetThreadId(handle)
            && GetThreadId(handle) != 0)
        {
            LeaveCriticalSection(&cs);
            return i;
        }
    }
    LeaveCriticalSection(&cs);
    return 0;
}

// sperrt das register
void ThreadRegister::lock()
{
    EnterCriticalSection(&cs);
}

// entsperrt das register
void ThreadRegister::unlock()
{
    LeaveCriticalSection(&cs);
}

// Löscht die bereits beendetetn Threads und gibt ihre Reccourcen wieder frei
void ThreadRegister::cleanUpClosedThreads()
{
    EnterCriticalSection(&cs);
    while (closedThreads.getEintragAnzahl() > 0)
    {
#ifndef WIN32
        pthread_join(closedThreads.get(0), 0);
#endif
        closedThreads.remove(0);
    }
    LeaveCriticalSection(&cs);
}