/*
#  $Id$
# -----------------------------------------------------------------------------
#  The part of 'VideoNEXT MediaClient SDK'
# -----------------------------------------------------------------------------
#  Author: Podlesniy Gleb, 02/07/2020
#  Edited by:
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
*/

#ifndef VN_FILERECORDER_H
#define VN_FILERECORDER_H

#include "ace/Condition_Thread_Mutex.h"
#include "c_ptr.h"

#include <string>
#include <queue>
#include <stdio.h>
#include <stdexcept>
#include <map>
#include "boost/shared_ptr.hpp"
#include <sys/time.h>

#include "../vn_player_internal.h"

struct AVFormatContext;
struct AVCodec;
struct AVPacket;

namespace videonext { namespace media {

class AudioVideoPlaybackController;

class FileRecorder
{
public:
	struct StreamInfo
	{
		const AVCodec* codec;
		int width;
		int height;
		const unsigned char* extraData;
		unsigned extraDataSize;

		StreamInfo();
		StreamInfo(const AVCodec* _codec, int _width, int _height, const unsigned char* _extraData, unsigned _extraDataSize);
	};

	FileRecorder(AudioVideoPlaybackController* playbackController, bool chunked = false);
	~FileRecorder();

	bool start(const char* filename, const std::map<std::string, std::string>& metadata);
	bool start(time_t ts, const std::string& base_storage_dir, const std::map<std::string, std::string>& meta);
	void end(bool stop = false);

	void pushFrame(const StreamInfo& streamInfo, const CPMediaFrame& frame);

	VN_PLAYER_RECORDING_STATUS status();

	bool is_running();
	inline bool is_chunked() const {return chunked_;}
	inline time_t get_chunk_timestamp() const {return chunk_timestamp_;}

private:
    bool popFrame(CPMediaFrame& frame, size_t& queueSize);
	void setStatus(VN_PLAYER_RECORDING_STATUS status, int progress = 0, const char* error = NULL);
#ifdef WIN32
	static DWORD WINAPI recordingThreadFunc(LPVOID arg);
#else
	static void* recordingThreadFunc(void* arg);
#endif
	void recordingThread();
	void initAV();
	void freeAV();
	bool addVideoStream(const CPMediaFrame& frame);
	void addMetaData(const CPMediaFrame& frame);
	void fillAvPkt(uint8_t* frame, int frameSize, AVPacket& pkt);
	uint8_t* findMpeg4ExtraData(uint8_t* frame, int frameSize, int& size);
	uint8_t* findMpeg4Start(uint8_t* frame, int frameSize);
	bool isIndexFrame(uint8_t* frame, int size);
	void writeFrame(const CPMediaFrame& frame);
	void endWrite();
	static int write_i(void* opaque, uint8_t* buf, int buf_size);
	static int64_t seek_i(void* opaque, int64_t offset, int whence);

	AudioVideoPlaybackController* playbackController_;
	std::string filename_;
	std::string filename_tmp_;
	std::map<std::string, std::string> metadata_;
	FILE* file_;
	VN_PLAYER_RECORDING_STATUS status_;
	int progress_;
	time_t chunk_timestamp_;
	std::string error_;
	StreamInfo streamInfo_;
	unsigned char* extraData_;
	unsigned extraDataSize_;
	std::queue<CPMediaFrame> frameQueue_;
	ACE_Thread_Mutex lock_;
	ACE_Condition<ACE_Thread_Mutex> cond_;
#ifdef WIN32
	HANDLE threadHandle_;
#else
	pthread_t thread_;
#endif
	AVFormatContext* formatCtx_;
	uint8_t* buf_;
	bool videoStreamAdded_;
	bool headerSent_;
	bool headerWritten_;
	bool indexFrameFound_;
	bool chunked_;
	int64_t firstVideoPts_;
	int64_t prevVdts_;
	struct timeval rec_time_;
};

}}

#endif
