/*
 * 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 "tone.h"

#include <thread>

#include "call_control_manager.h"
#include "telephony_log_wrapper.h"
#ifdef SUPPORT_DSOFTBUS
#include "distributed_communication_manager.h"
#endif

namespace OHOS {
namespace Telephony {
using AudioPlay = int32_t (AudioPlayer::*)(const std::string &, AudioStandard::AudioStreamType, PlayerType);

Tone::Tone() : audioPlayer_(new (std::nothrow) AudioPlayer()) {}

Tone::Tone(ToneDescriptor tone) : audioPlayer_(new (std::nothrow) AudioPlayer())
{
    currentToneDescriptor_ = tone;
}

Tone::~Tone()
{
    if (audioPlayer_ != nullptr) {
        delete audioPlayer_;
        audioPlayer_ = nullptr;
    }
}

void Tone::Init() {}

int32_t Tone::Play()
{
    if (currentToneDescriptor_ == TONE_UNKNOWN) {
        TELEPHONY_LOGE("tone descriptor unknown");
        return CALL_ERR_AUDIO_UNKNOWN_TONE;
    }
    if (IsUseTonePlayer(currentToneDescriptor_)) {
        TELEPHONY_LOGI("currentToneDescriptor = %{public}d", currentToneDescriptor_);
        if (!InitTonePlayer()) {
            TELEPHONY_LOGE("InitTonePlayer failed");
            return TELEPHONY_ERROR;
        }
        if (currentToneDescriptor_ == ToneDescriptor::TONE_FINISHED) {
            if (!tonePlayer_->StartTone()) {
                return CALL_ERR_AUDIO_TONE_PLAY_FAILED;
            }
            return TELEPHONY_SUCCESS;
        }
        std::shared_ptr<AudioStandard::TonePlayer> tonePlayerPtr = tonePlayer_;
        std::thread play([tonePlayerPtr]() {
            pthread_setname_np(pthread_self(), TONE_PLAY_THREAD);
            tonePlayerPtr->StartTone();
        });
        play.detach();
    }
    return TELEPHONY_SUCCESS;
}

int32_t Tone::Stop()
{
    std::lock_guard<ffrt::mutex> lock(mutex_);
    if (currentToneDescriptor_ == TONE_UNKNOWN) {
        TELEPHONY_LOGE("tone descriptor unknown");
        return CALL_ERR_AUDIO_UNKNOWN_TONE;
    }
    if (IsUseTonePlayer(currentToneDescriptor_)) {
        if (tonePlayer_ != nullptr) {
            tonePlayer_->StopTone();
            tonePlayer_->Release();
        }
    } else {
        if (audioPlayer_ == nullptr) {
            TELEPHONY_LOGE("audioPlayer_ is nullptr");
            return TELEPHONY_ERR_LOCAL_PTR_NULL;
        }
        audioPlayer_->SetStop(PlayerType::TYPE_TONE, true);
    }
    return TELEPHONY_SUCCESS;
}

bool Tone::InitTonePlayer()
{
    using namespace OHOS::AudioStandard;
    if (tonePlayer_ == nullptr) {
        StreamUsage streamUsage = GetStreamUsageByToneType(currentToneDescriptor_);
        AudioRendererInfo rendererInfo = {};
        rendererInfo.contentType = ContentType::CONTENT_TYPE_UNKNOWN;
        rendererInfo.streamUsage = streamUsage;
        rendererInfo.rendererFlags = 0;
        tonePlayer_ = TonePlayer::Create(rendererInfo);
        if (tonePlayer_ == nullptr) {
            return false;
        }
        ToneType toneType = ConvertToneDescriptorToToneType(currentToneDescriptor_);
        if (!tonePlayer_->LoadTone(toneType)) {
            return false;
        }
    }
    return true;
}

ToneDescriptor Tone::ConvertDigitToTone(char digit)
{
    ToneDescriptor dtmf = ToneDescriptor::TONE_UNKNOWN;
    switch (digit) {
        case '0':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_0;
            break;
        case '1':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_1;
            break;
        case '2':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_2;
            break;
        case '3':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_3;
            break;
        case '4':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_4;
            break;
        case '5':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_5;
            break;
        case '6':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_6;
            break;
        case '7':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_7;
            break;
        case '8':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_8;
            break;
        case '9':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_9;
            break;
        case '*':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_P;
            break;
        case '#':
            dtmf = ToneDescriptor::TONE_DTMF_CHAR_W;
            break;
        default:
            break;
    }
    return dtmf;
}

AudioStandard::ToneType Tone::ConvertToneDescriptorToToneType(ToneDescriptor tone)
{
    using namespace OHOS::AudioStandard;
    ToneType tonType = ToneType::NUM_TONES;
    switch (tone) {
        case ToneDescriptor::TONE_DTMF_CHAR_0:
            tonType = ToneType::TONE_TYPE_DIAL_0;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_1:
            tonType = ToneType::TONE_TYPE_DIAL_1;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_2:
            tonType = ToneType::TONE_TYPE_DIAL_2;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_3:
            tonType = ToneType::TONE_TYPE_DIAL_3;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_4:
            tonType = ToneType::TONE_TYPE_DIAL_4;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_5:
            tonType = ToneType::TONE_TYPE_DIAL_5;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_6:
            tonType = ToneType::TONE_TYPE_DIAL_6;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_7:
            tonType = ToneType::TONE_TYPE_DIAL_7;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_8:
            tonType = ToneType::TONE_TYPE_DIAL_8;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_9:
            tonType = ToneType::TONE_TYPE_DIAL_9;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_P:
            tonType = ToneType::TONE_TYPE_DIAL_S;
            break;
        case ToneDescriptor::TONE_DTMF_CHAR_W:
            tonType = ToneType::TONE_TYPE_DIAL_P;
            break;
        default:
            tonType = ConvertCallToneDescriptorToToneType(tone);
            break;
    }
    return tonType;
}

AudioStandard::ToneType Tone::ConvertCallToneDescriptorToToneType(ToneDescriptor tone)
{
    using namespace OHOS::AudioStandard;
    ToneType tonType = ToneType::NUM_TONES;
    switch (tone) {
        case ToneDescriptor::TONE_RINGBACK:
            tonType = ToneType::TONE_TYPE_COMMON_SUPERVISORY_RINGTONE;
            break;
        case ToneDescriptor::TONE_WAITING:
            tonType = ToneType::TONE_TYPE_COMMON_SUPERVISORY_CALL_WAITING;
            break;
        case ToneDescriptor::TONE_FINISHED:
            tonType = ToneType::TONE_TYPE_COMMON_PROPRIETARY_PROMPT;
            break;
        default:
            break;
    }
    return tonType;
}

AudioStandard::StreamUsage Tone::GetStreamUsageByToneType(ToneDescriptor descriptor)
{
    AudioStandard::StreamUsage streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_UNKNOWN;
    switch (descriptor) {
        case ToneDescriptor::TONE_DTMF_CHAR_0:
        case ToneDescriptor::TONE_DTMF_CHAR_1:
        case ToneDescriptor::TONE_DTMF_CHAR_2:
        case ToneDescriptor::TONE_DTMF_CHAR_3:
        case ToneDescriptor::TONE_DTMF_CHAR_4:
        case ToneDescriptor::TONE_DTMF_CHAR_5:
        case ToneDescriptor::TONE_DTMF_CHAR_6:
        case ToneDescriptor::TONE_DTMF_CHAR_7:
        case ToneDescriptor::TONE_DTMF_CHAR_8:
        case ToneDescriptor::TONE_DTMF_CHAR_9:
        case ToneDescriptor::TONE_DTMF_CHAR_P:
        case ToneDescriptor::TONE_DTMF_CHAR_W:
            streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_DTMF;
            break;
        case ToneDescriptor::TONE_RINGBACK:
            streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_VOICE_MODEM_COMMUNICATION;
            break;
        case ToneDescriptor::TONE_WAITING:
            streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_VOICE_MODEM_COMMUNICATION;
            int32_t state;
            DelayedSingleton<CallControlManager>::GetInstance()->GetVoIPCallState(state);
            if (state == (int32_t)CallStateToApp::CALL_STATE_RINGING ||
                state == (int32_t)CallStateToApp::CALL_STATE_OFFHOOK ||
                state == (int32_t)CallStateToApp::CALL_STATE_ANSWERED) {
                streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_VOICE_COMMUNICATION;
            }
            break;
        case ToneDescriptor::TONE_FINISHED:
            streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_VOICE_RINGTONE;
            break;
        default:
            break;
    }
#ifdef SUPPORT_DSOFTBUS
    bool isVoipCall = false;
    auto dcMgrInstance = DelayedSingleton<DistributedCommunicationManager>::GetInstance();
    if (dcMgrInstance != nullptr) {
        isVoipCall = dcMgrInstance->IsSinkRole() && dcMgrInstance->IsConnected();
    }
    if (isVoipCall && streamUsage == AudioStandard::StreamUsage::STREAM_USAGE_VOICE_MODEM_COMMUNICATION) {
        streamUsage = AudioStandard::StreamUsage::STREAM_USAGE_VOICE_COMMUNICATION;
        TELEPHONY_LOGI("dc use voip");
    }
#endif
    return streamUsage;
}

bool Tone::IsUseTonePlayer(ToneDescriptor tone)
{
    bool ret = false;
    switch (tone) {
        case ToneDescriptor::TONE_DTMF_CHAR_0:
        case ToneDescriptor::TONE_DTMF_CHAR_1:
        case ToneDescriptor::TONE_DTMF_CHAR_2:
        case ToneDescriptor::TONE_DTMF_CHAR_3:
        case ToneDescriptor::TONE_DTMF_CHAR_4:
        case ToneDescriptor::TONE_DTMF_CHAR_5:
        case ToneDescriptor::TONE_DTMF_CHAR_6:
        case ToneDescriptor::TONE_DTMF_CHAR_7:
        case ToneDescriptor::TONE_DTMF_CHAR_8:
        case ToneDescriptor::TONE_DTMF_CHAR_9:
        case ToneDescriptor::TONE_DTMF_CHAR_P:
        case ToneDescriptor::TONE_DTMF_CHAR_W:
            ret = true;
            break;
        case ToneDescriptor::TONE_RINGBACK:
        case ToneDescriptor::TONE_WAITING:
        case ToneDescriptor::TONE_FINISHED:
            ret = true;
            break;
        default:
            break;
    }
    return ret;
}

void Tone::ReleaseRenderer()
{
    if (audioPlayer_ == nullptr) {
        TELEPHONY_LOGE("audioPlayer_ is nullptr");
        return;
    }
    audioPlayer_->ReleaseRenderer();
}

ToneDescriptor Tone::getCurrentToneType()
{
    TELEPHONY_LOGI("currentToneDescriptor_ = %{public}d", currentToneDescriptor_);
    return currentToneDescriptor_;
}
} // namespace Telephony
} // namespace OHOS