/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtp_sender_audio.h"

#include <string.h> //memcpy
#include <cassert> //assert

namespace webrtc {
RTPSenderAudio::RTPSenderAudio(const WebRtc_Word32 id, RTPSenderInterface* rtpSender) :
    _id(id),
    _rtpSender(rtpSender),
    _audioFeedbackCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
    _audioFeedback(NULL),
    _sendAudioCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
    _frequency(8000),
    _packetSizeSamples(160),
    _dtmfEventIsOn(false),
    _dtmfEventFirstPacketSent(false),
    _dtmfPayloadType(-1),
    _dtmfTimestamp(0),
    _dtmfKey(0),
    _dtmfLengthSamples(0),
    _dtmfLevel(0),
    _dtmfTimeLastSent(0),
    _dtmfTimestampLastSent(0),
    _REDPayloadType(-1),
    _inbandVADactive(false),
    _cngNBPayloadType(-1),
    _cngWBPayloadType(-1),
    _cngSWBPayloadType(-1),
    _lastPayloadType(-1),
    _includeAudioLevelIndication(false),    // @TODO - reset at Init()?
    _audioLevelIndicationID(0)
{
};

RTPSenderAudio::~RTPSenderAudio()
{
    delete &_sendAudioCritsect;
    delete &_audioFeedbackCritsect;
}

WebRtc_Word32
RTPSenderAudio::Init()
{
    CriticalSectionScoped cs(_sendAudioCritsect);

    _dtmfPayloadType = -1;
    _inbandVADactive = false;
    _cngNBPayloadType = -1;
    _cngWBPayloadType = -1;
    _cngSWBPayloadType = -1;
    _lastPayloadType = -1;
    _REDPayloadType = -1;
    _dtmfTimeLastSent = 0;
    _dtmfTimestampLastSent = 0;
    ResetDTMF();
    return 0;
}

void
RTPSenderAudio::ChangeUniqueId(const WebRtc_Word32 id)
{
    _id = id;
}

WebRtc_Word32
RTPSenderAudio::RegisterAudioCallback(RtpAudioFeedback* messagesCallback)
{
    CriticalSectionScoped cs(_audioFeedbackCritsect);
    _audioFeedback = messagesCallback;
    return 0;
}

void
RTPSenderAudio::SetAudioFrequency(const WebRtc_UWord32 f)
{
    CriticalSectionScoped cs(_sendAudioCritsect);
    _frequency = f;
}

int
RTPSenderAudio::AudioFrequency() const
{
    CriticalSectionScoped cs(_sendAudioCritsect);
    return _frequency;
}

    // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG)
WebRtc_Word32
RTPSenderAudio::SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples)
{
    CriticalSectionScoped cs(_sendAudioCritsect);

    _packetSizeSamples = packetSizeSamples;
    return 0;
}

WebRtc_Word32
RTPSenderAudio::RegisterAudioPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE],
                                     const WebRtc_Word8 payloadType,
                                     const WebRtc_UWord32 frequency,
                                     const WebRtc_UWord8 channels,
                                     const WebRtc_UWord32 rate,
                                     ModuleRTPUtility::Payload*& payload)
{
    WebRtc_Word32 length = (WebRtc_Word32)strlen(payloadName);
    if(length > RTP_PAYLOAD_NAME_SIZE)
    {
        return -1;
    }

    CriticalSectionScoped cs(_sendAudioCritsect);

    if (ModuleRTPUtility::StringCompare(payloadName,"cn",2))
    {
        //  we can have multiple CNG payload types
        if(frequency == 8000)
        {
            _cngNBPayloadType = payloadType;

        } else if(frequency == 16000)
        {
            _cngWBPayloadType = payloadType;

        } else if(frequency == 32000)
        {
            _cngSWBPayloadType = payloadType;
        }else
        {
            return -1;
        }
    }
    if (ModuleRTPUtility::StringCompare(payloadName,"telephone-event",15))
    {
        // Don't add it to the list
        // we dont want to allow send with a DTMF payloadtype
        _dtmfPayloadType = payloadType;
        return 0;
        // The default timestamp rate is 8000 Hz, but other rates may be defined.
    }
    payload = new ModuleRTPUtility::Payload;
    payload->typeSpecific.Audio.frequency = frequency;
    payload->typeSpecific.Audio.channels = channels;
    payload->typeSpecific.Audio.rate = rate;
    payload->audio = true;
    memcpy(payload->name, payloadName, length+1);
    return 0;
}

bool
RTPSenderAudio::MarkerBit(const FrameType frameType,
                          const WebRtc_Word8 payloadType)
{
    CriticalSectionScoped cs(_sendAudioCritsect);

    // for audio true for first packet in a speech burst
    bool markerBit = false;
    if(_lastPayloadType != payloadType)
    {
        if(_cngNBPayloadType != -1)
        {
            // we have configured NB CNG
            if(_cngNBPayloadType == payloadType)
            {
                // only set a marker bit when we change payload type to a non CNG
                return false;
            }
        }
        if(_cngWBPayloadType != -1)
        {
            // we have configured WB CNG
            if(_cngWBPayloadType == payloadType)
            {
                // only set a marker bit when we change payload type to a non CNG
                return false;
            }
        }
        if(_cngSWBPayloadType != -1)
        {
            // we have configured SWB CNG
            if(_cngSWBPayloadType == payloadType)
            {
                // only set a marker bit when we change payload type to a non CNG
                return false;
            }
        }
        // payloadType differ
        if(_lastPayloadType == -1)
        {
            if(frameType != kAudioFrameCN)
            {
                // first packet and NOT CNG
                return true;

            }else
            {
                // first packet and CNG
                _inbandVADactive = true;
                return false;
            }
        }
        // not first packet AND
        // not CNG AND
        // payloadType changed

        // set a marker bit when we change payload type
        markerBit = true;
    }

    // For G.723 G.729, AMR etc we can have inband VAD
    if(frameType == kAudioFrameCN)
    {
        _inbandVADactive = true;

    } else if(_inbandVADactive)
    {
        _inbandVADactive = false;
        markerBit = true;
    }
    return markerBit;
}

bool
RTPSenderAudio::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const
{
    if(_dtmfEventIsOn)
    {
        telephoneEvent = _dtmfKey;
        return true;
    }
    WebRtc_UWord32 delaySinceLastDTMF = (ModuleRTPUtility::GetTimeInMS() - _dtmfTimeLastSent);
    if(delaySinceLastDTMF < 100)
    {
        telephoneEvent = _dtmfKey;
        return true;
    }
    telephoneEvent = -1;
    return false;
}

WebRtc_Word32
RTPSenderAudio::SendAudio(const FrameType frameType,
                          const WebRtc_Word8 payloadType,
                          const WebRtc_UWord32 captureTimeStamp,
                          const WebRtc_UWord8* payloadData,
                          const WebRtc_UWord32 dataSize,
                          const RTPFragmentationHeader* fragmentation)
{
    WebRtc_UWord16 payloadSize = (WebRtc_UWord16)dataSize;
    WebRtc_UWord16 maxPayloadLength = _rtpSender->MaxPayloadLength();
    bool dtmfToneStarted = false;
    WebRtc_UWord16 dtmfLengthMS = 0;
    WebRtc_UWord8 key = 0;

    // Check if we have pending DTMFs to send
    if ( !_dtmfEventIsOn && PendingDTMF())
    {
        CriticalSectionScoped cs(_sendAudioCritsect);

        WebRtc_UWord32 delaySinceLastDTMF = (ModuleRTPUtility::GetTimeInMS() - _dtmfTimeLastSent);

        if(delaySinceLastDTMF > 100)
        {
            // New tone to play
            _dtmfTimestamp = captureTimeStamp;
            if (NextDTMF(&key, &dtmfLengthMS, &_dtmfLevel) >= 0)
            {
                _dtmfEventFirstPacketSent = false;
                _dtmfKey = key;
                _dtmfLengthSamples = (_frequency/1000)*dtmfLengthMS;
                dtmfToneStarted = true;
                _dtmfEventIsOn = true;
            }
        }
    }
    if(dtmfToneStarted)
    {
        CriticalSectionScoped cs(_audioFeedbackCritsect);
        if(_audioFeedback)
        {
            _audioFeedback->OnPlayTelephoneEvent(_id, key, dtmfLengthMS, _dtmfLevel);
        }
    }

    // A source MAY send events and coded audio packets for the same time
    // but we don't support it
    {
        _sendAudioCritsect.Enter();

        if (_dtmfEventIsOn)
        {
            if(frameType == kFrameEmpty)
            {
                // kFrameEmpty is used to drive the DTMF when in CN mode
                // it can be triggered more frequently than we want to send the DTMF packets
                if(_packetSizeSamples > (captureTimeStamp - _dtmfTimestampLastSent) )
                {
                    // not time to send yet
                    _sendAudioCritsect.Leave();
                    return 0;
                }
            }
            _dtmfTimestampLastSent = captureTimeStamp;

            WebRtc_UWord32 dtmfDurationSamples = (captureTimeStamp - _dtmfTimestamp);

            bool ended = false;
            bool send = true;

            if(_dtmfLengthSamples > dtmfDurationSamples)
            {
                if (dtmfDurationSamples > 0) // Skip send packet at start, since we shouldn't use duration 0
                {
                } else
                {
                    send = false;
                }
            }else
            {
                ended = true;
                _dtmfEventIsOn = false;
                _dtmfTimeLastSent = ModuleRTPUtility::GetTimeInMS();
            }
            // don't hold the critsect while calling SendTelephoneEventPacket
            _sendAudioCritsect.Leave();
            if(send)
            {
                if(dtmfDurationSamples > 0xffff)
                {
                    // RFC 4733 2.5.2.3 Long-Duration Events
                    SendTelephoneEventPacket(ended, _dtmfTimestamp, (WebRtc_UWord16)0xffff, false);

                    // set new timestap for this segment
                    _dtmfTimestamp = captureTimeStamp;
                    dtmfDurationSamples -= 0xffff;
                    _dtmfLengthSamples -= 0xffff;

                    return SendTelephoneEventPacket(ended, _dtmfTimestamp, (WebRtc_UWord16)dtmfDurationSamples, false);
                } else
                {
                    // set markerBit on the first packet in the burst
                    WebRtc_Word32 retVal = SendTelephoneEventPacket(ended, _dtmfTimestamp, (WebRtc_UWord16)dtmfDurationSamples, !_dtmfEventFirstPacketSent);
                    _dtmfEventFirstPacketSent = true;
                    return retVal;
                }
            }
            return(0);
        }
        _sendAudioCritsect.Leave();
    }
    if(payloadSize == 0 || payloadData == NULL)
    {
        if(frameType == kFrameEmpty)
        {
            // we don't send empty audio RTP packets
            // no error since we use it to drive DTMF when we use VAD
            return 0;
        }else
        {
            return -1;
        }
    }

    WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
    bool markerBit = MarkerBit(frameType, payloadType);

    WebRtc_Word32 rtpHeaderLength = 0;
    WebRtc_UWord16 timestampOffset = 0;

    if( _REDPayloadType >= 0 &&
        fragmentation &&
        fragmentation->fragmentationVectorSize > 1 &&
        !markerBit)
    {
        // have we configured RED? use its payload type
        // we need to get the current timestamp to calc the diff
        WebRtc_UWord32 oldTimeStamp = _rtpSender->Timestamp();
        rtpHeaderLength = _rtpSender->BuildRTPheader(dataBuffer, _REDPayloadType, markerBit, captureTimeStamp);
        timestampOffset = WebRtc_UWord16(_rtpSender->Timestamp() - oldTimeStamp);
    } else
    {
        rtpHeaderLength= _rtpSender->BuildRTPheader(dataBuffer, payloadType, markerBit, captureTimeStamp);
    }
    if(rtpHeaderLength == -1)
    {
        return -1;
    }
    {
        CriticalSectionScoped cs(_sendAudioCritsect);

        if (_includeAudioLevelIndication)
        {
            dataBuffer[0] |= 0x10; // set eXtension bit

            // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/

            /*
            0                   1                   2                   3
            0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |      0xBE     |      0xDE     |            length=1           |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |  ID   | len=0 |V|   level     |      0x00     |      0x00     |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            */

            // add the extension

            // add our ID (0xBEDE)
            ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, RTP_AUDIO_LEVEL_UNIQUE_ID);
            rtpHeaderLength += 2;

            // add the length (length=1) in number of word32
            const WebRtc_UWord8 length = 1;
            ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, length);
            rtpHeaderLength += 2;

            // add ID (defined by the user) and len(=0) byte
            const WebRtc_UWord8 id = _audioLevelIndicationID;
            const WebRtc_UWord8 len = 0;
            dataBuffer[rtpHeaderLength++] = (id << 4) + len;

            // add voice-activity flag (V) bit and the audio level (in dBov)
            const WebRtc_UWord8 V = (frameType == kAudioFrameSpeech);
            WebRtc_UWord8 level = _audioLevel_dBov;
            dataBuffer[rtpHeaderLength++] = (V << 7) + level;

            // add two bytes zero padding
            ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, 0);
            rtpHeaderLength += 2;
        }

        if(maxPayloadLength < rtpHeaderLength + payloadSize )
        {
            // too large payload buffer
            return -1;
        }

        if( _REDPayloadType >= 0 &&                         // have we configured RED?
            fragmentation &&
            fragmentation->fragmentationVectorSize > 1 &&
            !markerBit)
        {
            if(fragmentation == NULL)
            {
                // this can't happen any more but save the code incase we want to use it later again
                // we don't send this type of packet due to old NetEq issue
                dataBuffer[rtpHeaderLength++] = (WebRtc_UWord8)payloadType;
                memcpy(dataBuffer+rtpHeaderLength, payloadData, payloadSize);
            }else
            {
                if( fragmentation->fragmentationVectorSize > 1 &&
                    !markerBit &&               // markerBit == first packet
                    timestampOffset <= 0x3fff)  // silence for too long send only new data
                {
                    if(fragmentation->fragmentationVectorSize != 2)
                    {
                        // we only support 2 codecs when using RED
                        return -1;
                    }
                    // only 0x80 if we have multiple blocks
                    dataBuffer[rtpHeaderLength++] = 0x80 + fragmentation->fragmentationPlType[1];
                    WebRtc_UWord32 blockLength = fragmentation->fragmentationLength[1];

                    // sanity blockLength
                    if(blockLength > 0x3ff) // block length 10 bits 1023 bytes
                    {
                        return -1;
                    }
                    WebRtc_UWord32 REDheader =  (timestampOffset << 10) + blockLength;
                    ModuleRTPUtility::AssignUWord24ToBuffer(dataBuffer+rtpHeaderLength, REDheader);
                    rtpHeaderLength += 3;

                    dataBuffer[rtpHeaderLength++] = fragmentation->fragmentationPlType[0];
                    // copy the RED data
                    memcpy(dataBuffer+rtpHeaderLength,
                        payloadData + fragmentation->fragmentationOffset[1],
                        fragmentation->fragmentationLength[1]);

                    // copy the normal data
                    memcpy( dataBuffer+rtpHeaderLength + fragmentation->fragmentationLength[1],
                            payloadData + fragmentation->fragmentationOffset[0],
                            fragmentation->fragmentationLength[0]);

                    payloadSize = WebRtc_UWord16(fragmentation->fragmentationLength[0] + fragmentation->fragmentationLength[1]);

                } else
                {
                    dataBuffer[rtpHeaderLength++] = (WebRtc_UWord8)payloadType;

                    memcpy( dataBuffer+rtpHeaderLength,
                            payloadData + fragmentation->fragmentationOffset[0],
                            fragmentation->fragmentationLength[0]);

                    payloadSize = WebRtc_UWord16(fragmentation->fragmentationLength[0]);
                }
            }
        }else
        {
            if( fragmentation &&
                fragmentation->fragmentationVectorSize > 0)
            {
                // use the fragment info if we have one
                memcpy( dataBuffer+rtpHeaderLength,
                        payloadData + fragmentation->fragmentationOffset[0],
                        fragmentation->fragmentationLength[0]);

                payloadSize = WebRtc_UWord16(fragmentation->fragmentationLength[0]);

            }else
            {
                memcpy(dataBuffer+rtpHeaderLength, payloadData, payloadSize);
            }
        }
        _lastPayloadType = payloadType;

    }   // end critical section

    return _rtpSender->SendToNetwork(dataBuffer, payloadSize, (WebRtc_UWord16)rtpHeaderLength);
}


WebRtc_Word32
RTPSenderAudio::SetAudioLevelIndicationStatus(const bool enable,
                                              const WebRtc_UWord8 ID)
{
    if(ID < 1 || ID > 14)
    {
        return -1;
    }
    CriticalSectionScoped cs(_sendAudioCritsect);

    _includeAudioLevelIndication = enable;
    _audioLevelIndicationID = ID;

    return 0;
}

WebRtc_Word32
RTPSenderAudio::AudioLevelIndicationStatus(bool& enable,
                                           WebRtc_UWord8& ID) const
{
    CriticalSectionScoped cs(_sendAudioCritsect);
    enable = _includeAudioLevelIndication;
    ID = _audioLevelIndicationID;
    return 0;
}

    // Audio level magnitude and voice activity flag are set for each RTP packet
WebRtc_Word32
RTPSenderAudio::SetAudioLevel(const WebRtc_UWord8 level_dBov)
{
    if (level_dBov > 127)
    {
        return -1;
    }
    CriticalSectionScoped cs(_sendAudioCritsect);
    _audioLevel_dBov = level_dBov;
    return 0;
}

    // Set payload type for Redundant Audio Data RFC 2198
WebRtc_Word32
RTPSenderAudio::SetRED(const WebRtc_Word8 payloadType)
{
    if(payloadType < -1 )
    {
        return -1;
    }
    _REDPayloadType = payloadType;
    return 0;
}

    // Get payload type for Redundant Audio Data RFC 2198
WebRtc_Word32
RTPSenderAudio::RED(WebRtc_Word8& payloadType) const
{
    if(_REDPayloadType == -1)
    {
        // not configured
        return -1;
    }
    payloadType = _REDPayloadType;
    return 0;
}

// Send a TelephoneEvent tone using RFC 2833 (4733)
WebRtc_Word32
RTPSenderAudio::SendTelephoneEvent(const WebRtc_UWord8 key,
                                   const WebRtc_UWord16 time_ms,
                                   const WebRtc_UWord8 level)
{
    // DTMF is protected by its own critsect
    if(_dtmfPayloadType < 0)
    {
        // TelephoneEvent payloadtype not configured
        return -1;
    }
    return AddDTMF(key, time_ms, level);
}

WebRtc_Word32
RTPSenderAudio::SendTelephoneEventPacket(const bool ended,
                                         const WebRtc_UWord32 dtmfTimeStamp,
                                         const WebRtc_UWord16 duration,
                                         const bool markerBit)
{
    WebRtc_UWord8 dtmfbuffer[IP_PACKET_SIZE];
    WebRtc_UWord8 sendCount = 1;
    WebRtc_Word32 retVal = 0;

    if(ended)
    {
        // resend last packet in an event 3 times
        sendCount = 3;
    }
    do
    {
        _sendAudioCritsect.Enter();

        //Send DTMF data
        _rtpSender->BuildRTPheader(dtmfbuffer, _dtmfPayloadType, markerBit, dtmfTimeStamp);

        // reset CSRC and X bit
        dtmfbuffer[0] &= 0xe0;

        //Create DTMF data
        /*    From RFC 2833:

         0                   1                   2                   3
         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |     event     |E|R| volume    |          duration             |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        */
        // R bit always cleared
        WebRtc_UWord8 R = 0x00;
        WebRtc_UWord8 volume = _dtmfLevel;

        // First packet un-ended
          WebRtc_UWord8 E = 0x00;

        if(ended)
        {
            E = 0x80;
        }

        // First byte is Event number, equals key number
        dtmfbuffer[12] = _dtmfKey;
        dtmfbuffer[13] = E|R|volume;
        ModuleRTPUtility::AssignUWord16ToBuffer(dtmfbuffer+14, duration);

        _sendAudioCritsect.Leave();
        retVal = _rtpSender->SendToNetwork(dtmfbuffer, 4, 12);
        sendCount--;

    }while (sendCount > 0 && retVal == 0);

    return retVal;
}
} // namespace webrtc
