
#include "MediaF3Protocol.h"
#include "MediaWebSocketService.h"
#include "MediaClientService.h"
#include "Rtp808Service.h"
#include "ClientManager.h"

namespace mfs
{
    static int _messageNo = 0;
    static std::shared_ptr<MediaF3Protocol> _instance = nullptr;
    static std::string _clientID;

    void MediaF3Protocol::SetupOnce()
    {
        if (_instance)
        {
            return;
        }

        LOG_INFO <<"Connect F3: "<< AppSetting::Instance()->f3Ip << ":"<< AppSetting::Instance()->f3Port <<LOG_ENDL;

        auto client = ClientManager::Instance()->ConnectServer(AppSetting::Instance()->f3Ip, AppSetting::Instance()->f3Port, "f3",[](std::shared_ptr<uvw::Loop> loop, std::shared_ptr<uvw::TCPHandle> client,const std::string& name){
            return new MediaF3Protocol(loop, client, name);
        });
        _instance = std::static_pointer_cast<MediaF3Protocol>(client);
    }
    std::shared_ptr<MediaF3Protocol> MediaF3Protocol::Instance()
    {
        return _instance;
    }
    int MediaF3Protocol::GetMessageNum()
    {
        _messageNo++;
        return _messageNo;
    }
    void MediaF3Protocol::SendMessageInner(const MessageInfo& info)
    {
        // if (!MediaF3Protocol::Instance())
        // {
        //     LOG_INFO << "Connect f3 server!" << LOG_ENDL;
        //     MediaF3Protocol::SetupOnce();
        // }
        
        if (_connected)
        {
            uint32_t size = info.messgae.size();
            uint32_t dataSize = swapBigEndian(size);
            iovec datas[2] = {{&dataSize, 4}, {const_cast<char*>(info.messgae.data()), static_cast<size_t>(size)}};
            BaseProtocol::WriteV(datas, 2, true);
            logd("SendMessage("+std::to_string(info.number)+"):"+info.messgae);
            if (info.number>=0)
            {
                _messagesSent.push_back(info);
            }
        }
        else
        {
            // LOG_INFO << "Store message("<<info.number<<"):"<<info.messgae << LOG_ENDL;
            if (info.number>=0)
            {
                _messagesSending.push_back(info);
            }
        }
    }

    void MediaF3Protocol::OnConnection()
    {
        // logd("OnConnection");
        do
        {
            auto result = _client.lock();
            if (!result)
            {
                loge("TCPHandle is nullptr");
                break;
            }
            if (!result->readable())
            {
                loge("TCPHandle error readable");
                break;
            }
            if (!result->writable())
            {
                loge("TCPHandle error writable");
                break;
            }
            logd("Connected!");
            _connected = true; 
            MediaF3Protocol::Heartbeat();
            for (auto iter = _messagesSending.begin(); iter != _messagesSending.end(); iter++)
            {
                SendMessageInner(*iter);
            }
            _messagesSending.clear();

            return;
        } while (false);
        
        this->Close();
    }
    void MediaF3Protocol::OnData(char* data, int dataLen) 
    {
        // logv(data);
        _readParam.second += dataLen;
        while(true)
        {
            auto parseResult = parseCLBSCommand();
            if(parseResult != 0)
            {
                // logd("parseResult="+std::to_string(parseResult));
                break;
            }
        }
    };
    
    void MediaF3Protocol::OnClose()
    {
        _connected = false;
        _instance = nullptr;
    }
    void MediaF3Protocol::OnAlloc(uv_buf_t *buf, int suggested)
    {
        int freeSize = _readParam.first.size() - _readParam.second;
        if(freeSize < READ_BUFFER_STEP)
        {
            int newSize = _readParam.first.size() + READ_BUFFER_STEP;
            if (newSize >  AppSetting::Instance()->readbufferMax)
            {
                std::stringstream ss;
                ss << "Read buffer full:"<<errno;
                loge(ss.str());
                _streamInfo.message = "Buffer full(r)!";
                this->Close();
                return;
            }
            _readParam.first.resize(newSize);
        }
        buf->base = (char*)_readParam.first.data() + _readParam.second;
        buf->len = _readParam.first.size() - _readParam.second;
    } 
    int MediaF3Protocol::parseCLBSCommand()
    {
        // 检测缓存数据是否足够
        int sizeOffset = sizeof(uint32_t);
        if (_readParam.second < sizeOffset)
        {
            return -1;
        }
        uint32_t dataSize = *((uint32_t*)_readParam.first.data());
        dataSize = swapBigEndian(dataSize);
        auto oneCommandSize = sizeOffset + dataSize;
        if (_readParam.second < oneCommandSize)
        {
            return -2;
        }

        // 处理一条指令
        std::vector<char> strCommand(dataSize+1, 0);
        memcpy(strCommand.data(), _readParam.first.data()+sizeOffset, dataSize);
        int iret = executeCLBSCommand(strCommand.data());
        if (iret != 0)
        {
            LOG_ERROR << "fail parse command:"<<strCommand.data()<<LOG_ENDL;
        }
        else
        {
            // LOG_DEBUG << "executeCLBSCommand:"<<strCommand.data()<<LOG_ENDL;
        }

        _readParam.second -= oneCommandSize;
        if (_readParam.second>0)
        {
            memcpy(_readParam.first.data(), _readParam.first.data()+oneCommandSize, _readParam.second);
        }
        

        return iret;
    }
    
    int MediaF3Protocol::getMessageID(cJSON* dataNode, int& msgID)
    {
        cJSON* headNode = cJSON_GetObjectItem(dataNode, "msgHead");
        if (!headNode)
        {
            return -1;
        }
        cJSON* msgIDNode = cJSON_GetObjectItem(headNode, "msgID");
        if (!msgIDNode)
        {
            return -1;
        }
        msgID = msgIDNode->valueint;
        return 0;
    }
    int MediaF3Protocol::executeCLBSCommand(const char* strJson)
    {
        auto result = -1;

        cJSON* cjson = nullptr;
        do
        {
            cjson = cJSON_Parse(strJson);
            if (!cjson)
            {
                LOG_WARN << "executeCLBSCommand - fail parse json"<<LOG_ENDL;
                break;
            }
            cJSON* dataNode = cJSON_GetObjectItem(cjson, "data");
            if (!dataNode)
            {
                LOG_WARN << "executeCLBSCommand - no data"<<LOG_ENDL;
                break;
            }

            int messageID = 0;
            int iret = getMessageID(dataNode, messageID);
            if (iret != 0)
            {
                LOG_WARN << "executeCLBSCommand - no message id"<<LOG_ENDL;
                break;
            }

            cJSON* bodyNode = cJSON_GetObjectItem(dataNode, "msgBody");
            // LOG_DEBUG << "executeCLBSCommand - execute msgid:"<< messageID<<LOG_ENDL;
            switch (messageID)
            {
            case 1:
                OnAnswer(bodyNode);
                break;
            // case ECOMMANDID_SAVESTREAM:
            //     iret = commandSaveStreamData(bodyNode);
            //     break;
            // case ECOMMANDID_SELECTSTREAM:
            //     iret = commandSelectStream(bodyNode);
            //     break;
            default:
                LOG_WARN << "Unknow message ID!"<< messageID << LOG_ENDL;
                break;
            }
            result = iret;
        }while(0);
        if (cjson)
        {
            cJSON_Delete(cjson);
        }
        return result;
    }

    void MediaF3Protocol::OnAnswer(cJSON* dataNode)
    {
        cJSON* msgSNACKNode = cJSON_GetObjectItem(dataNode, "msgSNACK");
        cJSON* resultNode = cJSON_GetObjectItem(dataNode, "result");
        if (!msgSNACKNode || !resultNode || resultNode->type != cJSON_Number || msgSNACKNode->type != cJSON_Number)
        {
            /* code */
            loge("Error answer!");
            return;
        }
        int msgID = msgSNACKNode->valueint;
        int result = resultNode->valueint;
        
        for (auto iter = _messagesSent.begin(); iter != _messagesSent.end(); iter++)        
        {
            if (iter->number == msgID)
            {
                auto ptr = iter->answer.lock();
                if (ptr)
                {
                    ptr->onAnswer1078(msgID, result);
                }
                return;
            }
        }
        // logd("Time out!");
    }
    void MediaF3Protocol::SendMessage(const MessageInfo& info)
    {
        if (!MediaF3Protocol::Instance())
        {
            LOG_INFO << "Connect f3 server!"<< LOG_ENDL;
            MediaF3Protocol::SetupOnce();
        }
        MediaF3Protocol::Instance()->SendMessageInner(info);
    }

    void MediaF3Protocol::SetClientID(const std::string& id)
    {
        _clientID = AppSetting::Instance()->f3id + "-" + id;
    }
    void MediaF3Protocol::Heartbeat()
    {
        std::stringstream ss;
        ss<<"{\"data\":\""<<_clientID<<"\",\"desc\":{\"messageType\":0,\"msgID\":"<< EF3CommandID::EF3CommandID_HEARTBEAT <<",\"sysTime\":\"201110095921\",\"type\":\"0\",\"uuid\":\"\"}}";

        MessageInfo info  ={0};
        info.number = -1;
        info.messgae = ss.str();
        MediaF3Protocol::SendMessage(info);
    }

    int MediaF3Protocol::PlayRealTime(const MessageBaseSetting& baseSetting, const std::string&  streamType, const std::string&  dataType, const int port)
    {
        std::stringstream ss;

        int mnum = GetMessageNum();
        ss<<"{\"data\":{\"msgBody\":{\"channelNum\":"<<baseSetting.channelNum<<",\"serverIp\":\""<<AppSetting::Instance()->rtpIpSend<<"\",\"streamType\":"<<streamType<<",\"tcpPort\":"<< port <<",\"type\":"<<dataType<<",\"udpPort\":0},\"msgHead\":{\"bodySize\":0,\"encrypt\":0,\"mobile\":\""<<baseSetting.simcard<<"\",\"msgID\":"<<EF3CommandID::EF3CommandID_REALTIME<<",\"msgSN\":"<<mnum<<",\"packageNO\":0,\"packageSum\":0,\"subPackage\":0,\"type\":0}},\"desc\":{\"messageType\":0,\"msgID\":"<<EF3CommandID::EF3CommandID_REALTIME<<",\"sysTime\":\"201110093338\",\"type\":\"0\",\"deviceId\":\""<< baseSetting.deviceID <<"\",\"uuid\":\""<<baseSetting.vehicleId<<"\"}}";
        MessageInfo info  ={0};
        info.number = mnum;
        info.messgae = ss.str();
        info.answer = baseSetting.answer;
        MediaF3Protocol::SendMessage(info);
        return mnum;
    }
    int MediaF3Protocol::ControlRealTime(const MessageBaseSetting& baseSetting, const std::string& streamType, const std::string& closeVideoType,const std::string& control)
    {
        std::stringstream ss;

        int mnum = GetMessageNum();
        ss<<"{\"data\":{\"msgBody\":{\"channelNum\":"<<baseSetting.channelNum<<",\"changeStreamType\":"<<streamType<<",\"closeVideoType\":"<<closeVideoType<<",\"deviceType\":"<<"\"1\""<<",\"control\":"<<control<<"},\"msgHead\":{\"bodySize\":0,\"encrypt\":0,\"mobile\":\""<<baseSetting.simcard<<"\",\"msgID\":"<<EF3CommandID::EF3CommandID_REALTIME_C<<",\"msgSN\":"<<mnum<<",\"packageNO\":0,\"packageSum\":0,\"subPackage\":0,\"type\":0}},\"desc\":{\"messageType\":0,\"msgID\":"<<EF3CommandID::EF3CommandID_REALTIME_C<<",\"sysTime\":\"201110093338\",\"type\":\"0\",\"deviceId\":\""<< baseSetting.deviceID <<"\",\"uuid\":\""<<baseSetting.vehicleId<<"\"}}";
        MessageInfo info  ={0};
        info.number = mnum;
        info.messgae = ss.str();
        info.answer = baseSetting.answer;
        MediaF3Protocol::SendMessage(info);
        return mnum;
    }
    int MediaF3Protocol::PlayPlayback(const MessageBaseSetting& baseSetting, const std::string& endTime, const std::string& startTime, const std::string& forwardOrRewind, const std::string& remoteMode, const std::string& streamType, const std::string& storageType, int port)
    {
        std::stringstream ss;

        auto st = atoi(streamType.c_str());
        st++;

        int mnum = GetMessageNum();
        ss<<"{\"data\":{\"msgBody\":{\"channelNum\":"<<baseSetting.channelNum<<",\"deviceType\":"<<"\"1\""<<",\"endTime\":\""<<endTime<<"\",\"startTime\":\""<<startTime<<"\",\"forwardOrRewind\":"<<forwardOrRewind<<",\"remoteMode\":"<<remoteMode<<",\"serverIp\":\""<<AppSetting::Instance()->rtpIpSend<<"\",\"storageType\":"<<storageType<<",\"streamType\":"<<std::to_string(st)<<",\"tcpPort\":"<< port<<",\"udpPort\":0,\"videoType\":0},\"msgHead\":{\"bodySize\":0,\"encrypt\":0,\"mobile\":\""<<baseSetting.simcard<<"\",\"msgID\":"<<EF3CommandID::EF3CommandID_PLAYBACK<<",\"deviceType\":"<<"\"1\""<<",\"msgSN\":"<<mnum<<",\"packageNO\":0,\"packageSum\":0,\"subPackage\":0,\"type\":0}},\"desc\":{\"messageType\":0,\"msgID\":"<<EF3CommandID::EF3CommandID_PLAYBACK<<",\"sysTime\":\"201110093338\",\"type\":\"0\",\"deviceId\":\""<< baseSetting.deviceID <<"\",\"uuid\":\""<<baseSetting.vehicleId<<"\"}}";
        MessageInfo info  ={0};
        info.number = mnum;
        info.messgae = ss.str();
        info.answer = baseSetting.answer;
        MediaF3Protocol::SendMessage(info);
        return mnum;
    }
    int MediaF3Protocol::ControlPlayback(const MessageBaseSetting& baseSetting, const std::string& dragPlaybackTime, const std::string& forwardOrRewind,const std::string& remote)
    {
        std::stringstream ss;

        int mnum = GetMessageNum();
        ss<<"{\"data\":{\"msgBody\":{\"channelNum\":"<<baseSetting.channelNum<<",\"dragPlaybackTime\":\""<<dragPlaybackTime<<"\",\"forwardOrRewind\":"<<forwardOrRewind<<",\"remote\":"<<remote<<"},\"msgHead\":{\"bodySize\":0,\"encrypt\":0,\"mobile\":\""<<baseSetting.simcard<<"\",\"msgID\":"<<EF3CommandID::EF3CommandID_PLAYBACK_C<<",\"msgSN\":"<<mnum<<",\"packageNO\":0,\"packageSum\":0,\"subPackage\":0,\"type\":0}},\"desc\":{\"messageType\":0,\"msgID\":"<<EF3CommandID::EF3CommandID_PLAYBACK_C<<",\"sysTime\":\"201110093338\",\"type\":\"0\",\"deviceId\":\""<< baseSetting.deviceID <<"\",\"uuid\":\""<<baseSetting.vehicleId<<"\"}}";
        MessageInfo info  ={0};
        info.number = mnum;
        info.messgae = ss.str();
        info.answer = baseSetting.answer;
        MediaF3Protocol::SendMessage(info);
        return mnum;
    }

    
}
