/*
 * Copyright (c) 2023-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 "timer/camera_deferred_timer.h"
#include "timer/steady_clock.h"
#include "timer/core/timer_core.h"
#include "dp_log.h"

namespace OHOS {
namespace CameraStandard {
namespace DeferredProcessing {

std::shared_ptr<Timer> Timer::Create(const std::string& name, TimerType timerType,
    uint32_t intervalMs, TimerCallback callback)
{
    DP_DEBUG_LOG("name: %s, timer type: %{public}d(0: once, 1: periodic), intervalMs: %u",
        name.c_str(), timerType, intervalMs);
    struct MakeSharedHelper : public Timer {
        MakeSharedHelper(const std::string& name, TimerType timerType, uint32_t intervalMs, TimerCallback callback)
            : Timer(name, timerType, intervalMs, std::move(callback))
        {
        }
    };
    auto timer = std::make_shared<MakeSharedHelper>(name, timerType, intervalMs, std::move(callback));
    if (!timer->Initialize()) {
        timer.reset();
    }
    return timer;
}

Timer::Timer(const std::string& name, TimerType timerType, uint32_t intervalMs, TimerCallback callback)
    : name_(name), timerType_(timerType), intervalMs_(intervalMs), callback_(std::move(callback)), expiredTimeMs_(0)
{
    DP_DEBUG_LOG("name: %s, timer type: %{public}d(0: once, 1: periodic), intervalMs: %u",
        name_.c_str(), timerType, intervalMs);
}

Timer::~Timer()
{
    DP_DEBUG_LOG("name: %s, timer type: %{public}d(0: once, 1: periodic), intervalMs: %u",
        name_.c_str(), timerType_, intervalMs_);
    std::lock_guard<std::mutex> lock(mutex_);
    active_ = false;
}

bool Timer::Initialize()
{
    return TimerCore::GetInstance().Initialize();
}

const std::string& Timer::GetName()
{
    return name_;
}

bool Timer::Start(uint32_t delayTimeMs)
{
    std::lock_guard<std::mutex> lock(mutex_);
    return StartUnlocked(delayTimeMs);
}

bool Timer::StartAt(uint64_t timestampMs)
{
    std::lock_guard<std::mutex> lock(mutex_);
    return StartAtUnlocked(timestampMs);
}

bool Timer::Stop()
{
    std::lock_guard<std::mutex> lock(mutex_);
    DP_DEBUG_LOG("stop timer (%s), timer type: %{public}d(0: once, 1: periodic), intervalMs: %u",
        name_.c_str(), timerType_, intervalMs_);
    if (!active_) {
        return true;
    }
    active_ = false;
    return TimerCore::GetInstance().DeregisterTimer(expiredTimeMs_, shared_from_this());
}

bool Timer::StartUnlocked(uint32_t delayTimeMs)
{
    auto curTimeMs = SteadyClock::GetTimestampMilli();
    auto timestampMs = curTimeMs + (delayTimeMs == 0 ? intervalMs_ : delayTimeMs);
    DP_DEBUG_LOG("timer (%s), type: %{public}d(0: once, 1: periodic), curTime: %{public}d, expiredTime: %{public}d",
        name_.c_str(), timerType_, static_cast<int>(curTimeMs), static_cast<int>(timestampMs));
    return StartAtUnlocked(timestampMs);
}

bool Timer::StartAtUnlocked(uint64_t timestampMs)
{
    if (active_) {
        TimerCore::GetInstance().DeregisterTimer(expiredTimeMs_, shared_from_this());
    }
    active_ = true;
    expiredTimeMs_ = timestampMs;
    return TimerCore::GetInstance().RegisterTimer(expiredTimeMs_, shared_from_this());
}

bool Timer::IsActive()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return active_;
}

void Timer::TimerExpired()
{
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (!active_) {
            DP_DEBUG_LOG("inactive timer (%s) expired, type: %{public}d(0: once, 1: periodic), intervalMs: %u",
                name_.c_str(), timerType_, intervalMs_);
            return;
        }
        if (timerType_ == TimerType::PERIODIC) {
            StartUnlocked();
        } else {
            active_ = false;
        }
    }
    DP_DEBUG_LOG("timer (%s) expired, type: %{public}d(0: once, 1: periodic), expiredTimeMs at %{public}d",
        name_.c_str(), timerType_, static_cast<int>(expiredTimeMs_));
    if (callback_) {
        callback_();
    }
}
} //namespace DeferredProcessing
} // namespace CameraStandard
} // namespace OHOS