/*
 * (C) Copyright 1996- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include <signal.h>

#include "eckit/thread/AutoLock.h"
#include "eckit/runtime/Main.h"
#include "eckit/log/Log.h"
#include "eckit/memory/MemoryPool.h"
#include "eckit/runtime/Monitor.h"
#include "eckit/thread/Thread.h"
#include "eckit/thread/ThreadControler.h"

//-----------------------------------------------------------------------------

namespace eckit {

//-----------------------------------------------------------------------------


ThreadControler::ThreadControler(Thread* proc,bool detached,size_t stack):
    detached_(detached),
    thread_(0),
    proc_(proc),
    stack_(stack),
    running_(false)
{
    //cout << "ThreadControler::ThreadControler(" << this << ")" << " " << hex << pthread_self() << std::endl;
}

ThreadControler::~ThreadControler()
{
    //cout << "ThreadControler::~ThreadControler(" << this << ")" << " " <<hex <<  pthread_self() << std::endl;
    AutoLock<MutexCond> lock(cond_);

    if(running_)
    {
        // The Thread will delete itself
        // so there is no need for:
        // delete proc_;
    }
    else
    {
        //Log::warning() << "Deleting Thread in ThreadControler::~ThreadControler()" << " " << hex << pthread_self() << std::endl;
        delete proc_;
        proc_ = 0;
    }
}

//------------------------------------------------------

void ThreadControler::execute()
{
    // Make a copy, because "this" will desappear
    Thread* proc = proc_;
    if (detached_)
        proc_ = 0;

    //cout << "ThreadControler::execute(" << this << ")" <<  " " << hex << pthread_self() << std::endl;
    //=================
    // Make sure the logs are created...

    Monitor::instance().startup();
    Monitor::instance().parent(Main::instance().taskID());

    //============


    { // Signal that we are running

        AutoLock<MutexCond> lock(cond_);
        running_ = true;
        cond_.signal();

        // NOTE: "this" is not valid any more after this point

    }

    //=============

    // We don't want to receive reconfigure events

    sigset_t set,old_set;

    sigemptyset(&set);

    sigaddset(&set, SIGHUP);
    sigaddset(&set, SIGCHLD);
    sigaddset(&set, SIGPIPE);

    THRCALL(::pthread_sigmask(SIG_BLOCK, &set, &old_set));

    //=============

    ASSERT(proc);

    try {
        proc->run();
    }
    catch(std::exception& e) {
        Log::error() << "** " << e.what() << " Caught in "   << Here() << std::endl;
        Log::error() << "** Exception terminates thread " << pthread_self() << std::endl;
    }

    if(proc->autodel_)
    {
        delete proc;
    }
}

void* ThreadControler::startThread (void* data)
{
    //cout << "ThreadControler::startThread(" << data << ")" << " " << hex << pthread_self() << std::endl;
    reinterpret_cast<ThreadControler*>(data)->execute(); // static_cast or dynamic_cast ??
    return 0;
}

void ThreadControler::start()
{
    //cout << "ThreadControler::start(" << this << ")" << " " << hex << pthread_self() << std::endl;
    ASSERT(thread_ == 0);

    pthread_attr_t attr;
    pthread_attr_init(&attr);

    if(stack_)
    {
        THRCALL(::pthread_attr_setstacksize(&attr, stack_));
    }
    if(detached_)
        THRCALL(::pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
    else
        THRCALL(::pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));

    AutoLock<MutexCond> lock(cond_);

    THRCALL(::pthread_create(&thread_,&attr,startThread,this));

    pthread_attr_destroy(&attr);

    while(!running_)
        cond_.wait();
}

void ThreadControler::kill()
{
    pthread_cancel(thread_);
    //pthread_kill(thread_,sig);
}

void ThreadControler::stop()
{
    // Due to legacy code, this stop routine may be called on detached threads. Don't
    // stress about it!
    if (!detached_ && proc_ != NULL) {
        proc_->stop();
    }
}

void ThreadControler::wait()
{
    ASSERT(!detached_);
    // if(running_)
    THRCALL(::pthread_join(thread_,0));
}

bool ThreadControler::active()
{
    if(thread_ != 0)
    {
        // Try see if it exists

        int policy;
        sched_param param;

        int n = pthread_getschedparam(thread_, &policy, &param);

        // The thread does not exist
        if(n != 0)
            thread_ = 0;

    }
    return thread_ != 0;
}

//-----------------------------------------------------------------------------

} // namespace eckit

