/*
 * 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 "EffectImpl.h"

#include "ColorImpl.h"
#include "SceneResourceImpl.h"

namespace OHOS::Render3D::KITETS {
EffectsContainerImpl::EffectsContainerImpl(const std::shared_ptr<EffectsContainerETS> effectsContainerETS)
    : effectsContainerETS_(effectsContainerETS)
{}

EffectsContainerImpl::~EffectsContainerImpl()
{
    effectsContainerETS_.reset();
}

void EffectsContainerImpl::append(::SceneResources::weak::Effect item)
{
    if (!effectsContainerETS_) {
        return;
    }
    if (item.is_error()) {
        return;
    }
    auto effectOptional = static_cast<::SceneResources::weak::SceneResource>(item)->getImpl();
    if (!effectOptional.has_value()) {
        WIDGET_LOGE("invalid effect in taihe object");
        return;
    }
    auto effectImpl = reinterpret_cast<EffectImpl*>(effectOptional.value());
    if (effectImpl == nullptr) {
        return;
    }
    std::shared_ptr<EffectETS> itemEffect = effectImpl->GetInternalEffect();
    if (!itemEffect) {
        return;
    }
    effectsContainerETS_->AppendChild(itemEffect);
}

void EffectsContainerImpl::insertAfter(::SceneResources::weak::Effect item, ::SceneNodes::EffectOrNull const& sibling)
{
    if (!effectsContainerETS_) {
        return;
    }
    if (item.is_error()) {
        return;
    }
    auto effectOptional = static_cast<::SceneResources::weak::SceneResource>(item)->getImpl();
    if (!effectOptional.has_value()) {
        WIDGET_LOGE("invalid effect in taihe object");
        return;
    }
    auto effectImpl = reinterpret_cast<EffectImpl*>(effectOptional.value());
    if (effectImpl == nullptr) {
        return;
    }
    std::shared_ptr<EffectETS> itemEffect = effectImpl->GetInternalEffect();
    if (!itemEffect) {
        return;
    }
    std::shared_ptr<EffectETS> siblingEffect = nullptr;
    if (sibling.holds_effect()) {
        ::SceneResources::Effect effect = sibling.get_effect_ref();
        if (effect.is_error()) {
            return;
        }
        auto siblingEffectOptional = static_cast<::SceneResources::SceneResource>(effect)->getImpl();
        if (!siblingEffectOptional.has_value()) {
            WIDGET_LOGE("invalid effect in taihe object");
            return;
        }
        auto siblingEffectImpl = reinterpret_cast<EffectImpl*>(siblingEffectOptional.value());
        if (siblingEffectImpl != nullptr) {
            siblingEffect = siblingEffectImpl->GetInternalEffect();
        }
    }
    effectsContainerETS_->InsertChildAfter(itemEffect, siblingEffect);
}

void EffectsContainerImpl::remove(::SceneResources::weak::Effect item)
{
    if (!effectsContainerETS_) {
        return;
    }
    if (item.is_error()) {
        return;
    }
    auto effectOptional = static_cast<::SceneResources::weak::SceneResource>(item)->getImpl();
    if (!effectOptional.has_value()) {
        WIDGET_LOGE("invalid effect in taihe object");
        return;
    }
    auto effectImpl = reinterpret_cast<EffectImpl*>(effectOptional.value());
    if (effectImpl == nullptr) {
        WIDGET_LOGE("cast EffectImpl fail");
        return;
    }
    std::shared_ptr<EffectETS> itemEffect = effectImpl->GetInternalEffect();
    if (!itemEffect) {
        WIDGET_LOGE("GetInternalEffect fail");
        return;
    }
    effectsContainerETS_->RemoveChild(itemEffect);
}

::SceneNodes::EffectOrNull EffectsContainerImpl::get(int32_t index)
{
    if (!effectsContainerETS_) {
        return SceneNodes::EffectOrNull::make_nValue();
    }
    std::shared_ptr<EffectETS> effect = effectsContainerETS_->GetChild(index);
    return EffectImpl::MakeEffectOrNull(effect);
}

void EffectsContainerImpl::clear()
{
    if (effectsContainerETS_) {
        effectsContainerETS_->ClearChildren();
    }
}

int32_t EffectsContainerImpl::count()
{
    if (effectsContainerETS_) {
        return effectsContainerETS_->GetCount();
    }
    return 0;
}

::SceneNodes::EffectOrNull EffectImpl::MakeEffectOrNull(const std::shared_ptr<EffectETS>& effectETS)
{
    if (!effectETS) {
        return ::SceneNodes::EffectOrNull::make_nValue();
    }
    auto effect = taihe::make_holder<EffectImpl, ::SceneResources::Effect>(effectETS);
    return ::SceneNodes::EffectOrNull::make_effect(effect);
}

EffectImpl::EffectImpl(const std::shared_ptr<EffectETS> effectETS)
    : SceneResourceImpl(SceneResources::SceneResourceType::key_t::EFFECT, effectETS), effectETS_(effectETS)
{
    WIDGET_LOGD("EffectImpl ++");
}

EffectImpl::~EffectImpl()
{
    WIDGET_LOGD("EffectImpl --");
    effectETS_.reset();
}

bool EffectImpl::getEnabled()
{
    if (!effectETS_) {
        WIDGET_LOGE("empty EffectETS");
        return false;
    }
    return effectETS_->GetEnabled();
}

void EffectImpl::setEnabled(bool enabled)
{
    if (!effectETS_) {
        WIDGET_LOGE("empty EffectETS");
        return;
    }
    effectETS_->SetEnabled(enabled);
}

::taihe::string EffectImpl::getEffectId()
{
    if (!effectETS_) {
        WIDGET_LOGE("empty EffectETS");
        return "";
    }
    return effectETS_->GetEffectId().c_str();
}

::SceneResources::EffectPropertyOutputValue EffectImpl::getPropertyValue(::taihe::string_view key)
{
    if (!effectETS_) {
        WIDGET_LOGE("empty EffectETS");
        return ::SceneResources::EffectPropertyOutputValue::make_nValue();
    }
    std::shared_ptr<IPropertyProxy> propProxy = effectETS_->GetProperty(std::string(key));
    if (!propProxy) {
        WIDGET_LOGE("empty propProxy");
        return ::SceneResources::EffectPropertyOutputValue::make_nValue();
    }
    META_NS::IProperty::Ptr prop = propProxy->GetPropertyPtr();
    if (!prop) {
        WIDGET_LOGE("propProxy->GetPropertyPtr is null");
        return ::SceneResources::EffectPropertyOutputValue::make_nValue();
    }
    if (META_NS::IsCompatibleWith<bool>(prop)) {
        auto proxy = static_pointer_cast<PropertyProxy<bool>>(propProxy);
        ani_object obj = WrapBoolAsObj(proxy->GetValue());
        return ::SceneResources::EffectPropertyOutputValue::make_t_obj((uintptr_t)obj);
    }
    if (META_NS::IsCompatibleWith<int32_t>(prop)) {
        auto proxy = static_pointer_cast<PropertyProxy<int32_t>>(propProxy);
        ani_object obj = WrapIntAsObj(proxy->GetValue());
        return ::SceneResources::EffectPropertyOutputValue::make_t_obj((uintptr_t)obj);
    }
    if (META_NS::IsCompatibleWith<uint32_t>(prop)) {
        auto proxy = static_pointer_cast<PropertyProxy<uint32_t>>(propProxy);
        ani_object obj = WrapIntAsObj(proxy->GetValue());
        return ::SceneResources::EffectPropertyOutputValue::make_t_obj((uintptr_t)obj);
    }
    if (META_NS::IsCompatibleWith<float>(prop)) {
        auto proxy = static_pointer_cast<PropertyProxy<float>>(propProxy);
        ani_object obj = WrapDoubleAsObj(proxy->GetValue());
        return ::SceneResources::EffectPropertyOutputValue::make_t_obj((uintptr_t)obj);
    }
    if (META_NS::IsCompatibleWith<BASE_NS::Color>(prop)) {
        BASE_NS::Color value = META_NS::GetValue(META_NS::Property<BASE_NS::Color>(prop));
        ::SceneTypes::Color result = ::taihe::make_holder<ColorImpl, ::SceneTypes::Color>(value);
        ani_object obj = WrapColorAsObj(result);
        return ::SceneResources::EffectPropertyOutputValue::make_t_obj((uintptr_t)obj);
    }
    auto any = META_NS::GetInternalAny(prop);
    WIDGET_LOGE("Unsupported property type [%{public}s] [%{public}s]",
        any ? any->GetTypeIdString().c_str() : "<Unknown>",
        key.data());
    return ::SceneResources::EffectPropertyOutputValue::make_nValue();
}

bool EffectImpl::setPropertyValue(::taihe::string_view key, ::SceneResources::EffectPropertyInputValue const& value)
{
    if (!effectETS_) {
        WIDGET_LOGE("empty EffectETS");
        return false;
    }
    const std::string name = std::string(key);
    if (value.holds_t_obj()) {
        ani_object obj = reinterpret_cast<ani_object>(value.get_t_obj_ref());
        AniObjectType type = HandleAniObject(obj);
        switch (type) {
            case AniObjectType::TYPE_INT:
                return effectETS_->SetProperty(name, static_cast<int32_t>(ParseObjToInt(obj)));
            case AniObjectType::TYPE_DOUBLE:
                return effectETS_->SetProperty(name, static_cast<float>(ParseObjToDouble(obj)));
            case AniObjectType::TYPE_BOOLEAN:
                return effectETS_->SetProperty(name, static_cast<bool>(ParseObjToBool(obj)));
            case AniObjectType::TYPE_COLOR:
                return effectETS_->SetProperty(name, ParseObjToColor(obj));
            default:
                WIDGET_LOGE("the property type is unavaliable");
                return false;
        }
    }
    return false;
}
}  // namespace OHOS::Render3D::KITETS