#ifndef VN_PLAYER_H
#define VN_PLAYER_H

#include "vn_os.h"
#include "vn_kv_list.h"
#include <time.h>

#ifdef __cplusplus
extern "C"
{
#endif

struct vn_player_context_t; /* opaque */

enum VN_PLAYER_STREAM_STATE  {
    IDLE,             /* initial state, or timeout occurred if PLAY_* status was previous state */
    PLAY_FROM_SERVER, /* playing stream from server */
    PLAY_FROM_BUFFER, /* playing stream from cache */
    STOPPED,          /* player stopped */
    OPENING,          /* opening stream */
    BUFFERING,        /* buffering stream */
    OPENED            /* stream opened */
};

enum VN_PLAYER_STREAM_TYPE {
    AUDIO,
    VIDEO,
    DATA,
    UNKNOWN
};

enum VN_PLAYER_PIXEL_FORMAT {
    PIX_NONE, /* do not do any pixel conversion */
    PIX_YUV420P,
    PIX_RGB24,
    PIX_RGB32,
    PIX_BGR32
};

enum VN_PLAYER_RTP_TRANSPORT {
    RTP_TRANSPORT_TCP,
    RTP_TRANSPORT_UDP
};

enum VN_PLAYER_DECODER_TYPE {
    DECODER_NONE,
    DECODER_HW,
    DECODER_SW_MT,
    DECODER_SW
};

enum VN_PLAYER_RECORDING_STATUS
{
    RECORDING_IDLE,
    RECORDING_STARTED,
    RECORDING_ENDING,
    RECORDING_ENDED,
    RECORDING_ERROR,
    RECORDING_SKIPPED
};

enum VN_PLAYER_FRAME_BUFFERS_NUM {
    SINGLE_BUF = 1,
    DOUBLE_BUF,
    TRIPLE_BUF
};

#pragma pack(push, 1) /* store previous pack value */
                      /* and set new value to 1 (no alignment) */
struct vn_player_config_t
{
   VN_PLAYER_RTP_TRANSPORT rtp_transport;

   /**
    * By default engine does not do any decoding and sends just parsed raw frames.
    * You can enable decoding facility here.
    * Note: if hardware decoder is not available engine will fallback to software implementation.
    */
   VN_PLAYER_DECODER_TYPE decoder_type;

   /**
    * Preferred pixel format for decoded video frames.
    * Make sense if decoding is enabled
    */
   VN_PLAYER_PIXEL_FORMAT pixel_format;

   /**
    * Cache size in MB.
    */
   unsigned cache_size;


   /**
    * Jitter buffer length in milliseconds
   */
   unsigned buffer_len;

   /*
    * Provided by user opaque pointer
    */
   void *client_data;
};

struct vn_player_stream_info_t
{
   VN_PLAYER_STREAM_TYPE type;

   unsigned stream_id;

   unsigned duration; // in milliseconds. 0 if unknown

   const char *RTP_Codec_Str; /// see [RFC3551]

   // make sense for audio streams only
   unsigned sampling_freq;
   unsigned bits_per_sample;
   unsigned num_channels;

   /**
    * Optional config data
    */
   unsigned char *config_data;
   unsigned config_data_size;


   /**
    * Optional metadata (camera info)
    */
   vn_kv_t **metadata;
};

struct vn_player_cache_boundaries_t
{
   struct timeval begin;
   struct timeval end;
};

struct vn_player_frame_t
{
   /**
    * Actually tells from what stream we have frame.
    * See: "new_stream()" callback
    */
   vn_player_stream_info_t *stream_info;

   /**
    * Pointer to picture planes (if video frame) or to audio samples.
    * Audio samples and "single" plain video frames (RGB) stored in data[0]
    */
   unsigned char *data[4];

   /* Number of bytes in one row or the decoded video frame
      OR size of data[N] for audio or encoded video frames*/
   unsigned data_size[4];

   /**
    * Frame width, 0 - if unknown
    */
   unsigned width;

   /**
    * Frame height, 0 - if unknown
    */
   unsigned height;

   /**
    * Display aspect ratio
    */
   float dar;

   /**
    * Time when frame was recorder from DVR.
    * Can be equal to PTS when receiving "live" media, and different in "archive" mode.
    */
   struct timeval *captured_time;

   /**
    * Objects tracking data in binary format
    */
   const unsigned char *objects_data;

   /**
    * Size of objects tracking data
    */
   unsigned objects_data_size;
};

/* Specific error codes */
#define ERTCPTIMEOUT          10000
#define EENDOFSTREAM          10001
#define EMEDIAFORMATCHANGED   10002
#define EUNKNOWNSTREAMTYPE    10003
#define EENDOFCHUNK           10004
#define EENDOFARCHIVE         10005
#define EINTERNALERROR        10100

struct vn_player_result_status_t
{
   int error_code;
   const char *error_str;
};

struct vn_player_recording_status_t
{
	VN_PLAYER_RECORDING_STATUS status;
	int progress;
    time_t timestamp;
    const char* filename;
	const char* error_str;
};

struct vn_player_callbacks_t
{
    /**
     * Invokes for every new frame. Video and audio frames may be decoded here.
     * Tracked objects may be included in the frame.
     * If vn_player_config_t.pixel_format == PIX_NONE data[0,1,2] will not be initialized, data[4] will contain pointer to internal video frame
     */
    void (*new_frame)(const vn_player_frame_t*, const vn_player_cache_boundaries_t*, int buf_num, void *client_data);

    /**
     * Invokes when buffer's (cache's) boundaries have been changed
     */
    void (*buffer_changed)(const vn_player_cache_boundaries_t*, void *client_data);

    /**
     * Notifies client when player changes its state
     */
    void (*state_changed)(VN_PLAYER_STREAM_STATE, const vn_player_result_status_t*, void *client_data);

    /**
     * New RTP stream was added
     * @param stream_info useful to obtain additional stream params
     */
    void (*new_stream)(const vn_player_stream_info_t *stream_info, void *client_data);

    /**
     * Stream was removed. (RTCP BYE was received, etc..)
     */
    void (*stream_removed)(const vn_player_stream_info_t*, void *client_data);

    /**
     * Create buffer to render image. Optional. If NULL is returned - buffer will be created by framework.
     * Buffer should be freed only after player destroyed. I.e. after vn_player_destroy() call
     *
     * @param dar Display aspect ratio
     * @param buf_num We can support not only a single buffer, but also a double, triple buffer...
     */
    void* (*create_image_buffer)(int width, int height, float dar, VN_PLAYER_FRAME_BUFFERS_NUM& buf_num, void *client_data);

    /**
     * Lock image buffer
     *
     * @param buf_num The starting buffer number for the locking attempt
     * @return successfully locked buffer number
     */
    int (*lock_image_buffer)(int buf_num, void *client_data);

    /**
     * Unlock image buffer
     *
     * @param buf_num Buffer number to unlock
     */
    void (*unlock_image_buffer)(int buf_num, void *client_data);

    /**
     * Local file recording status callback
     */
    void (*recording_status_changed)(const vn_player_recording_status_t*, void *client_data);

   /**
    * Optional log callback
   */
   void (*msg_log)(const char*, void *client_data);
};

#pragma pack(pop)     /* restore previous pack value */

VN_SDK_API vn_player_context_t *vn_player_create(vn_player_config_t*);

VN_SDK_API void vn_player_set_callbacks(vn_player_context_t*, const vn_player_callbacks_t*);

/**
 * Asynchronous opening stream
 * Returns immediately.
 * See statuses OPENING/OPENED
 *
 * @return true if success
 */
VN_SDK_API bool vn_player_open(vn_player_context_t*, const char* url, float speed = 1.f);

/**
 * Trying to play opened stream. I.e. only after "OPENED" status
 * Returns immediately.
 * Player's statuses "PLAY_FROM_SERVER/PLAY_FROM_BUFFER" will inform about success playing.
 * @return true if success
 */
VN_SDK_API void vn_player_play(vn_player_context_t*);

/**
 * Pause stream. Live stream is not really paused. Just stopping invoke callback "new_frame()",
 * but cache will be filled as usually.
 */
VN_SDK_API void vn_player_pause(vn_player_context_t*);

/**
 * Mute/unmute audio
 */
VN_SDK_API void vn_player_mute_audio(vn_player_context_t*, int mute);

/**
 * Resume playback
 *
 * @param direction Play direction (-1: backward, 1: forward)
 * @return true if success
 */
VN_SDK_API bool vn_player_resume(vn_player_context_t*, int direction);

/**
 * Enable/disable step mode. Works on both server and buffer streams.
 * When step mode enabled, play() will invoke "new_frame()" callback once
 * and will set status IDLE.
 * pause() also is necessary before setting step mode.
*/
VN_SDK_API void vn_player_set_step_mode(vn_player_context_t*, unsigned);

VN_SDK_API void vn_player_set_playback_speed(vn_player_context_t*, float);

/**
 * Set jitter buffer length
 *
 * @param value in milliseconds
 */
VN_SDK_API void vn_player_set_jitter_buffer_len(vn_player_context_t*, unsigned value);

/**
 * If possible jump to another time in archive
 *
 * @param time unix epoch time stamp
 */
VN_SDK_API void vn_player_move_to_ts(vn_player_context_t*, time_t time);

/**
 * Starts recording of frames to a local file.
 *
 * @return true if success
 */
VN_SDK_API bool vn_player_start_recording(vn_player_context_t*, const char* file_name, vn_kv_t** metadata);

/**
 * Ends recording of frames to a local file.
 *
 * @return true if success
 */
VN_SDK_API bool vn_player_end_recording(vn_player_context_t*);

/**
 * Stop playback and destroy all allocated resources
 */
VN_SDK_API void vn_player_destroy(vn_player_context_t*);

VN_SDK_API const char *vn_player_state_to_str(int state);

#ifdef __cplusplus
}
#endif

#endif /*VN_PLAYER_H*/
