// Copyright (C) 2007  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_THREADED_OBJECT_EXTENSIOn_CPP
#define DLIB_THREADED_OBJECT_EXTENSIOn_CPP

#include "threaded_object_extension.h"
#include "create_new_thread_extension.h"

namespace dlib
{

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

    threaded_object::
    threaded_object (
    ):
        s(m_),
        id1(0),
        is_running_(false),
        is_alive_(false),
        should_stop_(false),
        id_valid(false)
    {
    }

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

    threaded_object::
    ~threaded_object (
    )
    {
        try
        {
            DLIB_ASSERT(is_alive() == false,
                   "\tthreaded_object::~threaded_object()"
                   << "\n\tYou have let a threaded object destruct itself before terminating its thread"
                   << "\n\tthis: " << this
            );
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
            assert(false);
            abort();
        }
    }

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

    bool threaded_object::
    is_running (
    ) const 
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tbool threaded_object::is_running()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        return is_running_;
    }

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

    bool threaded_object::
    is_alive (
    ) const
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tbool threaded_object::is_alive()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        return is_alive_;
    }

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

    void threaded_object::
    wait (
    ) const
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tvoid threaded_object::wait()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        while (is_alive_)
            s.wait();
    }

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

    void threaded_object::
    start (
    )
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tvoid threaded_object::start()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        if (is_alive_ == false)
        {
            if (create_new_thread<threaded_object,&threaded_object::thread_helper>(*this) == false)
            {
                is_running_ = false;
                throw thread_error();
            }
        }
        is_alive_ = true;
        is_running_ = true;
        should_stop_ = false;
        s.broadcast();
    }

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

    void threaded_object::
    restart (
    )
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tvoid threaded_object::restart()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        if (is_alive_ == false)
        {
            if (create_new_thread<threaded_object,&threaded_object::thread_helper>(*this) == false)
            {
                is_running_ = false;
                throw thread_error();
            }
            should_respawn_ = false;
        }
        else
        {
            should_respawn_ = true;
        }
        is_alive_ = true;
        is_running_ = true;
        should_stop_ = false;
        s.broadcast();
    }

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

    void threaded_object::
    set_respawn (
    )
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tvoid threaded_object::set_respawn()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        should_respawn_ = true;
    }

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

    bool threaded_object::
    should_respawn (
    ) const
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tbool threaded_object::should_respawn()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        return should_respawn_;
    }

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

    void threaded_object::
    pause (
    )
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tvoid threaded_object::pause()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        is_running_ = false;
    }

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

    void threaded_object::
    stop (
    )
    {
        auto_mutex M(m_);

        DLIB_ASSERT(id1 != get_thread_id() || id_valid == false,
               "\tvoid threaded_object::stop()"
               << "\n\tYou can NOT call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );

        should_stop_ = true;
        is_running_ = false;
        should_respawn_ = false;
        s.broadcast();
    }

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

    bool threaded_object::
    should_stop (
    ) const
    {
        auto_mutex M(m_);
        DLIB_ASSERT(is_alive_ && id1 == get_thread_id() && id_valid == true,
               "\tbool threaded_object::should_stop()"
               << "\n\tYou can only call this function from the thread that executes threaded_object::thread"
               << "\n\tthis: " << this
        );
        while (is_running_ == false && should_stop_ == false)
            s.wait();
        return should_stop_;
    }

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

    void threaded_object::
    thread_helper(
    )
    {
#ifdef ENABLE_ASSERTS
        id1 = get_thread_id();
        id_valid = true;
#endif
        while (true)
        {
            m_.lock();
            should_respawn_ = false;
            m_.unlock();

            thread();

            auto_mutex M(m_);

            if (should_respawn_)
                continue;

#ifdef ENABLE_ASSERTS
            id_valid = false;
#endif

            is_alive_ = false;
            is_running_ = false;
            should_stop_ = false;
            s.broadcast();

            return;
        }
    }

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

}

#endif // DLIB_THREADED_OBJECT_EXTENSIOn_CPP