/*

 * Copyright (c) 2022 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_renderer_adapter_impl.h"



#include <unordered_map>



#include "application_context.h"

#include "audio_errors.h"

#include "nweb_log.h"



namespace OHOS::NWeb {



const std::unordered_map<AudioAdapterSamplingRate, AudioSamplingRate> SAMPLING_RATE_MAP = {

    { AudioAdapterSamplingRate::SAMPLE_RATE_8000, AudioSamplingRate::SAMPLE_RATE_8000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_11025, AudioSamplingRate::SAMPLE_RATE_11025 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_12000, AudioSamplingRate::SAMPLE_RATE_12000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_16000, AudioSamplingRate::SAMPLE_RATE_16000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_22050, AudioSamplingRate::SAMPLE_RATE_22050 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_24000, AudioSamplingRate::SAMPLE_RATE_24000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_32000, AudioSamplingRate::SAMPLE_RATE_32000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_44100, AudioSamplingRate::SAMPLE_RATE_44100 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_48000, AudioSamplingRate::SAMPLE_RATE_48000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_64000, AudioSamplingRate::SAMPLE_RATE_64000 },

    { AudioAdapterSamplingRate::SAMPLE_RATE_96000, AudioSamplingRate::SAMPLE_RATE_96000 },

};



const std::unordered_map<AudioAdapterEncodingType, AudioEncodingType> ENCODING_TYPE_MAP = {

    { AudioAdapterEncodingType::ENCODING_PCM, AudioEncodingType::ENCODING_PCM },

    { AudioAdapterEncodingType::ENCODING_INVALID, AudioEncodingType::ENCODING_INVALID }

};



const std::unordered_map<AudioAdapterSampleFormat, AudioSampleFormat> SAMPLE_FORMAT_MAP = {

    { AudioAdapterSampleFormat::SAMPLE_U8, AudioSampleFormat::SAMPLE_U8 },

    { AudioAdapterSampleFormat::SAMPLE_S16LE, AudioSampleFormat::SAMPLE_S16LE },

    { AudioAdapterSampleFormat::SAMPLE_S24LE, AudioSampleFormat::SAMPLE_S24LE },

    { AudioAdapterSampleFormat::SAMPLE_S32LE, AudioSampleFormat::SAMPLE_S32LE },

    { AudioAdapterSampleFormat::SAMPLE_F32LE, AudioSampleFormat::SAMPLE_F32LE },

};



const std::unordered_map<AudioAdapterChannel, AudioChannel> AUDIO_CHANNEL_MAP = {

    { AudioAdapterChannel::MONO, AudioChannel::MONO },

    { AudioAdapterChannel::STEREO, AudioChannel::STEREO },

    { AudioAdapterChannel::CHANNEL_3, AudioChannel::CHANNEL_3 },

    { AudioAdapterChannel::CHANNEL_4, AudioChannel::CHANNEL_4 },

    { AudioAdapterChannel::CHANNEL_5, AudioChannel::CHANNEL_5 },

    { AudioAdapterChannel::CHANNEL_6, AudioChannel::CHANNEL_6 },

    { AudioAdapterChannel::CHANNEL_7, AudioChannel::CHANNEL_7 },

    { AudioAdapterChannel::CHANNEL_8, AudioChannel::CHANNEL_8 },

};



const std::unordered_map<AudioAdapterContentType, ContentType> CONTENT_TYPE_MAP = {

    { AudioAdapterContentType::CONTENT_TYPE_UNKNOWN, ContentType::CONTENT_TYPE_UNKNOWN },

    { AudioAdapterContentType::CONTENT_TYPE_SPEECH, ContentType::CONTENT_TYPE_SPEECH },

    { AudioAdapterContentType::CONTENT_TYPE_MUSIC, ContentType::CONTENT_TYPE_MUSIC },

    { AudioAdapterContentType::CONTENT_TYPE_MOVIE, ContentType::CONTENT_TYPE_MOVIE },

    { AudioAdapterContentType::CONTENT_TYPE_SONIFICATION, ContentType::CONTENT_TYPE_SONIFICATION },

    { AudioAdapterContentType::CONTENT_TYPE_RINGTONE, ContentType::CONTENT_TYPE_RINGTONE },

};



const std::unordered_map<AudioAdapterStreamUsage, StreamUsage> STREAM_USAGE_MAP = {

    { AudioAdapterStreamUsage::STREAM_USAGE_UNKNOWN, StreamUsage::STREAM_USAGE_UNKNOWN },

    { AudioAdapterStreamUsage::STREAM_USAGE_MEDIA, StreamUsage::STREAM_USAGE_MEDIA },

    { AudioAdapterStreamUsage::STREAM_USAGE_VOICE_COMMUNICATION, StreamUsage::STREAM_USAGE_VIDEO_COMMUNICATION },

    { AudioAdapterStreamUsage::STREAM_USAGE_VOICE_ASSISTANT, StreamUsage::STREAM_USAGE_VOICE_ASSISTANT },

    { AudioAdapterStreamUsage::STREAM_USAGE_NOTIFICATION_RINGTONE, StreamUsage::STREAM_USAGE_NOTIFICATION_RINGTONE },

};



const std::unordered_map<AudioStreamDeviceChangeReason, AudioAdapterDeviceChangeReason> CHANGE_REASON_MAP = {

    { AudioStreamDeviceChangeReason::UNKNOWN, AudioAdapterDeviceChangeReason::UNKNOWN },

    { AudioStreamDeviceChangeReason::NEW_DEVICE_AVAILABLE, AudioAdapterDeviceChangeReason::NEW_DEVICE_AVAILABLE },

    { AudioStreamDeviceChangeReason::OLD_DEVICE_UNAVALIABLE, AudioAdapterDeviceChangeReason::OLD_DEVICE_UNAVALIABLE },

    { AudioStreamDeviceChangeReason::OVERRODE, AudioAdapterDeviceChangeReason::OVERRODE },

};



const std::unordered_map<AudioAdapterConcurrencyMode, AudioConcurrencyMode> AUDIO_CONCURRENCY_MAP = {

    { AudioAdapterConcurrencyMode::INVALID, AudioConcurrencyMode::INVALID },

    { AudioAdapterConcurrencyMode::DEFAULT, AudioConcurrencyMode::DEFAULT },

    { AudioAdapterConcurrencyMode::MIX_WITH_OTHERS, AudioConcurrencyMode::MIX_WITH_OTHERS },

    { AudioAdapterConcurrencyMode::DUCK_OTHERS, AudioConcurrencyMode::DUCK_OTHERS },

    { AudioAdapterConcurrencyMode::PAUSE_OTHERS, AudioConcurrencyMode::PAUSE_OTHERS },

    { AudioAdapterConcurrencyMode::SILENT, AudioConcurrencyMode::SILENT },

};



AudioRendererCallbackImpl::AudioRendererCallbackImpl(std::shared_ptr<AudioRendererCallbackAdapter> cb) : cb_(cb) {};



void AudioRendererCallbackImpl::OnInterrupt(const InterruptEvent& interruptEvent)

{

    if (!cb_) {

        return;

    }

    switch (interruptEvent.hintType) {

        case InterruptHint::INTERRUPT_HINT_PAUSE:

            cb_->OnSuspend();

            break;

        case InterruptHint::INTERRUPT_HINT_STOP:

            cb_->OnSuspend();

            break;

        case InterruptHint::INTERRUPT_HINT_RESUME:

            cb_->OnResume();

            break;

        default:

            WVLOG_E("audio renderer interrupt hint not foud, code: %{public}d", interruptEvent.hintType);

            break;

    }

}



void AudioRendererCallbackImpl::OnStateChange(const RendererState state, const StateChangeCmdType cmdType) {}

AudioAdapterDeviceChangeReason AudioOutputChangeCallbackImpl::GetChangeReason(AudioStreamDeviceChangeReason reason)

{

    auto item = CHANGE_REASON_MAP.find(reason);

    if (item == CHANGE_REASON_MAP.end()) {

        WVLOG_E("device change reason not found");

        return AudioAdapterDeviceChangeReason::UNKNOWN;

    }

    return item->second;

}



AudioOutputChangeCallbackImpl::AudioOutputChangeCallbackImpl(std::shared_ptr<AudioOutputChangeCallbackAdapter> cb)

    : cb_(cb) {};

void AudioOutputChangeCallbackImpl::OnOutputDeviceChange(

    const AudioDeviceDescriptor& deviceInfo, const AudioStreamDeviceChangeReason reason)

{

    if (!cb_) {

        return;

    }

    AudioAdapterDeviceChangeReason reasonAdapter = GetChangeReason(reason);

    WVLOG_I("OnOutputDeviceChange reason: %{public}d", (int32_t)reasonAdapter);

    cb_->OnOutputDeviceChange((int32_t)reasonAdapter);

}



int32_t AudioRendererAdapterImpl::Create(

    const std::shared_ptr<AudioRendererOptionsAdapter> rendererOptions, std::string cachePath)

{

    std::string audioCachePath = cachePath;

    std::shared_ptr<AbilityRuntime::ApplicationContext> context =

        AbilityRuntime::ApplicationContext::GetApplicationContext();

    if (!context) {

        WVLOG_E("application context get failed");

        return AUDIO_ERROR;

    }

    if (audioCachePath.empty()) {

        audioCachePath = context->GetCacheDir();

        if (audioCachePath.empty()) {

            WVLOG_E("application cache path get failed");

            return AUDIO_ERROR;

        }

    }

    if (!rendererOptions) {

        WVLOG_E("rendererOptions is nullptr");

        return AUDIO_ERROR;

    }



    AudioRendererOptions audioOptions;

    TransformToAudioRendererOptions(audioOptions, rendererOptions);

    audio_renderer_ = AudioRenderer::Create(audioCachePath, audioOptions);

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer create failed");

        return AUDIO_NULL_ERROR;

    }

    audio_renderer_->SetOffloadAllowed(false);

    return AUDIO_OK;

}



bool AudioRendererAdapterImpl::Start()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return false;

    }

    return audio_renderer_->Start();

}



bool AudioRendererAdapterImpl::Pause()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return false;

    }

    return audio_renderer_->Pause();

}



bool AudioRendererAdapterImpl::Stop()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return false;

    }

    return audio_renderer_->Stop();

}



bool AudioRendererAdapterImpl::Release()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return false;

    }

    return audio_renderer_->Release();

}



int32_t AudioRendererAdapterImpl::Write(uint8_t* buffer, size_t bufferSize)

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return AUDIO_NULL_ERROR;

    }

    return audio_renderer_->Write(buffer, bufferSize);

}



int32_t AudioRendererAdapterImpl::GetLatency(uint64_t& latency)

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return AUDIO_NULL_ERROR;

    }

    return audio_renderer_->GetLatency(latency);

}



int32_t AudioRendererAdapterImpl::SetVolume(float volume)

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return AUDIO_NULL_ERROR;

    }

    return audio_renderer_->SetVolume(volume);

}



float AudioRendererAdapterImpl::GetVolume()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return AUDIO_NULL_ERROR;

    }

    return audio_renderer_->GetVolume();

}



int32_t AudioRendererAdapterImpl::SetAudioRendererCallback(

    const std::shared_ptr<AudioRendererCallbackAdapter>& callback)

{

    if (callback == nullptr) {

        WVLOG_E("set audio manager interrupt callback is nullptr");

        return AUDIO_NULL_ERROR;

    }

    callback_ = std::make_shared<AudioRendererCallbackImpl>(callback);



    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return AUDIO_NULL_ERROR;

    }

    int32_t ret = audio_renderer_->SetRendererCallback(callback_);

    if (ret != AudioStandard::SUCCESS) {

        WVLOG_E("audio renderer set callback failed, code: %{public}d", ret);

        return AUDIO_ERROR;

    }

    return AUDIO_OK;

}



int32_t AudioRendererAdapterImpl::SetAudioOutputChangeCallback(

    const std::shared_ptr<AudioOutputChangeCallbackAdapter>& callback)

{

    WVLOG_I("AudioRendererAdapterImpl::SetAudioOutputChangeCallback");

    if (callback == nullptr) {

        WVLOG_E("set audio manager interrupt callback is nullptr");

        return AUDIO_NULL_ERROR;

    }

    ouputChangeCallback_ = std::make_shared<AudioOutputChangeCallbackImpl>(callback);

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return AUDIO_NULL_ERROR;

    }

    int32_t ret = audio_renderer_->RegisterOutputDeviceChangeWithInfoCallback(ouputChangeCallback_);

    if (ret != AudioStandard::SUCCESS) {

        WVLOG_E("audio renderer set output device change callback failed, code: %{public}d", ret);

        return AUDIO_ERROR;

    }

    return AUDIO_OK;

}



void AudioRendererAdapterImpl::SetInterruptMode(bool audioExclusive)

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return;

    }

    InterruptMode interruptMode = audioExclusive ? InterruptMode::INDEPENDENT_MODE : InterruptMode::SHARE_MODE;

    WVLOG_D("AudioRendererAdapterImpl::SetInterruptMode audioExclusive: %{public}d", audioExclusive);

    audio_renderer_->SetInterruptMode(interruptMode);

}



void AudioRendererAdapterImpl::SetAudioSilentMode(bool isSilentMode)

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return;

    }

    audio_renderer_->SetSilentModeAndMixWithOthers(isSilentMode);

    WVLOG_D("AudioRendererAdapterImpl::SetAudioSilentMode isSilentMode: %{public}d", isSilentMode);

}



bool AudioRendererAdapterImpl::Flush()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return false;

    }

    return audio_renderer_->Flush();

}



bool AudioRendererAdapterImpl::IsRendererStateRunning()

{

    if (audio_renderer_ == nullptr) {

        WVLOG_E("audio rendderer is nullptr");

        return false;

    }

    return audio_renderer_->GetStatus() == OHOS::AudioStandard::RendererState::RENDERER_RUNNING;

}



AudioSamplingRate AudioRendererAdapterImpl::GetAudioSamplingRate(AudioAdapterSamplingRate samplingRate)

{

    auto item = SAMPLING_RATE_MAP.find(samplingRate);

    if (item == SAMPLING_RATE_MAP.end()) {

        WVLOG_E("audio sampling rate not found");

        return AudioSamplingRate::SAMPLE_RATE_44100;

    }

    return item->second;

}



AudioEncodingType AudioRendererAdapterImpl::GetAudioEncodingType(AudioAdapterEncodingType encodingType)

{

    auto item = ENCODING_TYPE_MAP.find(encodingType);

    if (item == ENCODING_TYPE_MAP.end()) {

        WVLOG_E("audio encoding type not found");

        return AudioEncodingType::ENCODING_INVALID;

    }

    return item->second;

}



AudioSampleFormat AudioRendererAdapterImpl::GetAudioSampleFormat(AudioAdapterSampleFormat sampleFormat)

{

    auto item = SAMPLE_FORMAT_MAP.find(sampleFormat);

    if (item == SAMPLE_FORMAT_MAP.end()) {

        WVLOG_E("audio sample format not found");

        return AudioSampleFormat::INVALID_WIDTH;

    }

    return item->second;

}



AudioChannel AudioRendererAdapterImpl::GetAudioChannel(AudioAdapterChannel channel)

{

    auto item = AUDIO_CHANNEL_MAP.find(channel);

    if (item == AUDIO_CHANNEL_MAP.end()) {

        WVLOG_E("audio channel not found");

        return AudioChannel::STEREO;

    }

    return item->second;

}



ContentType AudioRendererAdapterImpl::GetAudioContentType(AudioAdapterContentType contentType)

{

    auto item = CONTENT_TYPE_MAP.find(contentType);

    if (item == CONTENT_TYPE_MAP.end()) {

        WVLOG_E("audio content type not found");

        return ContentType::CONTENT_TYPE_MUSIC;

    }

    return item->second;

}



StreamUsage AudioRendererAdapterImpl::GetAudioStreamUsage(AudioAdapterStreamUsage streamUsage)

{

    auto item = STREAM_USAGE_MAP.find(streamUsage);

    if (item == STREAM_USAGE_MAP.end()) {

        WVLOG_E("audio stream usage not found");

        return StreamUsage::STREAM_USAGE_MEDIA;

    }

    return item->second;

}



AudioSessionStrategy AudioRendererAdapterImpl::GetAudioAudioStrategy(AudioAdapterConcurrencyMode concurrencyMode)

{

    struct AudioSessionStrategy strategy;

    auto item = AUDIO_CONCURRENCY_MAP.find(concurrencyMode);

    if (item == AUDIO_CONCURRENCY_MAP.end()) {

        WVLOG_E("audio concurrency mode not found");

        strategy.concurrencyMode = AudioConcurrencyMode::INVALID;

        return strategy;

    }

    strategy.concurrencyMode = item->second;

    if (strategy.concurrencyMode == AudioConcurrencyMode::PAUSE_OTHERS) {

        strategy.concurrencyMode = AudioConcurrencyMode::INVALID;

    }

    WVLOG_I("get audio strategy concurrencyMode: %{public}d.", strategy.concurrencyMode);

    return strategy;

}



void AudioRendererAdapterImpl::TransformToAudioRendererOptions(

    AudioRendererOptions& out, const std::shared_ptr<AudioRendererOptionsAdapter>& in)

{

    out.streamInfo.samplingRate = GetAudioSamplingRate(in->GetSamplingRate());

    out.streamInfo.encoding = GetAudioEncodingType(in->GetEncodingType());

    out.streamInfo.format = GetAudioSampleFormat(in->GetSampleFormat());

    out.streamInfo.channels = GetAudioChannel(in->GetChannel());

    out.rendererInfo.contentType = GetAudioContentType(in->GetContentType());

    out.rendererInfo.streamUsage = GetAudioStreamUsage(in->GetStreamUsage());

    out.rendererInfo.rendererFlags = in->GetRenderFlags();

    out.strategy = GetAudioAudioStrategy(in->GetConcurrencyMode());

}



} // namespace OHOS::NWeb