/*
 * Copyright (C) 2024 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 "call_object_manager.h"
#include "call_wired_headset.h"

#include "input_manager.h"
#include "audio_device_manager.h"
#include "audio_control_manager.h"
#include "call_manager_base.h"
#include "system_ability_definition.h"
#include "call_control_manager.h"
#include "audio_proxy.h"
#include "call_manager_base.h"
#include "ims_conference.h"

namespace OHOS {
namespace Telephony {

using WiredHeadSetCallback = std::function<void(const std::shared_ptr<OHOS::MMI::KeyEvent>)>;

constexpr int32_t INVALID_VALUE = -1;
constexpr int32_t FINAL_KEY_DOWN_DURATION_TWO = 2000;
CallWiredHeadSet::CallWiredHeadSet()
{
    isProcessed_ = false;
    downFirstTime_ = 0;
    subscribeIdForPressedUp_ = INVALID_VALUE;
    subscribeIdForPressedDown_ = INVALID_VALUE;
    subscribeIdForMediaPausedUp_ = INVALID_VALUE;
    subscribeIdForMediaPauseDown_ = INVALID_VALUE;
}

CallWiredHeadSet::~CallWiredHeadSet()
{
    DeInit();
}

bool CallWiredHeadSet::Init()
{
    const int32_t HEADSETHOOK_KEY = MMI::KeyEvent::KEYCODE_HEADSETHOOK;
    const int32_t MEDIA_PLAY_PAUSE_KEY = MMI::KeyEvent::KEYCODE_MEDIA_PLAY_PAUSE;

    auto pressedUpCallBack = [this](const std::shared_ptr<OHOS::MMI::KeyEvent> event) {
        this->DealKeyPressedUp(event);
    };
    auto pressedDownCallBack = [this](const std::shared_ptr<OHOS::MMI::KeyEvent> event) {
        this->DealKeyPressedDown(event);
    };

    subscribeIdForPressedDown_ = RegistKeyEvent(HEADSETHOOK_KEY, true, pressedDownCallBack);
    subscribeIdForPressedUp_ = RegistKeyEvent(HEADSETHOOK_KEY, false, pressedUpCallBack);
    subscribeIdForMediaPauseDown_ = RegistKeyEvent(MEDIA_PLAY_PAUSE_KEY, true, pressedDownCallBack);
    subscribeIdForMediaPausedUp_ = RegistKeyEvent(MEDIA_PLAY_PAUSE_KEY, false, pressedUpCallBack);

    TELEPHONY_LOGI("pressDown = %{public}d, pressUp = %{public}d, pauseDown = %{public}d, pauseUp = %{public}d",
        subscribeIdForPressedDown_, subscribeIdForPressedUp_, subscribeIdForMediaPauseDown_,
        subscribeIdForMediaPausedUp_);
    return true;
}

void CallWiredHeadSet::DeInit()
{
    UnRegistKeyEvent(subscribeIdForPressedDown_);
    UnRegistKeyEvent(subscribeIdForPressedUp_);
    UnRegistKeyEvent(subscribeIdForMediaPauseDown_);
    UnRegistKeyEvent(subscribeIdForMediaPausedUp_);
    TELEPHONY_LOGI("CallWiredHeadSet UnRegisted KeyMute Pressed callback succeed");
}

std::shared_ptr<MMI::KeyOption> CallWiredHeadSet::InitOption(
    const std::set<int32_t> &preKeys, int32_t finalKey, bool isFinalKeyDown, int32_t duration)
{
    std::shared_ptr<MMI::KeyOption> keyOption = std::make_shared<MMI::KeyOption>();
    keyOption->SetFinalKeyDown(isFinalKeyDown);
    keyOption->SetFinalKey(finalKey);
    keyOption->SetPreKeys(preKeys);
    keyOption->SetFinalKeyDownDuration(duration);
    return keyOption;
}

int32_t CallWiredHeadSet::RegistKeyEvent(int32_t keyCode, bool isFinalKeyDown, const WiredHeadSetCallback &callback)
{
    std::set<int32_t> preKeys;
    std::shared_ptr<MMI::KeyOption> keyOption = InitOption(preKeys, keyCode, isFinalKeyDown, 0);
    if (keyCode == MMI::KeyEvent::KEYCODE_HEADSETHOOK) {
        keyOption->SetPriority(MMI::SubscribePriority::PRIORITY_100);
    }
    int32_t subscribeId = MMI::InputManager::GetInstance()->SubscribeKeyEvent(keyOption, callback);
    TELEPHONY_LOGI("keyCode: %{public}d, subscribeId: %{public}d", keyCode, subscribeId);
    return subscribeId;
}

void CallWiredHeadSet::UnRegistKeyEvent(int32_t &subscribeId)
{
    if (subscribeId >= 0) {
        MMI::InputManager::GetInstance()->UnsubscribeKeyEvent(subscribeId);
        subscribeId = INVALID_VALUE;
    }
}

// wired headset mute key pressed callback method
void CallWiredHeadSet::DealKeyPressedUp(std::shared_ptr<MMI::KeyEvent> event)
{
    TELEPHONY_LOGI("received KeyMute Pressed Up event");
    if (DelayedSingleton<AudioDeviceManager>::GetInstance()->IsWiredHeadsetConnected() == true) {
        time_t currentTime = GetCurrentTimeMS();
        if ((currentTime - downFirstTime_) < FINAL_KEY_DOWN_DURATION_TWO) {
            DealKeyShortPressed();
        } else if (!isProcessed_) {
            DealKeyLongPressed();
        }
    }
    downFirstTime_ = 0;
    isProcessed_ = false;
}

void CallWiredHeadSet::DealKeyPressedDown(std::shared_ptr<MMI::KeyEvent> event)
{
    TELEPHONY_LOGI("received KeyMute Pressed Down event");
    if (isProcessed_) {
        return;
    }
    if (DelayedSingleton<AudioDeviceManager>::GetInstance()->IsWiredHeadsetConnected() == true) {
        time_t currentTime = GetCurrentTimeMS();
        if (downFirstTime_ == 0) {
            downFirstTime_ = GetCurrentTimeMS();
            return;
        }
        if (!isProcessed_ && (currentTime - downFirstTime_ >= FINAL_KEY_DOWN_DURATION_TWO)) {
            DealKeyLongPressed();
            isProcessed_ = true;
        }
    }
}

// wired headset mute key short pressed callback method
void CallWiredHeadSet::DealKeyShortPressed()
{
    sptr<CallBase> ringingCall = CallObjectManager::GetOneCallObject(CallRunningState::CALL_RUNNING_STATE_RINGING);
    if (ringingCall == nullptr) {
        sptr<CallBase> holdCall = CallObjectManager::GetOneCallObject(CallRunningState::CALL_RUNNING_STATE_HOLD);
        sptr<CallBase> activeCall = CallObjectManager::GetOneCallObject(CallRunningState::CALL_RUNNING_STATE_ACTIVE);
        if (activeCall != nullptr && holdCall != nullptr) {
            TELEPHONY_LOGI("DealKeyShortPressed UnHoldCall callid(%{public}d)", holdCall->GetCallID());
            DelayedSingleton<CallControlManager>::GetInstance()->UnHoldCall(holdCall->GetCallID());
            return;
        }
        sptr<CallBase> call = CallObjectManager::GetAudioLiveCall();
        if (call != nullptr) {
            bool isMuted = DelayedSingleton<AudioProxy>::GetInstance()->IsMicrophoneMute();
            if (call->GetCallType() == CallType::TYPE_VOIP) {
                isMuted = call->IsMuted();
            }
            TELEPHONY_LOGI("DealKeyShortPressed SetMuted isMuted(%{public}d)", (!isMuted));
            DelayedSingleton<CallControlManager>::GetInstance()->SetMuted(!isMuted);
        }
    } else {
        TELEPHONY_LOGI("DealKeyShortPressed AnswerCall callid(%{public}d)", ringingCall->GetCallID());
        VideoStateType videoState = ringingCall->GetVideoStateType();
        if (videoState != VideoStateType::TYPE_VOICE && videoState != VideoStateType::TYPE_VIDEO) {
            TELEPHONY_LOGI("DealKeyShortPressed get original call type");
            videoState = static_cast<VideoStateType>(ringingCall->GetOriginalCallType());
        }
        DelayedSingleton<CallControlManager>::GetInstance()->AnswerCall(
            ringingCall->GetCallID(), static_cast<int32_t>(videoState));
    }
}

// wired headset mute key long pressed callback method
void CallWiredHeadSet::DealKeyLongPressed()
{
    sptr<CallBase> ringingCall = CallObjectManager::GetOneCallObject(CallRunningState::CALL_RUNNING_STATE_RINGING);
    if (ringingCall != nullptr) {
        TELEPHONY_LOGI("DealKeyLongPressed RejectCall callid(%{public}d)", ringingCall->GetCallID());
        ringingCall->RejectCall();
    } else {
        sptr<CallBase> foregroundCall = CallObjectManager::GetForegroundCall();
        if (foregroundCall != nullptr) {
            TelConferenceState confState = foregroundCall->GetTelConferenceState();
            if (confState != TelConferenceState::TEL_CONFERENCE_IDLE) {
                int32_t conferenceId = DelayedSingleton<ImsConference>::GetInstance()->GetMainCall();
                TELEPHONY_LOGI("DealKeyLongPressed HangUpCall conferenceId(%{public}d)", conferenceId);
                DelayedSingleton<CallControlManager>::GetInstance()->HangUpCall(conferenceId);
            } else {
                TELEPHONY_LOGI("DealKeyLongPressed HangUpCall callid(%{public}d)", foregroundCall->GetCallID());
                foregroundCall->HangUpCall();
            }
        }
    }
}

time_t CallWiredHeadSet::GetCurrentTimeMS()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tpMicro
        = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    return tpMicro.time_since_epoch().count();
}
} // namespace Telephony
} // namespace OHOS