/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-10 by Raw Material Software Ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.

   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

  ------------------------------------------------------------------------------

   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.

  ==============================================================================
*/

#include "../core/juce_StandardHeader.h"

BEGIN_JUCE_NAMESPACE


#include "juce_Thread.h"
#include "juce_ScopedLock.h"
#include "../core/juce_Time.h"

// these functions are implemented in the platform-specific code.
void* juce_createThread (void* userData);
void juce_killThread (void* handle);
bool juce_setThreadPriority (void* handle, int priority);
void juce_setCurrentThreadName (const String& name);
#if JUCE_WIN32
void juce_CloseThreadHandle (void* handle);
#endif


//==============================================================================
void Thread::threadEntryPoint (Thread* const thread)
{
    {
        const ScopedLock sl (runningThreadsLock);
        runningThreads.add (thread);
    }

    JUCE_TRY
    {
        thread->threadId_ = Thread::getCurrentThreadId();

        if (thread->threadName_.isNotEmpty())
            juce_setCurrentThreadName (thread->threadName_);

        if (thread->startSuspensionEvent_.wait (10000))
        {
            if (thread->affinityMask_ != 0)
                setCurrentThreadAffinityMask (thread->affinityMask_);

            thread->run();
        }
    }
    JUCE_CATCH_ALL_ASSERT

    {
        const ScopedLock sl (runningThreadsLock);

        jassert (runningThreads.contains (thread));
        runningThreads.removeValue (thread);
    }

#if JUCE_WIN32
    juce_CloseThreadHandle (thread->threadHandle_);
#endif

    thread->threadHandle_ = 0;
    thread->threadId_ = 0;
}

// used to wrap the incoming call from the platform-specific code
void JUCE_API juce_threadEntryPoint (void* userData)
{
    Thread::threadEntryPoint (static_cast <Thread*> (userData));
}


//==============================================================================
Thread::Thread (const String& threadName)
    : threadName_ (threadName),
      threadHandle_ (0),
      threadPriority_ (5),
      threadId_ (0),
      affinityMask_ (0),
      threadShouldExit_ (false)
{
}

Thread::~Thread()
{
    stopThread (100);
}

//==============================================================================
void Thread::startThread()
{
    const ScopedLock sl (startStopLock);

    threadShouldExit_ = false;

    if (threadHandle_ == 0)
    {
        threadHandle_ = juce_createThread (this);
        juce_setThreadPriority (threadHandle_, threadPriority_);
        startSuspensionEvent_.signal();
    }
}

void Thread::startThread (const int priority)
{
    const ScopedLock sl (startStopLock);

    if (threadHandle_ == 0)
    {
        threadPriority_ = priority;
        startThread();
    }
    else
    {
        setPriority (priority);
    }
}

bool Thread::isThreadRunning() const
{
    return threadHandle_ != 0;
}

//==============================================================================
void Thread::signalThreadShouldExit()
{
    threadShouldExit_ = true;
}

bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
{
    // Doh! So how exactly do you expect this thread to wait for itself to stop??
    jassert (getThreadId() != getCurrentThreadId());

    const int sleepMsPerIteration = 5;
    int count = timeOutMilliseconds / sleepMsPerIteration;

    while (isThreadRunning())
    {
        if (timeOutMilliseconds > 0 && --count < 0)
            return false;

        sleep (sleepMsPerIteration);
    }

    return true;
}

void Thread::stopThread (const int timeOutMilliseconds)
{
    // agh! You can't stop the thread that's calling this method! How on earth
    // would that work??
    jassert (getCurrentThreadId() != getThreadId());

    const ScopedLock sl (startStopLock);

    if (isThreadRunning())
    {
        signalThreadShouldExit();
        notify();

        if (timeOutMilliseconds != 0)
            waitForThreadToExit (timeOutMilliseconds);

        if (isThreadRunning())
        {
            // very bad karma if this point is reached, as
            // there are bound to be locks and events left in
            // silly states when a thread is killed by force..
            jassertfalse
            Logger::writeToLog ("!! killing thread by force !!");

            juce_killThread (threadHandle_);
            threadHandle_ = 0;
            threadId_ = 0;

            const ScopedLock sl2 (runningThreadsLock);
            runningThreads.removeValue (this);
        }
    }
}

//==============================================================================
bool Thread::setPriority (const int priority)
{
    const ScopedLock sl (startStopLock);

    const bool worked = juce_setThreadPriority (threadHandle_, priority);

    if (worked)
        threadPriority_ = priority;

    return worked;
}

bool Thread::setCurrentThreadPriority (const int priority)
{
    return juce_setThreadPriority (0, priority);
}

void Thread::setAffinityMask (const uint32 affinityMask)
{
    affinityMask_ = affinityMask;
}

//==============================================================================
bool Thread::wait (const int timeOutMilliseconds) const
{
    return defaultEvent_.wait (timeOutMilliseconds);
}

void Thread::notify() const
{
    defaultEvent_.signal();
}

//==============================================================================
int Thread::getNumRunningThreads()
{
    return runningThreads.size();
}

Thread* Thread::getCurrentThread()
{
    const ThreadID thisId = getCurrentThreadId();

    const ScopedLock sl (runningThreadsLock);

    for (int i = runningThreads.size(); --i >= 0;)
    {
        Thread* const t = runningThreads.getUnchecked(i);

        if (t->threadId_ == thisId)
            return t;
    }

    return 0;
}

void Thread::stopAllThreads (const int timeOutMilliseconds)
{
    {
        const ScopedLock sl (runningThreadsLock);

        for (int i = runningThreads.size(); --i >= 0;)
            runningThreads.getUnchecked(i)->signalThreadShouldExit();
    }

    for (;;)
    {
        Thread* firstThread;

        {
            const ScopedLock sl (runningThreadsLock);
            firstThread = runningThreads.getFirst();
        }

        if (firstThread == 0)
            break;

        firstThread->stopThread (timeOutMilliseconds);
    }
}

Array<Thread*> Thread::runningThreads;
CriticalSection Thread::runningThreadsLock;


END_JUCE_NAMESPACE
