/*
 * Copyright (C) 2023 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 "watchdog.h"
#include <mutex>
#include <thread>

namespace {
    constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_PLAYER, "watchdog"};
}

namespace OHOS {
namespace Media {
WatchDog::~WatchDog()
{
    DisableWatchDog();
}

void WatchDog::EnableWatchDog()
{
    while (true) {
        std::unique_lock<std::mutex> lock(mutex_);
        if (enable_) {
            return;
        }

        if (disabling.load()) {
            continue; // Wait for disable execution to finish.
        }

        enable_ = true;
        thread_ = std::make_unique<std::thread>([this] () -> void { this->WatchDogThread(); });
        break;
    }
}

void WatchDog::DisableWatchDog()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (disabling.load() == false) {
        disabling.store(true);
        enable_ = false;
        cond_.notify_all();
        lock.unlock(); // Make the thread acquire the lock and exit.
        if (thread_ != nullptr && thread_->joinable()) {
            thread_->join();
            thread_.reset();
            thread_ = nullptr;
        }

        // It may be changed by thread. Assign value after thread recycle.
        pause_ = false;
        alarmed_ = false;
        disabling.store(false);
    }
}

void WatchDog::PauseWatchDog()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (enable_) {
        pause_ = true;
        paused_ = true;
        cond_.notify_all();
    }
}

void WatchDog::ResumeWatchDog()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (enable_) {
        pause_ = false;
        cond_.notify_all();
    }
}

void WatchDog::Notify()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (enable_) {
        if (alarmed_) {
            alarmed_ = false;
            AlarmRecovery();
            pause_ = false;
            cond_.notify_all();
        }

        count_++;
        cond_.notify_all();
    }
}

void WatchDog::Alarm()
{
    MEDIA_LOGI("Alarm!");
}

void WatchDog::AlarmRecovery()
{
    MEDIA_LOGI("AlarmRecovery!");
}

void WatchDog::WatchDogThread()
{
    while (true) {
        std::unique_lock<std::mutex> lock(mutex_);
        
        // For pause/resume control, wait only when paused.
        cond_.wait(lock, [this] {
            return (enable_ == false) || (pause_ == false);
        });

        if (paused_) {
            paused_ = false;
        }

        // For timeout detection.
        cond_.wait_for(lock, std::chrono::milliseconds(timeoutMs_), [this] {
            return (enable_ == false) || (pause_ == true) || (count_ > 0);
        });

        if (enable_ == false) {
            break;
        }

        if (pause_ == true || paused_ == true) {
            continue;
        }
        
        if (count_ == 0) {
            MEDIA_LOGI("Watchdog timeout!");
            if (alarmed_ == false) {
                alarmed_ = true;
                Alarm();
                pause_ = true;
            }
        }

        count_ = 0;
    }
}
} // namespace Media
} // namespace OHOS