
#include "MFSPublic.h"
#include "Rtp808Service.h"
#include "MediaWebSocketProtocol.h"
#include "3rd/cJSON.h"
#include "MediaInfoService.h"

namespace mfs
{
    
    http_parser_settings MediaWebSocketProtocol::callbackSetting = {
            nullptr,
            MediaWebSocketProtocol::onUrl,
            nullptr,
            MediaWebSocketProtocol::onHeaderField,
            MediaWebSocketProtocol::onHeaderValue,
            MediaWebSocketProtocol::onHeadersComplete,
            nullptr,
            nullptr,
            nullptr,
            nullptr
    };

    static bool ClearRtpBuffer(std::list<BaseWriteBuffer>& buffers, int& bufferSize)
    {
        buffers.clear();
        bufferSize = 0;
        return false;
    }

    void MediaWebSocketProtocol::sendMessage(const char* data, int dataLen)
    {
        WebSocketHead head = {0};
        head.fin = 0x01;
        head.opCode = WebSocketOpcodeType::TEXT_FRAME;
        int headSize = 2;
        if(dataLen <= 125)
        {
            head.payloadLength = static_cast<uint8_t>(dataLen);
        }
        else if(dataLen <= 0xFFFF)
        {
            head.payloadLength = 126;

#define SET_WS_HEAD_LEN(type) \
        type trueLen = dataLen; \
        std::reverse(reinterpret_cast<uint8_t*>(&trueLen), reinterpret_cast<uint8_t*>(&trueLen) + sizeof(trueLen)); \
        memcpy(head.dataLen, &trueLen, sizeof(trueLen)); \
        headSize += sizeof(trueLen)

            SET_WS_HEAD_LEN(uint16_t);
        }
        else
        {
            head.payloadLength = 127;
            SET_WS_HEAD_LEN(uint64_t);
        }
        int result = 0;
        iovec datas[2] = {{&head, static_cast<size_t>(headSize)}, {const_cast<char*>(data), static_cast<size_t>(dataLen)}};
        result = BaseProtocol::WriteV(datas, 2, true);
    }

    void MediaWebSocketProtocol::generatePackageHead(int dataLen, char* headBuf, int& headLen)
    {
        WebSocketHead head = {0};
        head.fin = 0x01;
        head.opCode = 0x02;
        head.opCode = _currentOpcode == 0x08 ? 0x08 : 0x02;
        int headSize = 2;
        if(dataLen <= 125)
        {
            head.payloadLength = static_cast<uint8_t>(dataLen);
        }
        else if(dataLen <= 0xFFFF)
        {
            head.payloadLength = 126;

#define SET_WS_HEAD_LEN(type) \
        type trueLen = dataLen; \
        std::reverse(reinterpret_cast<uint8_t*>(&trueLen), reinterpret_cast<uint8_t*>(&trueLen) + sizeof(trueLen)); \
        memcpy(head.dataLen, &trueLen, sizeof(trueLen)); \
        headSize += sizeof(trueLen)

            SET_WS_HEAD_LEN(uint16_t);
        }
        else
        {
            head.payloadLength = 127;
            SET_WS_HEAD_LEN(uint64_t);
        }
#undef SET_WS_HEAD_LEN

        if (headSize > headLen)
        {
            headLen = 0;
            loge("too long");
        }
        else
        {
            headLen = headSize;
            memcpy(headBuf, &head, headLen);
        }
        

    }
    int MediaWebSocketProtocol::writeData(char* data, int dataLen)
    {
        int iret = preWriteData(data, dataLen);
        if (iret != 0 )
        {
            return iret;
        }
        
        
        WebSocketHead head = {0};
        head.fin = 0x01;
        head.opCode = 0x02;
        head.opCode = _currentOpcode == 0x08 ? 0x08 : 0x02;
        int headSize = 2;
        if(dataLen <= 125)
        {
            head.payloadLength = static_cast<uint8_t>(dataLen);
        }
        else if(dataLen <= 0xFFFF)
        {
            head.payloadLength = 126;

#define SET_WS_HEAD_LEN(type) \
        type trueLen = dataLen; \
        std::reverse(reinterpret_cast<uint8_t*>(&trueLen), reinterpret_cast<uint8_t*>(&trueLen) + sizeof(trueLen)); \
        memcpy(head.dataLen, &trueLen, sizeof(trueLen)); \
        headSize += sizeof(trueLen)

            SET_WS_HEAD_LEN(uint16_t);
        }
        else
        {
            head.payloadLength = 127;
            SET_WS_HEAD_LEN(uint64_t);
        }
        int result = 0;
        iovec datas[2] = {{&head, static_cast<size_t>(headSize)}, {reinterpret_cast<uint8_t*>(data), static_cast<size_t>(dataLen)}};
        result = BaseProtocol::WriteV(datas, 2);
        // LOG_DEBUG << "Buffer count:"<<_listWriteBuffer.size() <<LOG_ENDL;
        if (result == -3)
        {
            // 不是回放就跳帧
            if (!_isPlayback)
            {
                std::stringstream ss;
                ss << _serviceName <<"> WriteV buffer is full, data len: " << (dataLen+4) << ", buffer size: " << _writeBufferSize;
                logd(ss.str());
                ClearRtpBuffer(_listWriteBuffer,_writeBufferSize);
            }
        }

        return result > 0 ? dataLen - headSize : result;
    }

    int MediaWebSocketProtocol::parseWebSocketHead()
    {
        WebSocketHead* head = reinterpret_cast<WebSocketHead*>(_readParam.first.data());
        if(_websocketStatus == 0)
        {
            if(_websocketOffset < 2)
            {
                return -2;
            }
            if (!(head -> rsv1 == 0 && head -> rsv2 == 0 && head -> rsv3 == 0 && head -> mask != 0))
            {
                _streamInfo.message = "Error head!";
                return -1;
            }
            _currentOpcode = head -> opCode;
            if(_currentOpcode == WebSocketOpcodeType::CLOSE)
            {
                
                _streamInfo.message = "Client shutdown!";
                logd("_currentOpcode is close");
                uint16_t data = 1000;
                data = swapBigEndian(data);
                Write(reinterpret_cast<char*>(&data), 2);
                return -1;
            }
            else if(_websocketOffset < 6)
            {
                return -2;
            }
            if(head -> opCode == 0x09)
            {
                //ping
            }
            if(head -> payloadLength <= 125)
            {
                _currentDataLen = head -> payloadLength;
            }
            else if(head -> payloadLength == 126)
            {
                _websocketStatus = 1;
            }
            else
            {
                _websocketStatus = 2;
            }
        }
        if(_websocketStatus == 1)
        {
            if(_websocketOffset < 8)
            {
                return -2;
            }
            _currentDataLen = swapBigEndian(*reinterpret_cast<uint16_t*>(head -> dataLen));
        }
        else if(_websocketStatus == 2)
        {
            if(_websocketOffset < 14)
            {
                return -2;
            }
            _currentDataLen = swapBigEndian(*reinterpret_cast<uint64_t*>(head -> dataLen));
        }
        // LOG_DEBUG << "_currentDataLen == "<<_currentDataLen <<LOG_ENDL;
        if (_currentDataLen >=  AppSetting::Instance()->readbufferMax - 14)
        {
            _streamInfo.message = "error websocket message length:"+std::to_string(_currentDataLen);
            return -1;
        }
        _websocketOffset -= _websocketStatus == 0 ? 6 : (_websocketStatus == 1 ? 8 : 14);
        _websocketStatus = 3;
        memcpy(_mask, _readParam.first.data() + _readParam.second - _websocketOffset - 4, 4);
        return 0;
    }
    int MediaWebSocketProtocol::parseWebSocketBody()
    {
        if(_websocketOffset < _currentDataLen)
        {
            return -2;
        }
        uint8_t* payloadData = _readParam.first.data() + _readParam.second - _websocketOffset;
        if(_currentOpcode == WebSocketOpcodeType::BINARY_FRAME && (_roomKey.length()>0))
        {
            for(int i = 0; i != _currentDataLen; ++i)
            {
                payloadData[i] = payloadData[i] ^ _mask[i % 4];
            }
            if (_isTalk && _isSaveTalk)
            {
                std::stringstream outfileName;
                outfileName << AppSetting::Instance()->holdTalkWebPath<<_url<<".web.audio";
                createdirectories(outfileName.str());
                std::ofstream outfile;
                outfile.open(outfileName.str(), std::ios::app);
                if (!outfile.is_open())
                {
                    std::stringstream ss;
                    ss<<"Failed to open audio file:"<<outfileName.str();
                    loge(ss.str());
                }
                else
                {
                    Rtp808HeadBaseA* headA = reinterpret_cast<Rtp808HeadBaseA*>(const_cast<uint8_t*>(payloadData));
                    auto audioDataLen = swapBigEndian(headA->dataLen);
                    outfile.write((char*)headA->data, audioDataLen);
                    outfile.close();
                    
                    std::stringstream ss;
                    ss<<"save audio ("<<audioDataLen<<"/"<<headA->dataLen<<"):"<< outfileName.str();
                    logd(ss.str());
                    PrintBuffer(payloadData+4, 32);
                }
            }
            
            onRtpData(payloadData, _currentDataLen);
        }
        else {
            for(int i = 0; i < _currentDataLen; ++i)
            {
                // payloadData[i] = payloadData[i] ^ _mask[i % 4];
                payloadData[i] = payloadData[i] ^ _mask[i&3];
            }
            if (_currentOpcode ==  WebSocketOpcodeType::TEXT_FRAME && _currentDataLen>0)
            {
                if (payloadData[0] == '{')
                {
                    // 新版
                    std::string message(_currentDataLen, 0);
                    memcpy(message.data(),payloadData,_currentDataLen);
                    onMessage(message);
                }
            }
        }

        int lastOffset = _websocketOffset - _currentDataLen;
        if(lastOffset > 0)
        {
            memcpy(_readParam.first.data(), _readParam.first.data() + _readParam.second - lastOffset, lastOffset);
        }
        _readParam.second = lastOffset;
        _websocketOffset = lastOffset;
        _websocketStatus = 0;
        return 0;
    }
    int MediaWebSocketProtocol::parse(int length)
    {
        _websocketOffset += length;
        int parseResult = 0;
        do
        {
            if(_websocketStatus == 3 && parseWebSocketBody() != 0)
            {
                break;
            }
            parseResult = parseWebSocketHead();
        } while(parseResult == 0 && _websocketOffset > 0);
        return parseResult;
    }

    int MediaWebSocketProtocol::recvHandShakeData()
    {
        // logd("Recv http head");
        http_parser_execute(&_parser, &callbackSetting, reinterpret_cast<const char*>(_readParam.first.data()), _readParam.second);
        _readParam.second = 0;
        FORWORT_ASSERT_RETURN(HTTP_PARSER_ERRNO(&_parser) == HPE_OK);

        if(_status != PARSE_HEAD_COMPLETED)
        {
            return -2;
        }

        return 0;
    }
    int MediaWebSocketProtocol::sendHandShakeData()
    {
        std::string handResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
        iovec data[3] = {{&handResponse[0], handResponse.size()}, {&_key[0], _key.size()}, {const_cast<char*>("\r\n\r\n"), 4}};
        int result = WriteV(data, sizeof(data) / sizeof(iovec));
        if(result > 0)
        {
            _status = SEND_HANDSHAKE;
        }
        return result;
    }
    int MediaWebSocketProtocol::onUrl(http_parser* parser, const char* data, size_t length)
    {
        MediaWebSocketProtocol* webSocket = reinterpret_cast<MediaWebSocketProtocol*>(parser -> data);
        webSocket -> _url = std::string(data, length);


        // LOG_DEBUG << "onUrl:"<<webSocket->_url << LOG_ENDL;
        return 0;
    }

    int MediaWebSocketProtocol::onHeaderField(http_parser* parser, const char* data, size_t length)
    {
        MediaWebSocketProtocol* webSocket = reinterpret_cast<MediaWebSocketProtocol*>(parser -> data);
        webSocket -> _temp = std::string(data, length);
        return 0;
    }

    int MediaWebSocketProtocol::onHeaderValue(http_parser* parser, const char* data, size_t length)
    {
        MediaWebSocketProtocol* webSocket = reinterpret_cast<MediaWebSocketProtocol*>(parser -> data);
        if(webSocket -> _temp == "Sec-WebSocket-Key")
        {
            std::string key(data, length);
            key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            char sha1Buffer[64] = {0};
            SHA1(sha1Buffer, key.c_str(), key.size());
            char base64Buffer[64] = {0};
            base64_encode(reinterpret_cast<uint8_t*>(sha1Buffer), 20, base64Buffer);
            webSocket -> _key = base64Buffer;
        }
        else if(webSocket->_temp == "Sec-WebSocket-Version")
        {
            webSocket->_version = std::string(data, length);
        }
        else if (webSocket->_temp == "X-Forwarded-For")
        {
            webSocket->logd("change ip :" + std::string(data, length));
            webSocket->_clientIp = std::string(data, length);
        }
        // {
        //     webSocket->logd(webSocket->_temp+" - " +std::string(data, length));
        // }
        return 0;
    }

    int MediaWebSocketProtocol::onHeadersComplete(http_parser* parser)
    {
        MediaWebSocketProtocol* webSocket = reinterpret_cast<MediaWebSocketProtocol*>(parser -> data);
        webSocket -> _status = PARSE_HEAD_COMPLETED;
        return 1;
    }

}