/*
 * 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 "graphics_task.h"
#include <mutex>
#include <utility>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <qos.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "res_sched_client.h"

#include "3d_widget_adapter_log.h"

namespace OHOS::Render3D {
constexpr uint32_t RES_TYPE_EXT_ENGINE_SET_QOS = 10028;
GraphicsTask::Message::Message(const std::function<Task>& task) : task_(std::move(task))
{}

GraphicsTask::Message::Message(GraphicsTask::Message&& msg)
    : task_(std::move(msg.task_)), pms_(std::move(msg.pms_)), ftr_(std::move(msg.ftr_))
{}

GraphicsTask::Message& GraphicsTask::Message::operator=(GraphicsTask::Message&& msg)
{
    task_ = std::move(msg.task_);
    pms_ = std::move(msg.pms_);
    ftr_ = std::move(msg.ftr_);
    return *this;
}

void GraphicsTask::Message::Execute()
{
    task_();
    Finish();
}

void GraphicsTask::Message::Finish()
{
    pms_.set_value();
}

std::shared_future<void> GraphicsTask::Message::GetFuture()
{
    return std::move(ftr_);
}

GraphicsTask& GraphicsTask::GetInstance()
{
    static GraphicsTask gfxTask;
    return gfxTask;
}

void GraphicsTask::PushSyncMessage(const std::function<Task>& task)
{
    std::shared_future<void> ftr;
    {
        std::lock_guard<std::mutex> lk(messageQueueMut_);
        ftr = messageQueue_.emplace(std::move(task)).GetFuture();
        messageQueueCnd_.notify_one();
    }

    if (ftr.valid()) {
        ftr.get();
    }
}

std::shared_future<void> GraphicsTask::PushAsyncMessage(const std::function<Task>& task)
{
    std::lock_guard<std::mutex> lk(messageQueueMut_);

    Message& msg = messageQueue_.emplace(std::move(task));
    messageQueueCnd_.notify_one();

    return msg.GetFuture();
}

GraphicsTask::GraphicsTask()
{
    Start();
}

GraphicsTask::~GraphicsTask()
{
    Stop();
    {
        std::lock_guard<std::mutex> lk(messageQueueMut_);
        while (!messageQueue_.empty()) {
            messageQueue_.front().Finish();
            messageQueue_.pop();
        }
    }

    if (loop_.joinable()) {
        loop_.join();
    }
    WIDGET_LOGI("~GraphicsTask() end");
}

void GraphicsTask::Start()
{
    WIDGET_LOGI("GraphicsTask::Start start");

    if (!exit_) {
        return;
    }
    exit_ = false;
    loop_ = std::thread([this] { this->EngineThread(); });
    PushAsyncMessage([this] {
        this->SetName();
        int ret = OHOS::QOS::SetThreadQos(OHOS::QOS::QosLevel::QOS_USER_INTERACTIVE);
        WIDGET_LOGI("set engine child thread qos %s", ret == 0 ? "success" : "failed");
        auto tid = syscall(SYS_gettid);
        if (tid > 0) {
            std::unordered_map<std::string, std::string> mapPayload{
                {"pid", std::to_string(getpid())}, {"tid", std::to_string(tid)}};
            WIDGET_LOGI("ReportEngineResType %s %s", mapPayload["pid"].c_str(), mapPayload["tid"].c_str());
            OHOS::ResourceSchedule::ResSchedClient::GetInstance().ReportData(
                RES_TYPE_EXT_ENGINE_SET_QOS, 1, mapPayload);
        }
    });
    WIDGET_LOGD("GraphicsTask::Start end");
}

void GraphicsTask::Stop()
{
    exit_ = true;
}

void GraphicsTask::EngineThread()
{
    WIDGET_LOGD("GraphicsTask::EngineThread execute start");
    do {
        std::unique_lock<std::mutex> lk(messageQueueMut_);
        messageQueueCnd_.wait(lk, [this] { return !messageQueue_.empty(); });

        Message msg(std::move(messageQueue_.front()));
        messageQueue_.pop();
        lk.unlock();

        msg.Execute();
    } while (!exit_);

    auto tid = syscall(SYS_gettid);
    if (tid > 0) {
        std::unordered_map<std::string, std::string> mapPayload{
            {"pid", std::to_string(getpid())}, {"tid", std::to_string(tid)}};
        WIDGET_LOGI("ReportEngineResType %s %s", mapPayload["pid"].c_str(), mapPayload["tid"].c_str());
        OHOS::ResourceSchedule::ResSchedClient::GetInstance().ReportData(RES_TYPE_EXT_ENGINE_SET_QOS, 0, mapPayload);
    }

    WIDGET_LOGD("GraphicsTask::EngineThread execute exit");
}

void GraphicsTask::SetName()
{
    WIDGET_LOGD("GraphicsTask::SetName start");
    prctl(PR_SET_NAME, "Engine Service Lume", 0, 0, 0);
}
}  // namespace OHOS::Render3D