/*
 *  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.
 */

#include "webrtc/voice_engine/voe_call_report_impl.h"

#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/file_wrapper.h"
#include "webrtc/system_wrappers/interface/trace.h"
#include "webrtc/voice_engine/channel.h"
#include "webrtc/voice_engine/include/voe_errors.h"
#include "webrtc/voice_engine/voice_engine_impl.h"

namespace webrtc
{

VoECallReport* VoECallReport::GetInterface(VoiceEngine* voiceEngine)
{
#ifndef WEBRTC_VOICE_ENGINE_CALL_REPORT_API
    return NULL;
#else
    if (NULL == voiceEngine)
    {
        return NULL;
    }
    VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
    s->AddRef();
    return s;
#endif
}

#ifdef WEBRTC_VOICE_ENGINE_CALL_REPORT_API

VoECallReportImpl::VoECallReportImpl(voe::SharedData* shared) :
    _file(*FileWrapper::Create()), _shared(shared)
{
    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "VoECallReportImpl() - ctor");
}

VoECallReportImpl::~VoECallReportImpl()
{
    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "~VoECallReportImpl() - dtor");
    delete &_file;
}

int VoECallReportImpl::ResetCallReportStatistics(int channel)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "ResetCallReportStatistics(channel=%d)", channel);
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    assert(_shared->audio_processing() != NULL);

    bool echoMode =
        _shared->audio_processing()->echo_cancellation()->are_metrics_enabled();

    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "  current AudioProcessingModule echo metric state %d)",
                 echoMode);
    // Reset the APM statistics
    if (_shared->audio_processing()->echo_cancellation()->enable_metrics(true)
        != 0)
    {
        _shared->SetLastError(VE_APM_ERROR, kTraceError,
            "ResetCallReportStatistics() unable to "
            "set the AudioProcessingModule echo metrics state");
        return -1;
    }
    // Restore metric states
    _shared->audio_processing()->echo_cancellation()->enable_metrics(echoMode);

    // Reset channel dependent statistics
    if (channel != -1)
    {
        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
        voe::Channel* channelPtr = ch.channel();
        if (channelPtr == NULL)
        {
            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                "ResetCallReportStatistics() failed to locate channel");
            return -1;
        }
        channelPtr->ResetDeadOrAliveCounters();
        channelPtr->ResetRTCPStatistics();
    } else {
      for (voe::ChannelManager::Iterator it(&_shared->channel_manager());
           it.IsValid();
           it.Increment()) {
        it.GetChannel()->ResetDeadOrAliveCounters();
        it.GetChannel()->ResetRTCPStatistics();
      }
    }

    return 0;
}

int VoECallReportImpl::GetEchoMetricSummary(EchoStatistics& stats)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetEchoMetricSummary()");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    assert(_shared->audio_processing() != NULL);

    return (GetEchoMetricSummaryInternal(stats));
}

int VoECallReportImpl::GetEchoMetricSummaryInternal(EchoStatistics& stats)
{
    // Retrieve echo metrics from the AudioProcessingModule
    int ret(0);
    bool mode(false);
    EchoCancellation::Metrics metrics;

    // Ensure that echo metrics is enabled

    mode =
        _shared->audio_processing()->echo_cancellation()->are_metrics_enabled();
    if (mode != false)
    {
        ret = _shared->audio_processing()->echo_cancellation()->
              GetMetrics(&metrics);
        if (ret != 0)
        {
            WEBRTC_TRACE(kTraceWarning, kTraceVoice,
                VoEId(_shared->instance_id(), -1),
                "  AudioProcessingModule GetMetrics() => error");
        }
    }
    else
    {
        WEBRTC_TRACE(kTraceWarning, kTraceVoice,
            VoEId(_shared->instance_id(), -1),
            "  AudioProcessingModule echo metrics is not enabled");
    }

    if ((ret != 0) || (mode == false))
    {
        // Mark complete struct as invalid (-100 dB)
        WEBRTC_TRACE(kTraceWarning, kTraceVoice,
            VoEId(_shared->instance_id(), -1),
            "  unable to retrieve echo metrics from the AudioProcessingModule");
        stats.erl.min = -100;
        stats.erl.max = -100;
        stats.erl.average = -100;
        stats.erle.min = -100;
        stats.erle.max = -100;
        stats.erle.average = -100;
        stats.rerl.min = -100;
        stats.rerl.max = -100;
        stats.rerl.average = -100;
        stats.a_nlp.min = -100;
        stats.a_nlp.max = -100;
        stats.a_nlp.average = -100;
    }
    else
    {

        // Deliver output results to user
        stats.erl.min = metrics.echo_return_loss.minimum;
        stats.erl.max = metrics.echo_return_loss.maximum;
        stats.erl.average = metrics.echo_return_loss.average;
        WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
            VoEId(_shared->instance_id(), -1), "  erl: min=%d, max=%d, avg=%d",
            stats.erl.min, stats.erl.max, stats.erl.average);

        stats.erle.min = metrics.echo_return_loss_enhancement.minimum;
        stats.erle.max = metrics.echo_return_loss_enhancement.maximum;
        stats.erle.average = metrics.echo_return_loss_enhancement.average;
        WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
            VoEId(_shared->instance_id(), -1), "  erle: min=%d, max=%d, avg=%d",
            stats.erle.min, stats.erle.max, stats.erle.average);

        stats.rerl.min = metrics.residual_echo_return_loss.minimum;
        stats.rerl.max = metrics.residual_echo_return_loss.maximum;
        stats.rerl.average = metrics.residual_echo_return_loss.average;
        WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
            VoEId(_shared->instance_id(), -1), "  rerl: min=%d, max=%d, avg=%d",
            stats.rerl.min, stats.rerl.max, stats.rerl.average);

        stats.a_nlp.min = metrics.a_nlp.minimum;
        stats.a_nlp.max = metrics.a_nlp.maximum;
        stats.a_nlp.average = metrics.a_nlp.average;
        WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
            VoEId(_shared->instance_id(), -1),
            "  a_nlp: min=%d, max=%d, avg=%d",
            stats.a_nlp.min, stats.a_nlp.max, stats.a_nlp.average);
    }
    return 0;
}

int VoECallReportImpl::GetRoundTripTimeSummary(int channel, StatVal& delaysMs)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetRoundTripTimeSummary()");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
    voe::Channel* channelPtr = ch.channel();
    if (channelPtr == NULL)
    {
        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
            "GetRoundTripTimeSummary() failed to locate channel");
        return -1;
    }

    return channelPtr->GetRoundTripTimeSummary(delaysMs);
}

int VoECallReportImpl::GetDeadOrAliveSummary(int channel,
                                             int& numOfDeadDetections,
                                             int& numOfAliveDetections)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetDeadOrAliveSummary(channel=%d)", channel);
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    return (GetDeadOrAliveSummaryInternal(channel, numOfDeadDetections,
                                          numOfAliveDetections));
}

int VoECallReportImpl::GetDeadOrAliveSummaryInternal(int channel,
                                                     int& numOfDeadDetections,
                                                     int& numOfAliveDetections)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetDeadOrAliveSummary(channel=%d)", channel);

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
    voe::Channel* channelPtr = ch.channel();
    if (channelPtr == NULL)
    {
        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
            "GetRoundTripTimeSummary() failed to locate channel");
        return -1;
    }

    return channelPtr->GetDeadOrAliveCounters(numOfDeadDetections,
                                              numOfAliveDetections);
}

int VoECallReportImpl::WriteReportToFile(const char* fileNameUTF8)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "WriteReportToFile(fileNameUTF8=%s)", fileNameUTF8);
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    if (NULL == fileNameUTF8)
    {
        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
            "WriteReportToFile() invalid filename");
        return -1;
    }

    if (_file.Open())
    {
        _file.CloseFile();
    }

    // Open text file in write mode
    if (_file.OpenFile(fileNameUTF8, false, false, true) != 0)
    {
        _shared->SetLastError(VE_BAD_FILE, kTraceError,
            "WriteReportToFile() unable to open the file");
        return -1;
    }

    // Summarize information and add it to the open file
    //
    _file.WriteText("WebRtc VoiceEngine Call Report\n");
    _file.WriteText("==============================\n");
    _file.WriteText("\nNetwork Packet Round Trip Time (RTT)\n");
    _file.WriteText("------------------------------------\n\n");

    if (_shared->channel_manager().NumOfChannels() == 0)
      return 0;

    for (voe::ChannelManager::Iterator it(&_shared->channel_manager());
         it.IsValid();
         it.Increment()) {
      StatVal delaysMs;
      _file.WriteText("channel %d:\n", it.GetChannel()->ChannelId());
      it.GetChannel()->GetRoundTripTimeSummary(delaysMs);
      _file.WriteText("  min:%5d [ms]\n", delaysMs.min);
      _file.WriteText("  max:%5d [ms]\n", delaysMs.max);
      _file.WriteText("  avg:%5d [ms]\n", delaysMs.average);
    }

    _file.WriteText("\nDead-or-Alive Connection Detections\n");
    _file.WriteText("------------------------------------\n\n");

    for (voe::ChannelManager::Iterator it(&_shared->channel_manager());
         it.IsValid();
         it.Increment()) {
      int dead = 0;
      int alive = 0;
      _file.WriteText("channel %d:\n", it.GetChannel()->ChannelId());
      GetDeadOrAliveSummary(it.GetChannel()->ChannelId(), dead, alive);
      _file.WriteText("  #dead :%6d\n", dead);
      _file.WriteText("  #alive:%6d\n", alive);
    }

    EchoStatistics echo;
    GetEchoMetricSummary(echo);

    _file.WriteText("\nEcho Metrics\n");
    _file.WriteText("------------\n\n");

    _file.WriteText("erl:\n");
    _file.WriteText("  min:%5d [dB]\n", echo.erl.min);
    _file.WriteText("  max:%5d [dB]\n", echo.erl.max);
    _file.WriteText("  avg:%5d [dB]\n", echo.erl.average);
    _file.WriteText("\nerle:\n");
    _file.WriteText("  min:%5d [dB]\n", echo.erle.min);
    _file.WriteText("  max:%5d [dB]\n", echo.erle.max);
    _file.WriteText("  avg:%5d [dB]\n", echo.erle.average);
    _file.WriteText("rerl:\n");
    _file.WriteText("  min:%5d [dB]\n", echo.rerl.min);
    _file.WriteText("  max:%5d [dB]\n", echo.rerl.max);
    _file.WriteText("  avg:%5d [dB]\n", echo.rerl.average);
    _file.WriteText("a_nlp:\n");
    _file.WriteText("  min:%5d [dB]\n", echo.a_nlp.min);
    _file.WriteText("  max:%5d [dB]\n", echo.a_nlp.max);
    _file.WriteText("  avg:%5d [dB]\n", echo.a_nlp.average);

    _file.WriteText("\n<END>");

    _file.Flush();
    _file.CloseFile();

    return 0;
}

#endif  // WEBRTC_VOICE_ENGINE_CALL_REPORT_API

}  // namespace webrtc
