/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "audio_player.h"

#include "audio_control_manager.h"
#include "audio_system_manager.h"
#include "call_manager_errors.h"
#include "telephony_log_wrapper.h"

namespace OHOS {
namespace Telephony {
bool AudioPlayer::InitRenderer(const wav_hdr &wavHeader, AudioStandard::AudioStreamType streamType)
{
    AudioStandard::AudioRendererParams rendererParams;
    rendererParams.sampleFormat = AudioStandard::AudioSampleFormat::SAMPLE_S16LE;
    rendererParams.sampleRate = static_cast<AudioStandard::AudioSamplingRate>(wavHeader.SamplesPerSec);
    rendererParams.channelCount = static_cast<AudioStandard::AudioChannel>(wavHeader.NumOfChan);
    rendererParams.encodingType = static_cast<AudioStandard::AudioEncodingType>(AudioStandard::ENCODING_PCM);
    audioRenderer_ = AudioStandard::AudioRenderer::Create(streamType);
    if (audioRenderer_ == nullptr) {
        TELEPHONY_LOGE("audio renderer create failed");
        return false;
    }
    if (audioRenderer_->SetParams(rendererParams) != TELEPHONY_SUCCESS) {
        TELEPHONY_LOGE("audio renderer set params failed");
        return false;
    }
    callback_ = std::make_shared<CallAudioRendererCallback>();
    if (callback_ == nullptr) {
        TELEPHONY_LOGE("callback_ is nullptr");
        return false;
    }
    audioRenderer_->SetRendererCallback(callback_);
    if (!audioRenderer_->Start()) {
        TELEPHONY_LOGE("audio renderer start failed");
        return false;
    }
    if (audioRenderer_->GetBufferSize(bufferLen)) {
        TELEPHONY_LOGE("audio renderer get buffer size failed");
        return false;
    }
    uint32_t frameCount;
    if (audioRenderer_->GetFrameCount(frameCount)) {
        TELEPHONY_LOGE("audio renderer get frame count failed");
        return false;
    }
    isRenderInitialized_ = true;
    return true;
}

bool AudioPlayer::InitRenderer()
{
    AudioStandard::AudioRendererOptions rendererOptions;
    rendererOptions.streamInfo.samplingRate = AudioStandard::AudioSamplingRate::SAMPLE_RATE_96000;
    rendererOptions.streamInfo.encoding = AudioStandard::AudioEncodingType::ENCODING_PCM;
    rendererOptions.streamInfo.format = AudioStandard::AudioSampleFormat::SAMPLE_U8;
    rendererOptions.streamInfo.channels = AudioStandard::AudioChannel::MONO;
    rendererOptions.rendererInfo.contentType = AudioStandard::ContentType::CONTENT_TYPE_SPEECH;
    rendererOptions.rendererInfo.streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_VOICE_MODEM_COMMUNICATION;
    rendererOptions.rendererInfo.rendererFlags = RENDERER_FLAG;
    audioRenderer_ = AudioStandard::AudioRenderer::Create(rendererOptions);
    if (audioRenderer_ == nullptr) {
        TELEPHONY_LOGE("audio renderer create failed");
        return false;
    }
    if (!audioRenderer_->Start()) {
        TELEPHONY_LOGE("audio renderer start failed");
        return false;
    }
    uint32_t frameCount;
    if (audioRenderer_->GetFrameCount(frameCount)) {
        TELEPHONY_LOGE("audio renderer get frame count failed");
        return false;
    }
    isRenderInitialized_ = true;
    return true;
}

bool AudioPlayer::InitCapturer()
{
    AudioStandard::AudioCapturerOptions capturerOptions;
    capturerOptions.streamInfo.samplingRate = AudioStandard::AudioSamplingRate::SAMPLE_RATE_96000;
    capturerOptions.streamInfo.encoding = AudioStandard::AudioEncodingType::ENCODING_PCM;
    capturerOptions.streamInfo.format = AudioStandard::AudioSampleFormat::SAMPLE_U8;
    capturerOptions.streamInfo.channels = AudioStandard::AudioChannel::MONO;
    capturerOptions.capturerInfo.sourceType = AudioStandard::SourceType::SOURCE_TYPE_VIRTUAL_CAPTURE;
    capturerOptions.capturerInfo.capturerFlags = CAPTURER_FLAG;
    audioCapturer_ = AudioStandard::AudioCapturer::Create(capturerOptions);
    if (audioCapturer_ == nullptr) {
        TELEPHONY_LOGE("audio capturer create failed");
        return false;
    }
    if (!audioCapturer_->Start()) {
        TELEPHONY_LOGE("audio capturer start failed");
        return false;
    }
    uint32_t frameCount;
    if (audioCapturer_->GetFrameCount(frameCount)) {
        TELEPHONY_LOGE("audio capturer get frame count failed");
        return false;
    }
    isCapturerInitialized_ = true;
    return true;
}

int32_t AudioPlayer::Play(const std::string &path, AudioStandard::AudioStreamType streamType, PlayerType playerType)
{
    wav_hdr wavHeader;
    std::string realPath = "";
    if (!GetRealPath(path, realPath) || realPath.empty()) {
        TELEPHONY_LOGE("path or realPath is NULL");
        return TELEPHONY_ERR_ARGUMENT_INVALID;
    }
    FILE *wavFile = fopen(realPath.c_str(), "rb");
    if (wavFile == nullptr) {
        TELEPHONY_LOGE("open audio file failed");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    (void)fread(&wavHeader, READ_SIZE, sizeof(wav_hdr), wavFile);
    SetStop(playerType, false);
    if (!InitRenderer(wavHeader, streamType)) {
        TELEPHONY_LOGE("audio renderer and capturer init failed");
        (void)fclose(wavFile);
        return TELEPHONY_ERR_UNINIT;
    }
    uint8_t *buffer = (uint8_t *)malloc(bufferLen + bufferLen);
    if (buffer == nullptr) {
        TELEPHONY_LOGE("audio malloc buffer failed");
        (void)fclose(wavFile);
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    TELEPHONY_LOGI("start audio rendering");
    StartPlayLoop(wavFile, wavHeader, buffer, playerType);
    free(buffer);
    (void)fclose(wavFile);
    TELEPHONY_LOGI("audio renderer playback done");
    return TELEPHONY_SUCCESS;
}

void AudioPlayer::StartPlayLoop(FILE *wavFile, wav_hdr wavHeader, uint8_t *buffer, PlayerType playerType)
{
    size_t bytesToWrite = 0;
    size_t bytesWritten = 0;
    while (!isStop_) {
        if (IsStop(playerType)) {
            break;
        } else if (feof(wavFile)) {
            if (fseek(wavFile, 0, SEEK_SET) != 0) {
                break;
            }
            fread(&wavHeader, READ_SIZE, sizeof(wav_hdr), wavFile); // skip the wav header
        }
        bytesToWrite = fread(buffer, READ_SIZE, bufferLen, wavFile);
        bytesWritten = 0;
        while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > MIN_BYTES)) {
            if (IsStop(playerType)) {
                break;
            }
            bytesWritten += static_cast<size_t>(
                    audioRenderer_->Write(buffer + bytesWritten, bytesToWrite - bytesWritten));
        }
    }
}

int32_t AudioPlayer::Play(PlayerType playerType)
{
    SetStop(playerType, false);
    if (!InitRenderer() || !InitCapturer()) {
        TELEPHONY_LOGE("audio renderer and capturer init failed");
        return TELEPHONY_ERR_UNINIT;
    }
    return TELEPHONY_SUCCESS;
}

void AudioPlayer::SetStop(PlayerType playerType, bool state)
{
    switch (playerType) {
        case PlayerType::TYPE_RING:
            isRingStop_ = state;
            if (isRingStop_) {
                DelayedSingleton<AudioControlManager>::GetInstance()->SetRingState(RingState::STOPPED);
            } else {
                DelayedSingleton<AudioControlManager>::GetInstance()->SetRingState(RingState::RINGING);
            }
            break;
        case PlayerType::TYPE_TONE:
            isToneStop_ = state;
            if (isToneStop_) {
                DelayedSingleton<AudioControlManager>::GetInstance()->SetToneState(ToneState::STOPPED);
            } else {
                DelayedSingleton<AudioControlManager>::GetInstance()->SetToneState(ToneState::TONEING);
            }
            break;
        case PlayerType::TYPE_SOUND:
            isSoundStop_ = state;
            if (isSoundStop_) {
                DelayedSingleton<AudioControlManager>::GetInstance()->SetSoundState(SoundState::STOPPED);
            } else {
                DelayedSingleton<AudioControlManager>::GetInstance()->SetSoundState(SoundState::SOUNDING);
            }
            break;
        default:
            break;
    }
}

bool AudioPlayer::IsStop(PlayerType playerType)
{
    bool ret = false;
    switch (playerType) {
        case PlayerType::TYPE_RING:
            ret = isRingStop_;
            break;
        case PlayerType::TYPE_TONE:
            ret = isToneStop_;
            break;
        case PlayerType::TYPE_SOUND:
            ret = isSoundStop_;
            break;
        default:
            break;
    }
    return ret;
}

void AudioPlayer::ReleaseRenderer()
{
    if (!isRenderInitialized_ || audioRenderer_ == nullptr) {
        return;
    }
    audioRenderer_->Flush();
    audioRenderer_->Drain();
    audioRenderer_->Stop();
    audioRenderer_->Release();
    isRenderInitialized_ = false;
}

void AudioPlayer::ReleaseCapturer()
{
    if (!isCapturerInitialized_ || audioCapturer_ == nullptr) {
        return;
    }
    audioCapturer_->Flush();
    audioCapturer_->Stop();
    audioCapturer_->Release();
    isCapturerInitialized_ = false;
}

bool AudioPlayer::GetRealPath(const std::string &profilePath, std::string &realPath)
{
    if (profilePath.empty()) {
        TELEPHONY_LOGE("profilePath is empty");
        return false;
    }

    char path[PATH_MAX] = { '\0' };
    if (realpath(profilePath.c_str(), path) == nullptr) {
        TELEPHONY_LOGE("get real path fail");
        return false;
    }
    realPath = path;
    return true;
}

void AudioPlayer::CallAudioRendererCallback::OnInterrupt(const AudioStandard::InterruptEvent &interruptEvent)
{
    if (interruptEvent.forceType == AudioStandard::INTERRUPT_FORCE) {
        switch (interruptEvent.hintType) {
            case AudioStandard::INTERRUPT_HINT_PAUSE:
                break;
            case AudioStandard::INTERRUPT_HINT_STOP:
                if (DelayedSingleton<AudioControlManager>::GetInstance()->IsCurrentRinging() &&
                    DelayedSingleton<AudioControlManager>::GetInstance()->IsSoundPlaying()) {
                    DelayedSingleton<AudioControlManager>::GetInstance()->StopRingtone();
                }
                break;
            default:
                break;
        }
    }
}

void AudioPlayer::RingCallback::OnInterrupt(const AudioStandard::InterruptEvent &interruptEvent)
{
    if (interruptEvent.forceType == AudioStandard::INTERRUPT_FORCE) {
        switch (interruptEvent.hintType) {
            case AudioStandard::INTERRUPT_HINT_PAUSE:
                break;
            case AudioStandard::INTERRUPT_HINT_STOP:
                if (DelayedSingleton<AudioControlManager>::GetInstance()->IsCurrentRinging() &&
                    DelayedSingleton<AudioControlManager>::GetInstance()->IsSoundPlaying()) {
                    DelayedSingleton<AudioControlManager>::GetInstance()->StopRingtone();
                }
                break;
            default:
                break;
        }
    }
}

void AudioPlayer::RegisterRingCallback(std::shared_ptr<Media::RingtonePlayer> &RingtonePlayer)
{
    std::lock_guard<ffrt::mutex> lock(mutex_);
    ringCallback_ = std::make_shared<RingCallback>();
    if (ringCallback_ == nullptr) {
        TELEPHONY_LOGE("ringCallback_ is nullptr");
        return;
    }
    if (RingtonePlayer == nullptr) {
        TELEPHONY_LOGE("RingtonePlayer is nullptr");
        return;
    }
    RingtonePlayer->SetRingtonePlayerInterruptCallback(ringCallback_);
}

int32_t AudioPlayer::SetMute()
{
    if (audioRenderer_ == nullptr) {
        TELEPHONY_LOGE("audioRenderer_ is nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    float volume = 0.0f;
    return audioRenderer_->SetVolume(volume);
}
} // namespace Telephony
} // namespace OHOS