#include "Critical.h"
#include "Globals.h"
#include "Thread.h"
#include <iostream>
#include <time.h>

using namespace Framework;

// Inhalt der Critical class aus Critical.h
// Konstructor
Critical::Critical()
{
    InitializeCriticalSection( &cs );
    owner = 0;
    lockCount = 0;
    id = (int)time( 0 );
}

// Destructor
Critical::~Critical()
{
    DeleteCriticalSection( &cs );
}

// sperrt das Objekt
void Critical::lock()
{
    pthread_t t = GetCurrentThread();
    getThreadRegister()->lock();
    Thread* tmp = getThreadRegister()->zThread( t );
    if( tmp )
        tmp->addCriticalLock();
    getThreadRegister()->unlock();
    EnterCriticalSection( &cs );
    if( !owner )
        owner = tmp;
    lockCount++;
}

// versucht das Objekt zu sperren
bool Critical::tryLock()
{
    if( lockCount > 0 )
        return false;
    getThreadRegister()->lock();
    Thread* tmp = getThreadRegister()->zThread( GetCurrentThread() );
    if( tmp )
        tmp->addCriticalLock();
    getThreadRegister()->unlock();
    EnterCriticalSection( &cs );
    if( !owner )
        owner = tmp;
    lockCount++;
    return true;
}

// entsperrt das Objekt
void Critical::unlock()
{
    getThreadRegister()->lock();
    Thread* tmp = 0;
    if( getThreadRegister()->isThread( owner ) )
    {
        if( owner && GetThreadId( owner->getThreadHandle() ) != GetThreadId( GetCurrentThread() ) )
            throw std::runtime_error( "A Thread that does not own a Critical Object trys to unlock it" );
        tmp = owner;
    }
    getThreadRegister()->unlock();
    if( !--lockCount )
        owner = 0;
    LeaveCriticalSection( &cs );
    getThreadRegister()->lock();
    if( tmp && getThreadRegister()->isThread( tmp ) )
        tmp->removeCriticalLock();
    getThreadRegister()->unlock();
}


// gibt true zurück, wenn das Objekt gesperrt ist
bool Critical::isLocked() const
{
    return lockCount > 0;
}

// gibt einen Zeiger auf den Thread zurück, der das Objekt gesperrt hat
const Thread* Critical::zOwner() const
{
    return owner;
}

Synchronizer::Synchronizer()
    : numWaiting( 0 ),
    skip( 0 )
{}

Synchronizer::~Synchronizer()
{
    skip = 1;
    std::unique_lock<std::mutex> lk( mutex );
    if( numWaiting > 0 )
    {
        lk.unlock();
        block.notify_all();
    }
    else
        lk.unlock();
    while( numWaiting > 0 )
        Sleep( 10 );
    lk.lock();
}

bool Synchronizer::wait()
{
    std::unique_lock<std::mutex> lk( mutex );
    if( skip )
        return false;
    numWaiting++;
    block.wait( lk );
    numWaiting--;
    return !skip;
}

bool Synchronizer::wait( int milisec )
{
    std::unique_lock<std::mutex> lk( mutex );
    if( skip )
        return false;
    numWaiting++;
    std::cv_status status = block.wait_for( lk, std::chrono::milliseconds( milisec ) );
    numWaiting--;
    return !skip && status == std::cv_status::no_timeout;
}

void Synchronizer::notify()
{
    block.notify_one();
}

void Synchronizer::notify( int amount )
{
    while( amount-- )
        block.notify_one();
}

void Synchronizer::notifyAll()
{
    block.notify_all();
}

int Synchronizer::getNumberOfWaitingThreads() const
{
    return numWaiting;
}