#ifndef _VN_PLAYER_HH_
#define _VN_PLAYER_HH_

#include <assert.h>
#include <napi.h>
#include "impl/Mutex.h"
#include "vn_player.h"

#ifdef WIN32
#include <windows.h>
#else
class TryMutex : public videonext::media::Mutex
{
public:
    int trylock();
};
#endif

class VN_Player : public Napi::ObjectWrap<VN_Player>
{
public:
    static Napi::Object Init(Napi::Env env, Napi::Object exports);
    VN_Player(const Napi::CallbackInfo& info);
    ~VN_Player();
    inline std::string get_objid() const {return objid_;}
    inline std::string get_short_objid() const {return short_objid_;}

private:
    static Napi::FunctionReference constructor;
    static void cb_js_create_buf(napi_env env, napi_value js_cb, void* context, void* data);
    static void cb_js_new_frame(napi_env env, napi_value js_cb, void* context, void* data);
    static void cb_js_state_changed(napi_env env, napi_value js_cb, void* context, void* data);
    static void cb_js_recording_status_changed(napi_env env, napi_value js_cb, void* context, void* data);
    static void cb_js_msg_log(napi_env env, napi_value js_cb, void* context, void* data);

    void Play(const Napi::CallbackInfo& info);
    void Resume(const Napi::CallbackInfo& info);
    void Pause(const Napi::CallbackInfo& info);
    void Teardown(const Napi::CallbackInfo& info);
    void Jump(const Napi::CallbackInfo& info);
    void Crash(const Napi::CallbackInfo& info);
    void Speed(const Napi::CallbackInfo& info);
    void MuteAudio(const Napi::CallbackInfo& info);
    void SetJitterBufferLen(const Napi::CallbackInfo& info);

    void SetImgBuffer(const Napi::CallbackInfo& info);
    Napi::Value GetInfoAndLockFrameIfReady(Napi::Env env);
    Napi::Value GetInfoAndLockFrameIfReady(const Napi::CallbackInfo& info);
    void FrameUnlock(const Napi::CallbackInfo& info);

    static void* create_image_buffer(int width, int height, float dar, VN_PLAYER_FRAME_BUFFERS_NUM& buf_num, void *client_data);
    static int  lock_image_buffer(int buf_pos, void *client_data);
    static void unlock_image_buffer(int buf_pos, void *client_data);
    static void new_frame(const vn_player_frame_t*, const vn_player_cache_boundaries_t*, int buf_pos, void* client_data);
    static void buffer_changed(const vn_player_cache_boundaries_t*, void* client_data);
    static void state_changed(VN_PLAYER_STREAM_STATE, const vn_player_result_status_t*, void* client_data);
    static void recording_status_changed(const vn_player_recording_status_t* status, void* client_data);
    static void new_stream(const vn_player_stream_info_t*, void* client_data);
    static void stream_removed(const vn_player_stream_info_t*, void* client_data);
    static void msg_log(const char* msg, void* client_data);

    static void FinalizeTSFN(napi_env env, void* data, void* context);

    struct vn_player_config_t   conf_;
    struct vn_player_context_t* ctx_;
    struct timeval*             captured_time_[TRIPLE_BUF];
    struct timeval              last_captured_time_;

#ifdef WIN32
    CRITICAL_SECTION   new_frame_ready_lock_;
    PCRITICAL_SECTION  frame_lock_[TRIPLE_BUF];
    CONDITION_VARIABLE create_buf_lock_;
#else
    videonext::media::Mutex new_frame_ready_lock_;
    TryMutex*               frame_lock_[TRIPLE_BUF];
    videonext::media::Cond  create_buf_lock_;
#endif

    bool vsync_;
    bool muted_;
    bool inited_;          // TODO: change to atomic
    bool new_frame_ready_; // TODO: change to atomic
    int  state_;
    int  buf_num_;
    int  buf_pos_;
    std::string objid_;
    std::string short_objid_;
    std::string objs_data_;

    napi_threadsafe_function tsfn_create_buf_;
    napi_threadsafe_function tsfn_new_frame_;
    napi_threadsafe_function tsfn_state_changed_;
    napi_threadsafe_function tsfn_recording_status_changed_;
    napi_threadsafe_function tsfn_msg_log_;
};

#endif
