/*
 *  Copyright (c) 2012 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.
 */

/*
 * BwEstimator.c
 *
 * This file contains the code for the Bandwidth Estimator designed
 * for iSAC.
 *
 */

#include <math.h>
#include <string.h>

#include "modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h"
#include "modules/audio_coding/codecs/isac/main/source/settings.h"
#include "modules/audio_coding/codecs/isac/main/include/isac.h"
#include "rtc_base/checks.h"

/* array of quantization levels for bottle neck info; Matlab code: */
/* sprintf('%4.1ff, ', logspace(log10(5000), log10(40000), 12)) */
static const float kQRateTableWb[12] =
{
  10000.0f, 11115.3f, 12355.1f, 13733.1f, 15264.8f, 16967.3f,
  18859.8f, 20963.3f, 23301.4f, 25900.3f, 28789.0f, 32000.0f};


static const float kQRateTableSwb[24] =
{
  10000.0f, 11115.3f, 12355.1f, 13733.1f, 15264.8f, 16967.3f,
  18859.8f, 20963.3f, 23153.1f, 25342.9f, 27532.7f, 29722.5f,
  31912.3f, 34102.1f, 36291.9f, 38481.7f, 40671.4f, 42861.2f,
  45051.0f, 47240.8f, 49430.6f, 51620.4f, 53810.2f, 56000.0f,
};




int32_t WebRtcIsac_InitBandwidthEstimator(
    BwEstimatorstr*              bwest_str,
    enum IsacSamplingRate encoderSampRate,
    enum IsacSamplingRate decoderSampRate)
{
  switch(encoderSampRate)
  {
    case kIsacWideband:
      {
        bwest_str->send_bw_avg       = INIT_BN_EST_WB;
        break;
      }
    case kIsacSuperWideband:
      {
        bwest_str->send_bw_avg       = INIT_BN_EST_SWB;
        break;
      }
  }

  switch(decoderSampRate)
  {
    case kIsacWideband:
      {
        bwest_str->prev_frame_length = INIT_FRAME_LEN_WB;
        bwest_str->rec_bw_inv        = 1.0f /
            (INIT_BN_EST_WB + INIT_HDR_RATE_WB);
        bwest_str->rec_bw            = (int32_t)INIT_BN_EST_WB;
        bwest_str->rec_bw_avg_Q      = INIT_BN_EST_WB;
        bwest_str->rec_bw_avg        = INIT_BN_EST_WB + INIT_HDR_RATE_WB;
        bwest_str->rec_header_rate   = INIT_HDR_RATE_WB;
        break;
      }
    case kIsacSuperWideband:
      {
        bwest_str->prev_frame_length = INIT_FRAME_LEN_SWB;
        bwest_str->rec_bw_inv        = 1.0f /
            (INIT_BN_EST_SWB + INIT_HDR_RATE_SWB);
        bwest_str->rec_bw            = (int32_t)INIT_BN_EST_SWB;
        bwest_str->rec_bw_avg_Q      = INIT_BN_EST_SWB;
        bwest_str->rec_bw_avg        = INIT_BN_EST_SWB + INIT_HDR_RATE_SWB;
        bwest_str->rec_header_rate   = INIT_HDR_RATE_SWB;
        break;
      }
  }

  bwest_str->prev_rec_rtp_number       = 0;
  bwest_str->prev_rec_arr_ts           = 0;
  bwest_str->prev_rec_send_ts          = 0;
  bwest_str->prev_rec_rtp_rate         = 1.0f;
  bwest_str->last_update_ts            = 0;
  bwest_str->last_reduction_ts         = 0;
  bwest_str->count_tot_updates_rec     = -9;
  bwest_str->rec_jitter                = 10.0f;
  bwest_str->rec_jitter_short_term     = 0.0f;
  bwest_str->rec_jitter_short_term_abs = 5.0f;
  bwest_str->rec_max_delay             = 10.0f;
  bwest_str->rec_max_delay_avg_Q       = 10.0f;
  bwest_str->num_pkts_rec              = 0;

  bwest_str->send_max_delay_avg        = 10.0f;

  bwest_str->hsn_detect_rec = 0;

  bwest_str->num_consec_rec_pkts_over_30k = 0;

  bwest_str->hsn_detect_snd = 0;

  bwest_str->num_consec_snt_pkts_over_30k = 0;

  bwest_str->in_wait_period = 0;

  bwest_str->change_to_WB = 0;

  bwest_str->numConsecLatePkts = 0;
  bwest_str->consecLatency = 0;
  bwest_str->inWaitLatePkts = 0;
  bwest_str->senderTimestamp = 0;
  bwest_str->receiverTimestamp = 0;

  bwest_str->external_bw_info.in_use = 0;

  return 0;
}

/* This function updates both bottle neck rates                                                      */
/* Parameters:                                                                                       */
/* rtp_number    - value from RTP packet, from NetEq                                                 */
/* frame length  - length of signal frame in ms, from iSAC decoder                                   */
/* send_ts       - value in RTP header giving send time in samples                                     */
/* arr_ts        - value given by timeGetTime() time of arrival in samples of packet from NetEq      */
/* pksize        - size of packet in bytes, from NetEq                                               */
/* Index         - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */
/* returns 0 if everything went fine, -1 otherwise                                                   */
int16_t WebRtcIsac_UpdateBandwidthEstimator(
    BwEstimatorstr* bwest_str,
    const uint16_t rtp_number,
    const int32_t frame_length,
    const uint32_t send_ts,
    const uint32_t arr_ts,
    const size_t pksize
    /*,    const uint16_t Index*/)
{
  float weight = 0.0f;
  float curr_bw_inv = 0.0f;
  float rec_rtp_rate;
  float t_diff_proj;
  float arr_ts_diff;
  float send_ts_diff;
  float arr_time_noise;
  float arr_time_noise_abs;

  float delay_correction_factor = 1;
  float late_diff = 0.0f;
  int immediate_set = 0;
  int num_pkts_expected;

  RTC_DCHECK(!bwest_str->external_bw_info.in_use);

  // We have to adjust the header-rate if the first packet has a
  // frame-size different than the initialized value.
  if ( frame_length != bwest_str->prev_frame_length )
  {
    bwest_str->rec_header_rate = (float)HEADER_SIZE * 8.0f *
        1000.0f / (float)frame_length;     /* bits/s */
  }

  /* UPDATE ESTIMATES ON THIS SIDE */
  /* compute far-side transmission rate */
  rec_rtp_rate = ((float)pksize * 8.0f * 1000.0f / (float)frame_length) +
      bwest_str->rec_header_rate;
  // rec_rtp_rate packet bits/s + header bits/s

  /* check for timer wrap-around */
  if (arr_ts < bwest_str->prev_rec_arr_ts)
  {
    bwest_str->prev_rec_arr_ts   = arr_ts;
    bwest_str->last_update_ts    = arr_ts;
    bwest_str->last_reduction_ts = arr_ts + 3*FS;
    bwest_str->num_pkts_rec      = 0;

    /* store frame length */
    bwest_str->prev_frame_length = frame_length;

    /* store far-side transmission rate */
    bwest_str->prev_rec_rtp_rate = rec_rtp_rate;

    /* store far-side RTP time stamp */
    bwest_str->prev_rec_rtp_number = rtp_number;

    return 0;
  }

  bwest_str->num_pkts_rec++;

  /* check that it's not one of the first 9 packets */
  if ( bwest_str->count_tot_updates_rec > 0 )
  {
    if(bwest_str->in_wait_period > 0 )
    {
      bwest_str->in_wait_period--;
    }

    bwest_str->inWaitLatePkts -= ((bwest_str->inWaitLatePkts > 0)? 1:0);
    send_ts_diff = (float)(send_ts - bwest_str->prev_rec_send_ts);

    if (send_ts_diff <= (16 * frame_length)*2)
      //doesn't allow for a dropped packet, not sure necessary to be
      // that strict -DH
    {
      /* if not been updated for a long time, reduce the BN estimate */
      if((uint32_t)(arr_ts - bwest_str->last_update_ts) *
         1000.0f / FS > 3000)
      {
        //how many frames should have been received since the last
        // update if too many have been dropped or there have been
        // big delays won't allow this reduction may no longer need
        // the send_ts_diff here
        num_pkts_expected = (int)(((float)(arr_ts -
                                           bwest_str->last_update_ts) * 1000.0f /(float) FS) /
                                  (float)frame_length);

        if(((float)bwest_str->num_pkts_rec/(float)num_pkts_expected) >
           0.9)
        {
          float inv_bitrate = (float) pow( 0.99995,
                                           (double)((uint32_t)(arr_ts -
                                                                     bwest_str->last_reduction_ts)*1000.0f/FS) );

          if ( inv_bitrate )
          {
            bwest_str->rec_bw_inv /= inv_bitrate;

            //precautionary, likely never necessary
            if (bwest_str->hsn_detect_snd &&
                bwest_str->hsn_detect_rec)
            {
              if (bwest_str->rec_bw_inv > 0.000066f)
              {
                bwest_str->rec_bw_inv = 0.000066f;
              }
            }
          }
          else
          {
            bwest_str->rec_bw_inv = 1.0f /
                (INIT_BN_EST_WB + INIT_HDR_RATE_WB);
          }
          /* reset time-since-update counter */
          bwest_str->last_reduction_ts = arr_ts;
        }
        else
          //reset here?
        {
          bwest_str->last_reduction_ts = arr_ts + 3*FS;
          bwest_str->last_update_ts = arr_ts;
          bwest_str->num_pkts_rec = 0;
        }
      }
    }
    else
    {
      bwest_str->last_reduction_ts = arr_ts + 3*FS;
      bwest_str->last_update_ts = arr_ts;
      bwest_str->num_pkts_rec = 0;
    }


    /* temporarily speed up adaptation if frame length has changed */
    if ( frame_length != bwest_str->prev_frame_length )
    {
      bwest_str->count_tot_updates_rec = 10;
      bwest_str->rec_header_rate = (float)HEADER_SIZE * 8.0f *
          1000.0f / (float)frame_length;     /* bits/s */

      bwest_str->rec_bw_inv = 1.0f /((float)bwest_str->rec_bw +
                                     bwest_str->rec_header_rate);
    }

    ////////////////////////
    arr_ts_diff = (float)(arr_ts - bwest_str->prev_rec_arr_ts);

    if (send_ts_diff > 0 )
    {
      late_diff = arr_ts_diff - send_ts_diff;
    }
    else
    {
      late_diff = arr_ts_diff - (float)(16 * frame_length);
    }

    if((late_diff > 0) && !bwest_str->inWaitLatePkts)
    {
      bwest_str->numConsecLatePkts++;
      bwest_str->consecLatency += late_diff;
    }
    else
    {
      bwest_str->numConsecLatePkts = 0;
      bwest_str->consecLatency = 0;
    }
    if(bwest_str->numConsecLatePkts > 50)
    {
      float latencyMs = bwest_str->consecLatency/(FS/1000);
      float averageLatencyMs = latencyMs / bwest_str->numConsecLatePkts;
      delay_correction_factor = frame_length / (frame_length + averageLatencyMs);
      immediate_set = 1;
      bwest_str->inWaitLatePkts = (int16_t)((bwest_str->consecLatency/(FS/1000)) / 30);// + 150;
      bwest_str->start_wait_period = arr_ts;
    }
    ///////////////////////////////////////////////



    /*   update only if previous packet was not lost */
    if ( rtp_number == bwest_str->prev_rec_rtp_number + 1 )
    {


      if (!(bwest_str->hsn_detect_snd && bwest_str->hsn_detect_rec))
      {
        if ((arr_ts_diff > (float)(16 * frame_length)))
        {
          //1/2 second
          if ((late_diff > 8000.0f) && !bwest_str->in_wait_period)
          {
            delay_correction_factor = 0.7f;
            bwest_str->in_wait_period = 55;
            bwest_str->start_wait_period = arr_ts;
            immediate_set = 1;
          }
          //320 ms
          else if (late_diff > 5120.0f && !bwest_str->in_wait_period)
          {
            delay_correction_factor = 0.8f;
            immediate_set = 1;
            bwest_str->in_wait_period = 44;
            bwest_str->start_wait_period = arr_ts;
          }
        }
      }


      if ((bwest_str->prev_rec_rtp_rate > bwest_str->rec_bw_avg) &&
          (rec_rtp_rate > bwest_str->rec_bw_avg)                 &&
          !bwest_str->in_wait_period)
      {
        /* test if still in initiation period and increment counter */
        if (bwest_str->count_tot_updates_rec++ > 99)
        {
          /* constant weight after initiation part */
          weight = 0.01f;
        }
        else
        {
          /* weight decreases with number of updates */
          weight = 1.0f / (float) bwest_str->count_tot_updates_rec;
        }
        /* Bottle Neck Estimation */

        /* limit outliers */
        /* if more than 25 ms too much */
        if (arr_ts_diff > frame_length * FS/1000 + 400.0f)
        {
          // in samples,  why 25ms??
          arr_ts_diff = frame_length * FS/1000 + 400.0f;
        }
        if(arr_ts_diff < (frame_length * FS/1000) - 160.0f)
        {
          /* don't allow it to be less than frame rate - 10 ms */
          arr_ts_diff = (float)frame_length * FS/1000 - 160.0f;
        }

        /* compute inverse receiving rate for last packet */
        curr_bw_inv = arr_ts_diff / ((float)(pksize + HEADER_SIZE) *
                                     8.0f * FS); // (180+35)*8*16000 = 27.5 Mbit....


        if(curr_bw_inv <
           (1.0f / (MAX_ISAC_BW + bwest_str->rec_header_rate)))
        {
          // don't allow inv rate to be larger than MAX
          curr_bw_inv = (1.0f /
                         (MAX_ISAC_BW + bwest_str->rec_header_rate));
        }

        /* update bottle neck rate estimate */
        bwest_str->rec_bw_inv = weight * curr_bw_inv +
            (1.0f - weight) * bwest_str->rec_bw_inv;

        /* reset time-since-update counter */
        bwest_str->last_update_ts    = arr_ts;
        bwest_str->last_reduction_ts = arr_ts + 3 * FS;
        bwest_str->num_pkts_rec = 0;

        /* Jitter Estimation */
        /* projected difference between arrival times */
        t_diff_proj = ((float)(pksize + HEADER_SIZE) * 8.0f *
                       1000.0f) / bwest_str->rec_bw_avg;


        // difference between projected and actual
        //   arrival time differences
        arr_time_noise = (float)(arr_ts_diff*1000.0f/FS) -
            t_diff_proj;
        arr_time_noise_abs = (float) fabs( arr_time_noise );

        /* long term averaged absolute jitter */
        bwest_str->rec_jitter = weight * arr_time_noise_abs +
            (1.0f - weight) * bwest_str->rec_jitter;
        if (bwest_str->rec_jitter > 10.0f)
        {
          bwest_str->rec_jitter = 10.0f;
        }
        /* short term averaged absolute jitter */
        bwest_str->rec_jitter_short_term_abs = 0.05f *
            arr_time_noise_abs + 0.95f *
            bwest_str->rec_jitter_short_term_abs;

        /* short term averaged jitter */
        bwest_str->rec_jitter_short_term = 0.05f * arr_time_noise +
            0.95f * bwest_str->rec_jitter_short_term;
      }
    }
  }
  else
  {
    // reset time-since-update counter when
    // receiving the first 9 packets
    bwest_str->last_update_ts    = arr_ts;
    bwest_str->last_reduction_ts = arr_ts + 3*FS;
    bwest_str->num_pkts_rec = 0;

    bwest_str->count_tot_updates_rec++;
  }

  /* limit minimum bottle neck rate */
  if (bwest_str->rec_bw_inv > 1.0f / ((float)MIN_ISAC_BW +
                                      bwest_str->rec_header_rate))
  {
    bwest_str->rec_bw_inv = 1.0f / ((float)MIN_ISAC_BW +
                                    bwest_str->rec_header_rate);
  }

  // limit maximum bitrate
  if (bwest_str->rec_bw_inv < 1.0f / ((float)MAX_ISAC_BW +
                                      bwest_str->rec_header_rate))
  {
    bwest_str->rec_bw_inv = 1.0f / ((float)MAX_ISAC_BW +
                                    bwest_str->rec_header_rate);
  }

  /* store frame length */
  bwest_str->prev_frame_length = frame_length;

  /* store far-side transmission rate */
  bwest_str->prev_rec_rtp_rate = rec_rtp_rate;

  /* store far-side RTP time stamp */
  bwest_str->prev_rec_rtp_number = rtp_number;

  // Replace bwest_str->rec_max_delay by the new
  // value (atomic operation)
  bwest_str->rec_max_delay = 3.0f * bwest_str->rec_jitter;

  /* store send and arrival time stamp */
  bwest_str->prev_rec_arr_ts = arr_ts ;
  bwest_str->prev_rec_send_ts = send_ts;

  /* Replace bwest_str->rec_bw by the new value (atomic operation) */
  bwest_str->rec_bw = (int32_t)(1.0f / bwest_str->rec_bw_inv -
                                      bwest_str->rec_header_rate);

  if (immediate_set)
  {
    bwest_str->rec_bw = (int32_t) (delay_correction_factor *
                                         (float) bwest_str->rec_bw);

    if (bwest_str->rec_bw < (int32_t) MIN_ISAC_BW)
    {
      bwest_str->rec_bw = (int32_t) MIN_ISAC_BW;
    }

    bwest_str->rec_bw_avg = bwest_str->rec_bw +
        bwest_str->rec_header_rate;

    bwest_str->rec_bw_avg_Q = (float) bwest_str->rec_bw;

    bwest_str->rec_jitter_short_term = 0.0f;

    bwest_str->rec_bw_inv = 1.0f / (bwest_str->rec_bw +
                                    bwest_str->rec_header_rate);

    bwest_str->count_tot_updates_rec = 1;

    immediate_set = 0;
    bwest_str->consecLatency = 0;
    bwest_str->numConsecLatePkts = 0;
  }

  return 0;
}


/* This function updates the send bottle neck rate                                                   */
/* Index         - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */
/* returns 0 if everything went fine, -1 otherwise                                                   */
int16_t WebRtcIsac_UpdateUplinkBwImpl(
    BwEstimatorstr*           bwest_str,
    int16_t               index,
    enum IsacSamplingRate encoderSamplingFreq)
{
  RTC_DCHECK(!bwest_str->external_bw_info.in_use);

  if((index < 0) || (index > 23))
  {
    return -ISAC_RANGE_ERROR_BW_ESTIMATOR;
  }

  /* UPDATE ESTIMATES FROM OTHER SIDE */
  if(encoderSamplingFreq == kIsacWideband)
  {
    if(index > 11)
    {
      index -= 12;   
      /* compute the jitter estimate as decoded on the other side */
      bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
          0.1f * (float)MAX_ISAC_MD;
    }
    else
    {
      /* compute the jitter estimate as decoded on the other side */
      bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
          0.1f * (float)MIN_ISAC_MD;
    }

    /* compute the BN estimate as decoded on the other side */
    bwest_str->send_bw_avg = 0.9f * bwest_str->send_bw_avg +
        0.1f * kQRateTableWb[index];
  }
  else
  {
    /* compute the BN estimate as decoded on the other side */
    bwest_str->send_bw_avg = 0.9f * bwest_str->send_bw_avg +
        0.1f * kQRateTableSwb[index];
  }

  if (bwest_str->send_bw_avg > (float) 28000 && !bwest_str->hsn_detect_snd)
  {
    bwest_str->num_consec_snt_pkts_over_30k++;

    if (bwest_str->num_consec_snt_pkts_over_30k >= 66)
    {
      //approx 2 seconds with 30ms frames
      bwest_str->hsn_detect_snd = 1;
    }
  }
  else if (!bwest_str->hsn_detect_snd)
  {
    bwest_str->num_consec_snt_pkts_over_30k = 0;
  }
  return 0;
}

// called when there is upper-band bit-stream to update jitter
// statistics.
int16_t WebRtcIsac_UpdateUplinkJitter(
    BwEstimatorstr*              bwest_str,
    int32_t                  index)
{
  RTC_DCHECK(!bwest_str->external_bw_info.in_use);

  if((index < 0) || (index > 23))
  {
    return -ISAC_RANGE_ERROR_BW_ESTIMATOR;
  }

  if(index > 0)
  {
    /* compute the jitter estimate as decoded on the other side */
    bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
        0.1f * (float)MAX_ISAC_MD;
  }
  else
  {
    /* compute the jitter estimate as decoded on the other side */
    bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
        0.1f * (float)MIN_ISAC_MD;
  }

  return 0;
}



// Returns the bandwidth/jitter estimation code (integer 0...23)
// to put in the sending iSAC payload
void
WebRtcIsac_GetDownlinkBwJitIndexImpl(
    BwEstimatorstr*           bwest_str,
    int16_t*              bottleneckIndex,
    int16_t*              jitterInfo,
    enum IsacSamplingRate decoderSamplingFreq)
{
  float MaxDelay;
  //uint16_t MaxDelayBit;

  float rate;
  float r;
  float e1, e2;
  const float weight = 0.1f;
  const float* ptrQuantizationTable;
  int16_t addJitterInfo;
  int16_t minInd;
  int16_t maxInd;
  int16_t midInd;

  if (bwest_str->external_bw_info.in_use) {
    *bottleneckIndex = bwest_str->external_bw_info.bottleneck_idx;
    *jitterInfo = bwest_str->external_bw_info.jitter_info;
    return;
  }

  /* Get Max Delay Bit */
  /* get unquantized max delay */
  MaxDelay = (float)WebRtcIsac_GetDownlinkMaxDelay(bwest_str);

  if ( ((1.f - weight) * bwest_str->rec_max_delay_avg_Q + weight *
        MAX_ISAC_MD - MaxDelay) > (MaxDelay - (1.f-weight) *
                                   bwest_str->rec_max_delay_avg_Q - weight * MIN_ISAC_MD) )
  {
    jitterInfo[0] = 0;
    /* update quantized average */
    bwest_str->rec_max_delay_avg_Q =
        (1.f - weight) * bwest_str->rec_max_delay_avg_Q + weight *
        (float)MIN_ISAC_MD;
  }
  else
  {
    jitterInfo[0] = 1;
    /* update quantized average */
    bwest_str->rec_max_delay_avg_Q =
        (1.f-weight) * bwest_str->rec_max_delay_avg_Q + weight *
        (float)MAX_ISAC_MD;
  }

  // Get unquantized rate.
  rate = (float)WebRtcIsac_GetDownlinkBandwidth(bwest_str);

  /* Get Rate Index */
  if(decoderSamplingFreq == kIsacWideband)
  {
    ptrQuantizationTable = kQRateTableWb;
    addJitterInfo = 1;
    maxInd = 11;
  }
  else
  {
    ptrQuantizationTable = kQRateTableSwb;
    addJitterInfo = 0;
    maxInd = 23;
  }

  minInd = 0;
  while(maxInd > minInd + 1)
  {
    midInd = (maxInd + minInd) >> 1;
    if(rate > ptrQuantizationTable[midInd])
    {
      minInd = midInd;
    }
    else
    {
      maxInd = midInd;
    }
  }
  // Chose the index which gives results an average which is closest
  // to rate
  r = (1 - weight) * bwest_str->rec_bw_avg_Q - rate;
  e1 = weight * ptrQuantizationTable[minInd] + r;
  e2 = weight * ptrQuantizationTable[maxInd] + r;
  e1 = (e1 > 0)? e1:-e1;
  e2 = (e2 > 0)? e2:-e2;
  if(e1 < e2)
  {
    bottleneckIndex[0] = minInd;
  }
  else
  {
    bottleneckIndex[0] = maxInd;
  }

  bwest_str->rec_bw_avg_Q = (1 - weight) * bwest_str->rec_bw_avg_Q +
      weight * ptrQuantizationTable[bottleneckIndex[0]];
  bottleneckIndex[0] += jitterInfo[0] * 12 * addJitterInfo;

  bwest_str->rec_bw_avg = (1 - weight) * bwest_str->rec_bw_avg + weight *
      (rate + bwest_str->rec_header_rate);
}



/* get the bottle neck rate from far side to here, as estimated on this side */
int32_t WebRtcIsac_GetDownlinkBandwidth( const BwEstimatorstr *bwest_str)
{
  int32_t  rec_bw;
  float   jitter_sign;
  float   bw_adjust;

  RTC_DCHECK(!bwest_str->external_bw_info.in_use);

  /* create a value between -1.0 and 1.0 indicating "average sign" of jitter */
  jitter_sign = bwest_str->rec_jitter_short_term /
      bwest_str->rec_jitter_short_term_abs;

  /* adjust bw proportionally to negative average jitter sign */
  bw_adjust = 1.0f - jitter_sign * (0.15f + 0.15f * jitter_sign * jitter_sign);

  /* adjust Rate if jitter sign is mostly constant */
  rec_bw = (int32_t)(bwest_str->rec_bw * bw_adjust);

  /* limit range of bottle neck rate */
  if (rec_bw < MIN_ISAC_BW)
  {
    rec_bw = MIN_ISAC_BW;
  }
  else if (rec_bw > MAX_ISAC_BW)
  {
    rec_bw = MAX_ISAC_BW;
  }
  return rec_bw;
}

/* Returns the max delay (in ms) */
int32_t
WebRtcIsac_GetDownlinkMaxDelay(const BwEstimatorstr *bwest_str)
{
  int32_t rec_max_delay;

  RTC_DCHECK(!bwest_str->external_bw_info.in_use);

  rec_max_delay = (int32_t)(bwest_str->rec_max_delay);

  /* limit range of jitter estimate */
  if (rec_max_delay < MIN_ISAC_MD)
  {
    rec_max_delay = MIN_ISAC_MD;
  }
  else if (rec_max_delay > MAX_ISAC_MD)
  {
    rec_max_delay = MAX_ISAC_MD;
  }
  return rec_max_delay;
}

/* Clamp val to the closed interval [min,max]. */
static int32_t clamp(int32_t val, int32_t min, int32_t max) {
  RTC_DCHECK_LE(min, max);
  return val < min ? min : (val > max ? max : val);
}

int32_t WebRtcIsac_GetUplinkBandwidth(const BwEstimatorstr* bwest_str) {
  return bwest_str->external_bw_info.in_use
             ? bwest_str->external_bw_info.send_bw_avg
             : clamp(bwest_str->send_bw_avg, MIN_ISAC_BW, MAX_ISAC_BW);
}

int32_t WebRtcIsac_GetUplinkMaxDelay(const BwEstimatorstr* bwest_str) {
  return bwest_str->external_bw_info.in_use
             ? bwest_str->external_bw_info.send_max_delay_avg
             : clamp(bwest_str->send_max_delay_avg, MIN_ISAC_MD, MAX_ISAC_MD);
}

void WebRtcIsacBw_GetBandwidthInfo(BwEstimatorstr* bwest_str,
                                   enum IsacSamplingRate decoder_sample_rate_hz,
                                   IsacBandwidthInfo* bwinfo) {
  RTC_DCHECK(!bwest_str->external_bw_info.in_use);
  bwinfo->in_use = 1;
  bwinfo->send_bw_avg = WebRtcIsac_GetUplinkBandwidth(bwest_str);
  bwinfo->send_max_delay_avg = WebRtcIsac_GetUplinkMaxDelay(bwest_str);
  WebRtcIsac_GetDownlinkBwJitIndexImpl(bwest_str, &bwinfo->bottleneck_idx,
                                       &bwinfo->jitter_info,
                                       decoder_sample_rate_hz);
}

void WebRtcIsacBw_SetBandwidthInfo(BwEstimatorstr* bwest_str,
                                   const IsacBandwidthInfo* bwinfo) {
  memcpy(&bwest_str->external_bw_info, bwinfo,
         sizeof bwest_str->external_bw_info);
}

/*
 * update long-term average bitrate and amount of data in buffer
 * returns minimum payload size (bytes)
 */
int WebRtcIsac_GetMinBytes(
    RateModel*         State,
    int                StreamSize,    /* bytes in bitstream */
    const int          FrameSamples,  /* samples per frame */
    const double       BottleNeck,    /* bottle neck rate; excl headers (bps) */
    const double       DelayBuildUp,  /* max delay from bottleneck buffering (ms) */
    enum ISACBandwidth bandwidth
    /*,int16_t        frequentLargePackets*/)
{
  double MinRate = 0.0;
  int    MinBytes;
  double TransmissionTime;
  int    burstInterval = BURST_INTERVAL;

  // first 10 packets @ low rate, then INIT_BURST_LEN packets @
  // fixed rate of INIT_RATE bps
  if (State->InitCounter > 0)
  {
    if (State->InitCounter-- <= INIT_BURST_LEN)
    {
      if(bandwidth == isac8kHz)
      {
        MinRate = INIT_RATE_WB;
      }
      else
      {
        MinRate = INIT_RATE_SWB;
      }
    }
    else
    {
      MinRate = 0;
    }
  }
  else
  {
    /* handle burst */
    if (State->BurstCounter)
    {
      if (State->StillBuffered < (1.0 - 1.0/BURST_LEN) * DelayBuildUp)
      {
        /* max bps derived from BottleNeck and DelayBuildUp values */
        MinRate = (1.0 + (FS/1000) * DelayBuildUp /
                   (double)(BURST_LEN * FrameSamples)) * BottleNeck;
      }
      else
      {
        // max bps derived from StillBuffered and DelayBuildUp
        // values
        MinRate = (1.0 + (FS/1000) * (DelayBuildUp -
                                      State->StillBuffered) / (double)FrameSamples) * BottleNeck;
        if (MinRate < 1.04 * BottleNeck)
        {
          MinRate = 1.04 * BottleNeck;
        }
      }
      State->BurstCounter--;
    }
  }


  /* convert rate from bits/second to bytes/packet */
  MinBytes = (int) (MinRate * FrameSamples / (8.0 * FS));

  /* StreamSize will be adjusted if less than MinBytes */
  if (StreamSize < MinBytes)
  {
    StreamSize = MinBytes;
  }

  /* keep track of when bottle neck was last exceeded by at least 1% */
  if (StreamSize * 8.0 * FS / FrameSamples > 1.01 * BottleNeck) {
    if (State->PrevExceed) {
      /* bottle_neck exceded twice in a row, decrease ExceedAgo */
      State->ExceedAgo -= /*BURST_INTERVAL*/ burstInterval / (BURST_LEN - 1);
      if (State->ExceedAgo < 0)
        State->ExceedAgo = 0;
    }
    else
    {
      State->ExceedAgo += (FrameSamples * 1000) / FS; /* ms */
      State->PrevExceed = 1;
    }
  }
  else
  {
    State->PrevExceed = 0;
    State->ExceedAgo += (FrameSamples * 1000) / FS;     /* ms */
  }

  /* set burst flag if bottle neck not exceeded for long time */
  if ((State->ExceedAgo > burstInterval) &&
      (State->BurstCounter == 0))
  {
    if (State->PrevExceed)
    {
      State->BurstCounter = BURST_LEN - 1;
    }
    else
    {
      State->BurstCounter = BURST_LEN;
    }
  }


  /* Update buffer delay */
  TransmissionTime = StreamSize * 8.0 * 1000.0 / BottleNeck;  /* ms */
  State->StillBuffered += TransmissionTime;
  State->StillBuffered -= (FrameSamples * 1000) / FS;     /* ms */
  if (State->StillBuffered < 0.0)
  {
    State->StillBuffered = 0.0;
  }

  return MinBytes;
}


/*
 * update long-term average bitrate and amount of data in buffer
 */
void WebRtcIsac_UpdateRateModel(
    RateModel *State,
    int StreamSize,                    /* bytes in bitstream */
    const int FrameSamples,            /* samples per frame */
    const double BottleNeck)        /* bottle neck rate; excl headers (bps) */
{
  double TransmissionTime;

  /* avoid the initial "high-rate" burst */
  State->InitCounter = 0;

  /* Update buffer delay */
  TransmissionTime = StreamSize * 8.0 * 1000.0 / BottleNeck;  /* ms */
  State->StillBuffered += TransmissionTime;
  State->StillBuffered -= (FrameSamples * 1000) / FS;     /* ms */
  if (State->StillBuffered < 0.0)
    State->StillBuffered = 0.0;

}


void WebRtcIsac_InitRateModel(
    RateModel *State)
{
  State->PrevExceed      = 0;                        /* boolean */
  State->ExceedAgo       = 0;                        /* ms */
  State->BurstCounter    = 0;                        /* packets */
  State->InitCounter     = INIT_BURST_LEN + 10;    /* packets */
  State->StillBuffered   = 1.0;                    /* ms */
}

int WebRtcIsac_GetNewFrameLength(
    double bottle_neck,
    int    current_framesamples)
{
  int new_framesamples;

  const int Thld_20_30 = 20000;

  //const int Thld_30_20 = 30000;
  const int Thld_30_20 = 1000000;   // disable 20 ms frames

  const int Thld_30_60 = 18000;
  //const int Thld_30_60 = 0;      // disable 60 ms frames

  const int Thld_60_30 = 27000;


  new_framesamples = current_framesamples;

  /* find new framelength */
  switch(current_framesamples) {
    case 320:
      if (bottle_neck < Thld_20_30)
        new_framesamples = 480;
      break;
    case 480:
      if (bottle_neck < Thld_30_60)
        new_framesamples = 960;
      else if (bottle_neck > Thld_30_20)
        new_framesamples = 320;
      break;
    case 960:
      if (bottle_neck >= Thld_60_30)
        new_framesamples = 480;
      break;
  }

  return new_framesamples;
}

double WebRtcIsac_GetSnr(
    double bottle_neck,
    int    framesamples)
{
  double s2nr;

  const double a_20 = -30.0;
  const double b_20 = 0.8;
  const double c_20 = 0.0;

  const double a_30 = -23.0;
  const double b_30 = 0.48;
  const double c_30 = 0.0;

  const double a_60 = -23.0;
  const double b_60 = 0.53;
  const double c_60 = 0.0;


  /* find new SNR value */
  switch(framesamples) {
    case 320:
      s2nr = a_20 + b_20 * bottle_neck * 0.001 + c_20 * bottle_neck *
          bottle_neck * 0.000001;
      break;
    case 480:
      s2nr = a_30 + b_30 * bottle_neck * 0.001 + c_30 * bottle_neck *
          bottle_neck * 0.000001;
      break;
    case 960:
      s2nr = a_60 + b_60 * bottle_neck * 0.001 + c_60 * bottle_neck *
          bottle_neck * 0.000001;
      break;
    default:
      s2nr = 0;
  }

  return s2nr;

}
