
#ifndef _MEDIARTMPPROTOCOL_H_
#define _MEDIARTMPPROTOCOL_H_

#include "MFSPublic.h"
// #include "3rd/uvw.hpp"
#include "BaseService.h"
#include "MediaClientProtocol.h"
#include "ZWRedisTool.h"
#include "MediaClientConnection.h"
#include "codec/AMF.h"
#include "Timestamp.h"

namespace mfs
{
#define RTMP_VERSION            0x3
#define RTMP_SET_CHUNK_SIZE     0x1 /* 设置块大小 */
#define RTMP_AOBRT_MESSAGE      0X2 /* 终止消息 */
#define RTMP_ACK                0x3 /* 确认 */
#define RTMP_USER_EVENT         0x4 /* 用户控制消息 */
#define RTMP_ACK_SIZE           0x5 /* 窗口大小确认 */
#define RTMP_BANDWIDTH_SIZE     0x6 /* 设置对端带宽 */
#define RTMP_AUDIO              0x08
#define RTMP_VIDEO              0x09
#define RTMP_FLEX_MESSAGE       0x11 //amf3
#define RTMP_NOTIFY             0x12    // 数据消息
#define RTMP_INVOKE             0x14 //amf0
#define RTMP_FLASH_VIDEO        0x16

#define RTMP_CHUNK_TYPE_0       0 // 11
#define RTMP_CHUNK_TYPE_1       1 // 7
#define RTMP_CHUNK_TYPE_2       2 // 3
#define RTMP_CHUNK_TYPE_3       3 // 0

#define RTMP_CHUNK_CONTROL_ID   2
#define RTMP_CHUNK_INVOKE_ID    3
#define RTMP_CHUNK_AUDIO_ID     4
#define RTMP_CHUNK_VIDEO_ID     5
#define RTMP_CHUNK_DATA_ID      6

#define RTMP_CODEC_ID_H264      7
#define RTMP_CODEC_ID_AAC       10
#define RTMP_CODEC_ID_G711A     7
#define RTMP_CODEC_ID_G711U     8
#define RTMP_CODEC_ID_ADPCM     1

#define RTMP_AVC_SEQUENCE_HEADER  0x18
#define RTMP_AAC_SEQUENCE_HEADER  0x19


	enum RTMP_CHUNK_STATE
	{
		PARSE_HEADER,
		PARSE_BODY,
	};
    enum RTMP_HANDSHAE_STATE
    {
        HANDSHAKE_WAIT_C0C1,
        HANDSHAKE_WAIT_S0S1S2,
        HANDSHAKE_WAIT_C2,
    };
	enum RTMP_CONNECTION_STATE
	{
		HANDSHAKE,              // 握手
        HANDSHAKE_COMPLETED,    // 握手完成
		START_CONNECT,          // 开始连接
		START_CREATE_STREAM,    // 创建流
		START_DELETE_STREAM,    // 删除流
		START_PLAY,             // 播放
		START_PUBLISH,          // 推流
	};

    // 
    struct RtmpBuffer
    {
        uint8_t* data;
        int size;
        RtmpBuffer(uint8_t* p, int s):data(p),size(s){};
        RtmpBuffer(char* p, int s):data((uint8_t*)p),size(s){};
    };

    // 透传数据
    // chunk header: basic_header + rtmp_message_header 
    struct RtmpMessageHeader
    {
        uint8_t timestamp[3];
        uint8_t length[3];
        uint8_t type_id;
        uint8_t stream_id[4]; /* 小端格式 */
    } __attribute__ ((__packed__));

    struct RtmpMessage
    {
        uint32_t timestamp = 0;
        int32_t length = 0;
        uint8_t  type_id = 0;
        uint32_t stream_id = 0;
        uint32_t extend_timestamp = 0;

        uint64_t _timestamp = 0;
        uint8_t  codecId = 0;

        uint8_t  csid = 0;
        uint32_t index = 0;
        std::vector<char> payloadRecv;          // 用以接收数据时存储
        std::vector<RtmpBuffer> payloadVec;     // 用以发送时传递内存数据，本身不负责释放内存

        void Clear()
        {
            index = 0;
            timestamp = 0;
            extend_timestamp = 0;
            if (length > 0) {
                payloadRecv.clear();
                payloadVec.clear();
            }
        }

        bool IsCompleted() const 
        {
            if (index == length && length > 0 &&(payloadVec.size()>0 || payloadRecv.size()>0)) {
                return true;
            }

            return false;
        }
    };


struct MediaInfo
{
	uint8_t  video_codec_id = RTMP_CODEC_ID_H264;
	uint8_t  video_framerate = 0;
	uint32_t video_width = 0;
	uint32_t video_height = 0;
	std::vector<uint8_t> sps;
	std::vector<uint8_t> pps;
	std::vector<uint8_t> sei;
	uint32_t sps_size = 0;
	uint32_t pps_size = 0;
	uint32_t sei_size = 0;

	uint8_t  audio_codec_id = RTMP_CODEC_ID_AAC;
	uint32_t audio_channel = 0;
	uint32_t audio_samplerate = 0;
	uint32_t audio_frame_len = 0;
};

class MediaRtmpProtocol: public ZWRedisTool::ITaskClient , public MediaClientConnection
{
    public:
    // ITaskClient
    void RedisCommandCallback(const std::string& key, redisReply* reply);

    void closeConnection(const char* msg){_streamInfo.message=msg; this->Close();};
    int writeData(char* data, int dataLen);//{return this->Write(data,dataLen);};
     
    public:
    MediaRtmpProtocol(std::shared_ptr<uvw::Loop> loop, std::shared_ptr<uvw::TCPHandle> client, const std::string& name)
    :MediaClientConnection(loop, client, name)
    {
        _streamInfo.targetType = "2";

        max_chunk_size_ = 30000;
        _timeoutMilli = 30*1000;    // 30秒超时
    }

    // virtual int Write(char* data, int dataLen) override;
    virtual void OnData(char* data, int dataLen) override;
    virtual void OnAlloc(uv_buf_t *buf, int suggested) override; 
    virtual void OnClose() override;
    virtual void OnReady() override;

    virtual bool onlyRaw() override{return true;}

    void OnReadyWebSocket();
    

    BaseProtocol* getBaseProtocol(){return this;}
    virtual void sendMessage(const char* data, int dataLen){}
    virtual void sendMessage(int code, const std::string& message){};
    virtual void sendMessage(int code){};

    private:
#if CONVERT_AAC
        std::shared_ptr<adpcm_state> _adpcmState;
        std::shared_ptr<Pcm2AAC> _pcm2aac;
    std::ofstream  testfilestream;
#endif

    std::pair<std::vector<uint8_t>, int> _readParam;

    std::function<void(MediaClientConnection*,BaseProtocol*)> _onRelease;
    int _audioSampleRate;
    

    RTMP_CONNECTION_STATE _connectionState = HANDSHAKE;
    RTMP_CHUNK_STATE _chunkState = PARSE_HEADER;
    RTMP_HANDSHAE_STATE _handshakeState = HANDSHAKE_WAIT_C0C1;

    int _rtmpVersion = 3;                                                       // S0
    std::vector<uint8_t> _rtmpDataS1;                                           // S1 1536

    int _rtmpVersionClient = -1;                                                // C0
    std::vector<uint8_t> _rtmpDataC1;                                           // C1 1536

    int _packageNumber = 0;
    int _wirtedData=0;

    int _logReadFlag = 0;
    int _logWriteFlag =0;

    char _mask[4] = {0};
    int _isRecordAudio = 0;                                                     // 是否是录音流
    

    BaseProtocol* _service = nullptr;                                         // Rtp808

    // handshake
    int recvHandShakeData();
    int recvHandShakeData2();
    int sendHandShakeData();
    int handshake();

    // message
    int _chunk_stream_id;
	std::map<int, RtmpMessage> _rtmp_messages;
	uint32_t _in_chunk_size = 128;
	uint32_t _out_chunk_size = 128;
	AmfObjects meta_data_;
	AmfDecoder amf_decoder_;
	AmfEncoder amf_encoder_;
	uint32_t stream_id_ = 0;
	std::string app_;
	std::string stream_name_;
	std::string stream_path_;
	std::string status_;
    bool has_key_frame_ = false;
	Timestamp timestamp_;

    int _default_stream_id = 1;
    
	uint32_t peer_bandwidth_ = 5000000;
	uint32_t acknowledgement_size_ = 5000000;
	uint32_t max_chunk_size_ = 128;

	bool has_sps_pps = false;
    bool first_audio = true;
	MediaInfo media_info_v;
	MediaInfo media_info_a;
	std::vector<char> avc_sequence_header_;
	std::vector<char> aac_sequence_header_;
	uint32_t avc_sequence_header_size_ = 0;
	uint32_t aac_sequence_header_size_ = 0;
	uint8_t audio_tag_ = 0;
	const uint32_t kSamplingFrequency[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0};

    std::function<void(MediaClientConnection*,BaseProtocol*,char*,int)> _onDataCallback;
    std::function<void(MediaClientConnection*,BaseProtocol*,char*,int)> _onAudioDataCallback;
    std::function<void(MediaClientConnection*,BaseProtocol*,const ControlSetting& setting)> _onControlCallback;
private:
    // chunk
    int getMessage(RtmpMessage& out_rtmp_msg);
    int parseChunkHead();
    int parseChunkBody();
	int createBasicHeader(uint8_t fmt, uint32_t csid, char* buf);
	int createMessageHeader(uint8_t fmt, RtmpMessage& rtmp_msg, char* buf);
	int createChunk(uint32_t csid, RtmpMessage& rtmp_msg, char* buf, uint32_t buf_size, std::vector<RtmpBuffer>& vecData);
    


    int GetAACSampleRateIndex(int sampleRate);
    void setPeerBandwidth();
    void sendAcknowledgement();
    void setChunkSize();
    
    bool sendVideoData(uint64_t timestamp, const RtmpBuffer& payload);
    bool sendVideoData(uint64_t timestamp, const std::vector<RtmpBuffer>& payload, uint32_t payload_size);
    bool sendAudioData(uint64_t timestamp, const RtmpBuffer& payload);
    bool sendAudioData(uint64_t timestamp, const std::vector<RtmpBuffer>& payload, uint32_t payload_size);
    int pushVideoFrame(const RtmpBuffer& data);
    int pushVideoFrame(const std::vector<RtmpBuffer>& data, int dataSize);
    int pushAudioFrame(const RtmpBuffer& data);
    int pushAudioFrame(const std::vector<RtmpBuffer>& data, int dataSize);
    int setMediaInfoV(const MediaInfo& media_info);
    int setMediaInfoA(const MediaInfo& media_info);


    bool sendInvokeMessage(uint32_t csid, const std::vector<RtmpBuffer>& payload, uint32_t payload_size);
    bool sendNotifyMessage(uint32_t csid, const std::vector<RtmpBuffer>& payload, uint32_t payload_size);   
    bool sendInvokeMessage(uint32_t csid, const RtmpBuffer& payload);
    bool sendNotifyMessage(uint32_t csid, const RtmpBuffer& payload);   
    int sendRtmpChunks(uint32_t csid, RtmpMessage& rtmp_msg);

    // message
    bool handleMessage(RtmpMessage& rtmp_msg);
    bool handleInvoke(RtmpMessage& rtmp_msg);
    bool handleNotify(RtmpMessage& rtmp_msg);
    bool handleVideo(RtmpMessage& rtmp_msg);
    bool handleAudio(RtmpMessage& rtmp_msg);

    bool handleConnect();
    bool handleCreateStream();
    bool handlePublish();
    bool handlePlay();
    bool handlePlay2();
    bool handleDeleteStream();
	bool handleResult(RtmpMessage& rtmp_msg);
	bool handleOnStatus(RtmpMessage& rtmp_msg);

    
    virtual void alogd(const std::string& message, const std::string& fileName, const int line, const std::string& functionName) override {
        BaseProtocol::alogd(std::string()+"["+_url+"]"+message, fileName, line, functionName);
    }
    virtual void aloge(const std::string& message, const std::string& fileName, const int line, const std::string& functionName)  override {
        BaseProtocol::aloge(std::string()+"["+_url+"]"+message, fileName, line, functionName);
    }
    virtual void alogv(const std::string& message, const std::string& fileName, const int line, const std::string& functionName)  override {
        BaseProtocol::alogv(std::string()+"["+_url+"]"+message, fileName, line, functionName);
    }
};

}




#endif // _MEDIACLIENTPROTOCOL_H_