/*
 * Copyright (c) 2025 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 "scene_adapter/sr_module.h"

#include <core/ecs/intf_ecs.h>
#include <3d/implementation_uids.h>
#include <3d/ecs/systems/intf_node_system.h>
#include <3d/ecs/components/render_mesh_component.h>
#include <3d/ecs/components/render_handle_component.h>
#include <3d/ecs/components/camera_component.h>
#include <3d/ecs/components/render_configuration_component.h>

#include <render/implementation_uids.h>
#include <render/gles/intf_device_gles.h>
#include <render/intf_renderer.h>
#include <render/intf_render_context.h>
#include <render/device/intf_gpu_resource_manager.h>

#include <parameters.h>
#include <plugin_sr/ecs/systems/intf_sr_system.h>
#include <plugin_sr/ecs/components/sr_component.h>

#define LOW_QUALITY 3.0
#define BALANCED_QUALITY 2.0
#define HIGH_QUALITY 1.5
#define ULTRA_QUALITY 1.3
static constexpr BASE_NS::Uid UID_SR_PLUGIN{"c075f146-30bb-4df2-90f5-73069de404dc"};

namespace OHOS::Render3D {
SRData SRModule::sr_;
BASE_NS::refcnt_ptr<CORE_NS::IEcs> SRModule::ecs_;
CORE_NS::EntityReference SRModule::srConfigEntity_;

namespace {
float QualityToRate(const QualityTypeSR q)
{
    float rate;
    switch (q) {
        case QualityTypeSR::LOW:
            rate = LOW_QUALITY;
            break;
        case QualityTypeSR::BALANCED:
            rate = BALANCED_QUALITY;
            break;
        case QualityTypeSR::HIGH:
            rate = HIGH_QUALITY;
            break;
        case QualityTypeSR::ULTRA:
            rate = ULTRA_QUALITY;
            break;
        default:
            rate = ULTRA_QUALITY;
            break;
    }

    return rate;
}

SR::SRComponent::FlagBits MethodToFlagBit(const MethodTypeSR m)
{
    switch (m) {
        case MethodTypeSR::BILINEAR:
            return SR::SRComponent::FlagBits::BILINEAR_BIT;
        case MethodTypeSR::LUT:
            return SR::SRComponent::FlagBits::LUT_BIT;
        case MethodTypeSR::HGSR1:
            return SR::SRComponent::FlagBits::HGSR1_BIT;
        default:
            return SR::SRComponent::FlagBits::BILINEAR_BIT;
    }
}
}  // namespace

RENDER_NS::RenderHandleReference CreateRenderNodeGraph(
    BASE_NS::shared_ptr<RENDER_NS::IRenderContext> renderContext_, const BASE_NS::string_view rngPath)
{
    RENDER_NS::IRenderNodeGraphManager& graphManager = renderContext_->GetRenderNodeGraphManager();
    const RENDER_NS::RenderHandleReference handle = graphManager.LoadAndCreate(
        RENDER_NS::IRenderNodeGraphManager::RenderNodeGraphUsageType::RENDER_NODE_GRAPH_STATIC, rngPath);
    return handle;
}

struct CreatedImageData {
    BASE_NS::Math::UVec2 size{0, 0};
    RENDER_NS::RenderHandleReference handle;
    CORE_NS::EntityReference entity;
};

SRModule::SRModule()
{}

void SRModule::Init(BASE_NS::shared_ptr<SCENE_NS::IInternalScene> scene,
    BASE_NS::shared_ptr<RENDER_NS::IRenderContext> renderContext, BASE_NS::shared_ptr<CORE_NS::IEngine> engine,
    BASE_NS::refcnt_ptr<CORE_NS::IEcs> ecs)
{
    ecs_ = ecs;
}

void SRModule::Update(BASE_NS::shared_ptr<SCENE_NS::IInternalScene> scene)
{
    customRenderNodeGraph_.clear();
    customRenderNodeGraph_.push_back(rngSR_);
    scene->ModifyCustomRenderNodeGraph(
        Scene::IInternalScene::RenderNodeGraphModificationMode::APPEND, customRenderNodeGraph_);
}

bool SRModule::EnableSR()
{
    BASE_NS::array_view<const Core::IPlugin* const> plugins = Core::GetPluginRegister().GetPlugins();
    for (auto plugin : plugins) {
        if (plugin->version.uid == UID_SR_PLUGIN) {
            sr_.enable_ = true;
            return true;
        }
    }
    return false;
}

const SRData SRModule::InitConfig(
    BASE_NS::shared_ptr<SCENE_NS::IInternalScene> scene, BASE_NS::shared_ptr<RENDER_NS::IRenderContext> renderContext)
{
    auto* srConfigMgr =
        static_cast<SR::ISRComponentManager*>((*ecs_).GetComponentManager(SR::ISRComponentManager::UID));
    if (!srConfigMgr) {
        return sr_;
    }

    auto srHandle = srConfigMgr->Read(0);
    if (!srHandle) {
        return sr_;
    }

    const SR::SRComponent& testComponent = *srHandle;
    const auto method = testComponent.algorithm;
    const auto quality = testComponent.srRate;
    if (method == "sr_lut") {
        sr_.type_ = MethodTypeSR::LUT;
    } else if (method == "sr_hgsr1") {
        sr_.type_ = MethodTypeSR::HGSR1;
    } else {
        sr_.type_ = MethodTypeSR::BILINEAR;
    }

    switch (sr_.type_) {
        case MethodTypeSR::LUT:
            rngSR_ = CreateRenderNodeGraph(renderContext, "sr_rofs://rendernodegraphs/sr_lut.rng");
            break;
        case MethodTypeSR::HGSR1:
            rngSR_ = CreateRenderNodeGraph(renderContext, "sr_rofs://rendernodegraphs/sr_hgsr1.rng");
            break;
        default:
            rngSR_ = CreateRenderNodeGraph(renderContext, "sr_rofs://rendernodegraphs/sr_bilinear.rng");
            break;
    }
    if (!rngSR_) {
        return sr_;
    }

    if (quality == "low") {
        sr_.rate_ = QualityToRate(QualityTypeSR::LOW);
    } else if (quality == "balanced") {
        sr_.rate_ = QualityToRate(QualityTypeSR::BALANCED);
    } else if (quality == "high") {
        sr_.rate_ = QualityToRate(QualityTypeSR::HIGH);
    } else {
        sr_.rate_ = QualityToRate(QualityTypeSR::ULTRA);
    }

    return sr_;
}

const SRData SRModule::GetConfig()
{
    return sr_;
}

void SRModule::SetWindowSize(int width, int height)
{
    if (width < 0 || height < 0)
        return;
    sr_.width_ = width;
    sr_.height_ = height;
}

bool SRModule::Enable()
{
    return sr_.enable_;
}

RENDER_NS::RenderHandleReference SRModule::CreateGpuResource(
    BASE_NS::shared_ptr<RENDER_NS::IRenderContext> rc, float width, float height)
{
    RENDER_NS::IGpuResourceManager& gpuResourceMgr = rc->GetDevice().GetGpuResourceManager();
    RENDER_NS::GpuImageDesc desc;
    desc.width = static_cast<int>(width);
    desc.height = static_cast<int>(height);
    desc.depth = 1;
    desc.format = BASE_NS::Format::BASE_FORMAT_R8G8B8A8_SRGB;
    desc.memoryPropertyFlags = RENDER_NS::MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    desc.usageFlags = RENDER_NS::ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
                      RENDER_NS::ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT |
                      RENDER_NS::ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
    desc.imageType = RENDER_NS::ImageType::CORE_IMAGE_TYPE_2D;
    desc.imageTiling = RENDER_NS::ImageTiling::CORE_IMAGE_TILING_OPTIMAL;
    desc.imageViewType = RENDER_NS::ImageViewType::CORE_IMAGE_VIEW_TYPE_2D;
    desc.engineCreationFlags = RENDER_NS::EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS;
    auto handle = gpuResourceMgr.Create("srInput", desc);
    return handle;
}

void SRModule::AttachComponent()
{
    if (!ecs_)
        return;
    auto* srConfigMgr =
        static_cast<SR::ISRComponentManager*>((*ecs_).GetComponentManager(SR::ISRComponentManager::UID));
    if (!srConfigMgr)
        return;

    auto srEntity = srConfigMgr->GetEntity(0);
    auto srHandle = srConfigMgr->Write(srEntity);
    if (!srHandle)
        return;

    SR::SRComponent& srComponent = *srHandle;

    srComponent.enableFlags = MethodToFlagBit(sr_.type_);

    srComponent.generalConfiguration.outputWidth = sr_.width_;
    srComponent.generalConfiguration.outputHeight = sr_.height_;
    srComponent.generalConfiguration.inputWidth = static_cast<int32_t>(sr_.width_ / sr_.rate_);
    srComponent.generalConfiguration.inputHeight = static_cast<int32_t>(sr_.height_ / sr_.rate_);
}

}  // namespace OHOS::Render3D