
#include "MediaInfoService.h"
#include "Rtp808Protocol.h"
#include "MediaWebSocketService.h"
#include "MediaClientService.h"
// #include "MediaRtmpService.h"
#include "ZWRoom.h"

namespace mfs
{
// std::map<std::string, Rtp808Protocol*> Rtp808Protocol::clientMap;

    static std::string testfilenamepcm = "/mnt/e/test_files/20200408_AAC/test.pcm";
    static std::string testfilenameaac = "/mnt/e/test_files/20200408_AAC/test.aac";
    Rtp808Protocol::Rtp808Protocol(std::shared_ptr<uvw::Loop> loop, std::shared_ptr<uvw::TCPHandle> client, const std::string& type)
    :BaseProtocol(loop, client, type),_type(type)//, _clientIterator(clientMap.end())
    {
        client->userAlloc(true);
        _frameParser = std::make_shared<Rtp808FrameParser>();
    
        _frameBuffer[0].second = 0;
        _frameBuffer[1].second = 0;
        _currentBuffer = _frameBuffer;
        _startTime = getNowStr();
        
        _isTalk = IsContainsStr(_type, "Talkback"); 
        _isPlayback = IsContainsStr(_type, "Palyback"); 
#if CONVERT_AAC
        _adpcmState = std::make_shared<adpcm_state>();
        _pcm2aac = std::make_shared<Pcm2AAC>();
        _pcm2aac->Init(8000, AV_SAMPLE_FMT_S16, 1);
        
        testfilestreampcm.open(testfilenamepcm, std::ios::binary);
        testfilestreamaac.open(testfilenameaac, std::ios::binary);
#endif
        _streamInfo.targetType = "0";
        // LOG_START;
        logd(std::to_string((int64_t)this));
    }

    Rtp808Protocol::~Rtp808Protocol()
    {
        // LOG_FREE;
        logd(std::to_string((int64_t)this));
    }
    void Rtp808Protocol::OnAlloc(uv_buf_t *buf, int suggested)
    {
        // void* ptrorigin = static_cast<void *>(_currentBuffer->first.data());
        // std::cout << "OnAlloc:ptr0="<<ptrorigin << std::endl;
        auto& readParam = *_currentBuffer;
        // std::cout << "OnAlloc2:ptr0="<<static_cast<void *>(readParam.first.data()) << std::endl;
        
        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)
            {
                // 回放时，允许堵塞
                if (!_isPlayback)
                {
                    loge("Read buffer full 1!");
                    _streamInfo.message = "Buffer full(r)!";
                    this->Close();
                }
                else
                {
                    logd("Read buffer full 2!");
                }
                // LOG_WARN << "playback readbuffer full"<<LOG_ENDL;
                buf->base = (char*)readParam.first.data() + readParam.second;
                buf->len = readParam.first.size() - readParam.second;
                return;
            }
            readParam.first.resize(newSize);
        }
        buf->base = (char*)readParam.first.data() + readParam.second;
        buf->len = readParam.first.size() - readParam.second;
        // LOG_DEBUG << "suggested = "<<suggested<<", len = " << buf->len << ", base="<<buf->base << LOG_ENDL;
    }
    int Rtp808Protocol::Write(char* data, int dataLen, bool forced)
    {
        auto iret = 0;
        if (_isTalk && _isYuebiao)
        {
            for (int i = 0; i < dataLen - 30; i++)
            {
                if(data[i] == 0X30 && data[i+1] == 0X31 && data[i+2] == 0X63 && data[i+3] == 0X64)
                {
                    // Rtp808HeadBaseA_YB* head = reinterpret_cast<Rtp808HeadBaseA_YB*>(&data[i]);
                    char bcd[10]={0};
                    iret += BaseProtocol::Write(data, i+8, forced);
                    iret += BaseProtocol::Write(bcd, 10, forced);
                    iret += BaseProtocol::Write(data+i+8+6, dataLen-i-8-6, forced);
                    break;
                }
            }
        }
        else
        {
            iret = BaseProtocol::Write(data, dataLen, forced);
        }
        return iret;
    }
    void Rtp808Protocol::OnData(char* data, int dataLen)
    {

        BaseProtocol::OnData(data, dataLen);

        _currentBuffer->second += dataLen;
        // LOG_DEBUG << "recv "<<dataLen<< "/"<<_currentBuffer->second<< LOG_ENDL;
        
        _flow += dataLen;
        _allFlow += dataLen;
        // logstream(_logReadFlag, dataLen, _readBytesAll, STREAM_LOG_RATE); 
        
        // LOG_DEBUG << "_currentBuffer = "<<_currentBuffer->second << LOG_ENDL;
        int parseResult = 0;
        do
        {
            // LOG_DEBUG << "_canParseNext = "<<_canParseNext << LOG_ENDL;
            // 解析一帧
            if (_canParseNext)
            {
                // LOG_DEBUG << "loadData "<<_nextFramePos<< "/"<<_currentBuffer->second<< LOG_ENDL;
                parseResult = _frameParser->loadData(*_currentBuffer, _nextFramePos);
                if (parseResult == 0)
                {
                    updateTimestamp();
                    _nextFramePos += _frameParser->rtpFrameLen();
                    if (_isFirstFrame)
                    {
                        OnReady();
                        _isFirstFrame = false;
                    }
                }
            }
            
            // LOG_DEBUG << "_nextFramePos = "<<_nextFramePos <<",parseResult="<<parseResult<<",currentState="<<_frameParser->currentState() << LOG_ENDL;
            // 处理当前帧数据
            if (_nextFramePos>0)
            {
                auto iret = parseRtpBody();
                if (iret != 0)
                {
                    _canParseNext = false;
                    break;
                }
                _canParseNext = true;
            }
            
        } while(parseResult == 0 && _currentBuffer->second > 0);
        if (parseResult == -1)
        {
            if (_onDataErrorCallback)
            {
                _onDataErrorCallback(_frameParser->errorField().c_str());
            }
            
            loge("Parse rtp head fail.");
            _streamInfo.message = "RTP error:"+_frameParser->errorMessage();
            this->Close();
        }
        
    }; 
    
    void Rtp808Protocol::OnClose()
    {
        BaseProtocol::OnClose();
        _endTime = getNowStr();
        OnRlease();
        if (_onDisconnectionFunc)
        {
            _onDisconnectionFunc(std::static_pointer_cast<Rtp808Protocol>(shared_from_this()));
        }
        if (_onReleaseFunc)
        {
            _onReleaseFunc(std::static_pointer_cast<Rtp808Protocol>(shared_from_this()));
        }
#if CONVERT_AAC
        testfilestreampcm.close();
        testfilestreamaac.close();
#endif
    }; 
    void Rtp808Protocol::OnFlowCallback()
    {
        MediaInfoService::Instance()->sendFlowInfo(this, 0);
    }
    void Rtp808Protocol::OnRlease()
    {
        MediaInfoService::Instance()->sendFlowInfo(this, 1);
    }

    void Rtp808Protocol::OnReady()
    {
        _streamInfo.simNumber = getSimNumber();
        _streamInfo.channel = std::to_string(getChannel());
        _streamInfo.uuid = getUuid();
        BaseProtocol::OnReady();
        
        if (_onConnectionFunc)
        {
            _onConnectionFunc(std::static_pointer_cast<Rtp808Protocol>(shared_from_this()));
        }

        auto roomKey = ZWRoom::addTerminal(std::static_pointer_cast<Rtp808Protocol>(shared_from_this()));
        if (roomKey.empty())
        {
            loge("none room!");
            this->Close();
            return;
        }
        else
        {
            _uuid = roomKey;
            // if (_lastIFrame.second > 0)
            // {
            //     client->writeData((char*)_lastIFrame.first.data(), _lastIFrame.second);
            // }
        }
        logd("add terminal, room key:"+roomKey+" _uuid:"+_uuid);
    }
    void Rtp808Protocol::updateTimestamp()
    {
        // 对讲时更新时间戳
        // 有bug，先去除，使用原始时间戳，有问题再调整
        if (false)//(_isTalk && _frameParser->currentType() == RTP_808_DATATYPE_A)
        {
            // audio -> update timestamp
            auto payloadType = _frameParser->payloadType();
            if (payloadType == PAYLOAD_TYPE_ADPCM || payloadType == PAYLOAD_TYPE_G726)
            {
                if (_timestamp == 0)
                {
                    _timestamp = _lastTime * 1000;
                }

                // LOG_DEBUG << "record audio timestamp:"<<_timestamp<<LOG_ENDL;
                _frameParser->setTime(_timestamp);
                
                // adpcm, g726(32kbit/s) 都是4倍压缩率;
                int dataLength = _frameParser->payloadDataLen();
                int pcmLength = dataLength << 2;
                _timestamp += pcmLength >> 4;//1000 * pcmLength / SAMPLE_RATE / BIT_DEPTH_BYTES;
            }
        }
    }

    void addADTSHead(uint8_t* buffer, int packageLength, int channels, int sampleRate, int aacprofile)
    {
        int profile = aacprofile; // AAC LC  
        int freqIdx = 0xb; // 44.1KHz  
        int chanCfg = channels; // CPE  

        if (96000 == sampleRate)
        {
            freqIdx = 0x00;
        }
        else if(88200 == sampleRate)
        {
            freqIdx = 0x01;
        }
        else if (64000 == sampleRate)
        {
            freqIdx = 0x02;
        }
        else if (48000 == sampleRate)
        {
            freqIdx = 0x03;
        }
        else if (44100 == sampleRate)
        {
            freqIdx = 0x04;
        }
        else if (32000 == sampleRate)
        {
            freqIdx = 0x05;
        }
        else if (24000 == sampleRate)
        {
            freqIdx = 0x06;
        }
        else if (22050 == sampleRate)
        {
            freqIdx = 0x07;
        }
        else if (16000 == sampleRate)
        {
            freqIdx = 0x08;
        }
        else if (12000 == sampleRate)
        {
            freqIdx = 0x09;
        }
        else if (11025 == sampleRate)
        {
            freqIdx = 0x0a;
        }
        else if (8000 == sampleRate)
        {
            freqIdx = 0x0b;
        }
        else if (7350 == sampleRate)
        {
            freqIdx = 0xc;
        }
        // fill in ADTS data  
        buffer[0] = 0xFF;
        buffer[1] = 0xF9;
        buffer[2] = ((profile) << 6) + (freqIdx << 2) + (chanCfg >> 2);
        buffer[3] = (((chanCfg & 3) << 6) + (packageLength >> 11));
        buffer[4] = ((packageLength & 0x7FF) >> 3);
        buffer[5] = (((packageLength & 7) << 5) + 0x1F);
        buffer[6] = 0xFC;
    }

    // 返回0，处理完成；返回其它，阻塞
    int Rtp808Protocol::parseRtpBody()
    {
        
        if(_frameParser->currentState() == 0 || _frameParser->currentState() == 2)
        {
            
            auto frameBuffer = _currentBuffer;
            int lastOffset = frameBuffer->second - _nextFramePos; // 剩下未处理的数据长度
            auto ptrWriteBuffer = (char*)frameBuffer->first.data();
            auto ptrWirteBufferLength = _nextFramePos;

#if CONVERT_AAC
            std::vector<uint8_t> aacbuffer(ptrWriteBuffer, ptrWriteBuffer+26);
            Rtp808HeadBaseV* head = reinterpret_cast<Rtp808HeadBaseV*>((uint8_t*)ptrWriteBuffer);
            auto payloadType = PAYLOAD_TYPE_MASK & head->headPT;
            if (payloadType == PAYLOAD_TYPE_ADPCM)
            {
                auto adpcmdata = ptrWriteBuffer+26;
                auto adpcmdatalen = ptrWirteBufferLength-26;

                auto len = (adpcmdatalen - 8) << 1;           //
                _adpcmState->valPrev = adpcmdata[4] + (adpcmdata[5] << 8);
                _adpcmState->index = adpcmdata[6];
                std::vector<short> pcmdata;
                if (pcmdata.size() < len)
                {
                    pcmdata.resize(len);
                }
                adpcm_decode((char *)(adpcmdata + 8), (short*)pcmdata.data(), len, _adpcmState.get());


                // std::string testfilenamepcm2 = "/mnt/e/test_files/20200408_AAC/test2.pcm";
                // std::ofstream  testfilestreampcm2;
                // testfilestreampcm2.open(testfilenamepcm2, std::ios::app);
                // testfilestreampcm2.write((const char*)pcmdata.data(), pcmdata.size()*2);
                // testfilestreampcm2.close();
                
                std::string testfilenamepcm3 = "/mnt/e/test_files/20200408_AAC/test2.aac";
                std::ofstream  testfilestreampcm3;
                // testfilestreampcm3.open(testfilenamepcm3, std::ios::app);
		        auto addret = _pcm2aac->AddData((char*)pcmdata.data(), pcmdata.size()*2);
                // LOG_DEBUG << "AddData="<<addret<<LOG_ENDL;

                if (addret == 0)
                {
                    char * pOutData = NULL;
                    int OutSize = 0;
                    while (true)
                    {
                        if (!_pcm2aac->GetData(pOutData, OutSize))
                        {
                        // LOG_DEBUG<<"convert aac fail"<<LOG_ENDL;
                            break;
                        }
                        // testfilestreampcm3.write((const char*)pOutData, OutSize);
                        // LOG_DEBUG<<"convert aac success"<<LOG_ENDL;
                    }
                    // testfilestreampcm3.close();
                
                    if (OutSize>0)
                    {
                        
                        std::string testfilenameaac3 = "/mnt/e/test_files/20200408_AAC/test3.aac";
                        // std::ofstream  testfilestreamaac3;
                        // testfilestreamaac3.open(testfilenameaac3, std::ios::app);
                        // testfilestreamaac3.write(pOutData, OutSize);
                        // testfilestreamaac3.close();

                        aacbuffer.resize(26+OutSize);
                        memcpy(aacbuffer.data()+26, pOutData, OutSize);

                        ptrWriteBuffer = aacbuffer.data();
                        ptrWirteBufferLength = 26+OutSize;
                        Rtp808HeadBaseV* heada = reinterpret_cast<Rtp808HeadBaseV*>((uint8_t*)ptrWriteBuffer);
                        heada->headPT &= (1 << 7);
                        heada->headPT |= PAYLOAD_TYPE_AACLC;
                        // LOG_DEBUG <<"heada->headPT="<<heada->headPT <<LOG_ENDL;
                        LOG_DEBUG <<"convert aac success:"<< OutSize<<LOG_ENDL;
                    } else {
                        LOG_DEBUG <<"convert aac fail"<<LOG_ENDL;
                        ptrWirteBufferLength=0;
                    }
                
                }
                
            }
#endif

            // 终端暂未实现, 加ADTS方便测试
            if (0)
            {
                std::vector<uint8_t> bufferADST;
                Rtp808HeadBaseV* head = reinterpret_cast<Rtp808HeadBaseV*>((uint8_t*)ptrWriteBuffer);
                auto payloadType = PAYLOAD_TYPE_MASK & head->headPT;
                if (PAYLOAD_TYPE_AAC == payloadType || PAYLOAD_TYPE_AACLC == payloadType)
                {
                    // rtp头部 数据长度 加7字节
                    Rtp808HeadBaseA* headA = reinterpret_cast<Rtp808HeadBaseA*>((uint8_t*)ptrWriteBuffer);
                    auto audioDataLen = swapBigEndian(headA->dataLen);
                    audioDataLen+=7;
                    headA->dataLen = swapBigEndian(audioDataLen);

                    // 加adst
                    int profile = 0;
                    switch (payloadType)
                    {
                    case PAYLOAD_TYPE_AAC:
                        profile = 0;
                        break;
                    case PAYLOAD_TYPE_AACLC:
                        profile = 1;
                        break;
                    }
                    // profile=1;
                    bufferADST.resize(ptrWirteBufferLength+7);
                    memcpy(bufferADST.data(), ptrWriteBuffer, 26);
                    addADTSHead(bufferADST.data()+26, ptrWirteBufferLength-26+7, CHANNELS, 16000, profile);
                    memcpy(bufferADST.data()+26+7, ptrWriteBuffer+26, ptrWirteBufferLength-26);

                    ptrWriteBuffer = (char*)bufferADST.data();
                    ptrWirteBufferLength+=7;

                    // std::string testfilenameaacadts = "/tmp/lichongtest_adts_"+_uuid+".aac";
                    // std::ofstream  testfilestreamaacadts;
                    // testfilestreamaacadts.open(testfilenameaacadts, std::ios::app);
                    // testfilestreamaacadts.write(ptrWriteBuffer+26, ptrWirteBufferLength-26);
                    // testfilestreamaacadts.close();
                }
            
            }
            
            if (ptrWirteBufferLength>0)
            {
                if (_onDataCallback)
                {
                    auto iret = _onDataCallback(ptrWriteBuffer, ptrWirteBufferLength);
                    if (iret != 0)
                    {
                        return iret;
                    }
                    
                }
            }
            
            // 交换缓存 
            frameBuffer->second = _nextFramePos;
            if(_frameParser->currentType() == RTP_808_DATATYPE_I)
            {
                _currentBuffer = _currentBuffer == _frameBuffer ? _frameBuffer + 1 : _frameBuffer;
                if(_currentBuffer -> first.size() < lastOffset)
                {
                    _currentBuffer -> first.resize(lastOffset);
                }

                // 缓存I帧
                if (_lastIFrame.first.size()<frameBuffer->second)
                {
                    _lastIFrame.first.resize(frameBuffer->second);
                }
                memcpy(_lastIFrame.first.data(), frameBuffer->first.data(), frameBuffer->second);
                _lastIFrame.second = frameBuffer->second;
            }
            if(lastOffset > 0)
            {
                memcpy(_currentBuffer->first.data(), frameBuffer->first.data() + _nextFramePos, lastOffset);
            }
            _currentBuffer->second = lastOffset;
            _nextFramePos = 0;
            // LOG_DEBUG << "forward "<<_currentBuffer->second<< LOG_ENDL;
        }
        return 0;
    }
    
    void Rtp808Protocol::FlushAllData()
    {
        // logd("FlushAllData:"+std::to_string(_currentBuffer->second));
        if (_currentBuffer->second>0)
        {
            if (_onDataCallback)
            {
                _onDataCallback((char*)_currentBuffer->first.data(), _currentBuffer->second);
            }
            _currentBuffer->second = 0;
            _nextFramePos = 0;
        }
    }
    
    void Rtp808Protocol::updateStreamInfo(const std::string& vehicleId, const std::string& userID, const std::string& uuid)
    {
        _streamInfo.playType = _isTalk ? "2" : _isPlayback ? "1" : "0";
        _streamInfo.vehicleId = vehicleId;
        _streamInfo.uuid = uuid;
        _streamInfo.userID = userID;
    }
}