#ifndef HV_EVENT_LOOP_THREAD_HPP_
#define HV_EVENT_LOOP_THREAD_HPP_

#include <thread>

#include "hlog.h"

#include "EventLoop.h"

namespace hv {

class EventLoopThread : public Status {
public:
    // Return 0 means OK, other failed.
    typedef std::function<int()> Functor;

    EventLoopThread(EventLoopPtr loop = NULL) {
        setStatus(kInitializing);
        loop_ = loop ? loop : std::make_shared<EventLoop>();
        setStatus(kInitialized);
    }

    ~EventLoopThread() {
        stop();
        join();
    }

    const EventLoopPtr& loop() {
        return loop_;
    }

    hloop_t* hloop() {
        return loop_->loop();
    }

    bool isRunning() {
        return loop_->isRunning();
    }

    // @param wait_thread_started: if ture this method will block until loop_thread started.
    // @param pre: This functor will be executed when loop_thread started.
    // @param post:This Functor will be executed when loop_thread stopped.
    void start(bool wait_thread_started = true,
               Functor pre = Functor(),
               Functor post = Functor()) {
        if (status() >= kStarting && status() < kStopped) return;
        if (isRunning()) return;
        setStatus(kStarting);

        thread_ = std::make_shared<std::thread>(&EventLoopThread::loop_thread, this, pre, post);

        if (wait_thread_started) {
            while (loop_->status() < kRunning) {
                hv_delay(1);
            }
        }
    }

    // @param wait_thread_started: if ture this method will block until loop_thread stopped.
    // stop thread-safe
    void stop(bool wait_thread_stopped = false) {
        if (status() < kStarting || status() >= kStopping) return;
        setStatus(kStopping);

        long loop_tid = loop_->tid();
        loop_->stop();

        if (wait_thread_stopped) {
            if (hv_gettid() == loop_tid) return;
            join();
        }
    }

    // @brief join loop_thread
    // @note  destructor will join loop_thread if you forget to call this method.
    void join() {
        if (thread_ && thread_->joinable()) {
            thread_->join();
            thread_ = NULL;
        }
    }

private:
    void loop_thread(const Functor& pre, const Functor& post) {
        hlogi("EventLoopThread started, tid=%ld", hv_gettid());
        setStatus(kStarted);

        if (pre) {
            loop_->queueInLoop([this, pre]{
                if (pre() != 0) {
                    loop_->stop();
                }
            });
        }

        loop_->run();
        assert(loop_->isStopped());

        if (post) {
            post();
        }

        setStatus(kStopped);
        hlogi("EventLoopThread stopped, tid=%ld", hv_gettid());
    }

private:
    EventLoopPtr                 loop_;
    std::shared_ptr<std::thread> thread_;
};

typedef std::shared_ptr<EventLoopThread> EventLoopThreadPtr;

}

#endif // HV_EVENT_LOOP_THREAD_HPP_
