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

#ifndef VN_FILE_MEDIA_STREAM_PRODUCER_IMPL_H_
#define VN_FILE_MEDIA_STREAM_PRODUCER_IMPL_H_

#include "ace/Condition_Thread_Mutex.h"

#include <stdexcept>
#include <string>
#include <map>

#include "VideoFrameConsumer.h"
#include "MediaStreamProducerImpl.h"
#include "AdaptiveUsageEnvironment.h"

namespace videonext { namespace media {

class AudioVideoPlaybackController;

class StreamError : public std::runtime_error
{
public:
	StreamError(int code, const char* message);
	int code() const { return code_; }

private:
	int code_;
};

class FileMediaStreamProducerImpl : public MediaStreamProducerImpl
{
public:
	FileMediaStreamProducerImpl(MediaStreamHandler* mediaStreamHandler, const std::string& url, unsigned cacheSize,
	                            unsigned streamId = 1, float speed = 1.f);

	virtual ~FileMediaStreamProducerImpl();

public:
	// Redefined virtual functions
	virtual void open();

	virtual void play();

	virtual void pause();

	virtual void setStepMode(bool value);

	virtual void setSpeed(float speed);

	virtual void setJitterBufferLen(unsigned value) {}

	virtual void setPlayDirection(int value);

	virtual void changeState(STREAM_STATE, int errorCode = 0);

	virtual void moveToTimestamp(time_t time) { jumpToTimestamp(time); }

	virtual void teardown();

	virtual void jumpToTimestamp(int value, bool mark = true);

	virtual bool isPlayingArchive() { return true; }

	virtual bool isStepMode() { return stepMode_ == 1; }

	virtual bool startRecording(const char* file_name, const std::map<std::string, std::string>& metadata);

	virtual bool endRecording(bool stop = false);

protected:
	virtual void stop();

private:
	static void playbackTask(void* clientData);
	void playFrame();

	static void presentTask(void* clientData);
	void presentFrame();

	void resume();

	void close();
	void closeSink();
	void getFrameTimestamp(const AVPacket& pkt, struct timeval& t);
	uint64_t getClipDuration(AVStream* stream);
	void parseMetadata();
	void parseMetadata(const std::string& metaText, const std::map<std::string, std::string>& map);
	void parseMetaObj(const std::string& metaText, const std::string& prefix, const std::string& idKey, const std::string& nameKey);
	time_t getClipStartTime();

	static uint8_t* getH264ExtraData(uint8_t* extraData, int extraDataSize, int& outSize);
	static uint8_t* getH265ExtraData(uint8_t* extraData, int extraDataSize, int& outSize);
	static uint8_t* findH264StartCode(uint8_t* data, int dataSize);
	static uint8_t* findH264Frame(uint8_t* data, int dataSize);
	static uint8_t* findH265Frame(uint8_t* data, int dataSize);
	static uint8_t* findMpeg4ExtraData(uint8_t* extraData, int extraDataSize, int& outSize);

private:
	AdaptiveUsageEnvironment* env_;
	TaskScheduler* scheduler_;

	char stopFlag_;
	bool stepMode_;
	unsigned frameSinkBufferSize_;

	AudioVideoPlaybackController* playbackController_;
	VideoFrameConsumer* sink_;

	std::string fileName_;
	AVFormatContext* formatCtx_;
	AVCodecID codecId_;

	int playDirection_;
	TaskToken playbackTask_;
	TaskToken presentTask_;

	ACE_Thread_Mutex playMutex_;

	time_t clipStartTime_;

	bool skipNextFrame_;
	uint64_t lastFramePTS_;

	uint8_t* extradata_;
	int extradataSize_;

	AVRational timeBase_;
	AVPacket pkt_;
	struct timeval frameTv_;
	struct timeval lastTv_;
	std::map<std::string, std::string> metadata_;
	bool firstFrame_;
	bool firstChunk_;
	std::string objid_;
	std::string base_dir_;
	time_t jumpToTimestamp_;
};

} // namespace media
} // namespace videonext

#endif
