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

#include "arc_swiper_controller.h"
#include "arc_swiper_indicator.h"
#include "ext_napi_utils.h"

#include "bridge/declarative_frontend/engine/js_converter.h"
#include "bridge/declarative_frontend/jsview/js_linear_gradient.h"
#include "core/components/swiper/swiper_indicator_theme.h"
#include "core/interfaces/native/node/node_swiper_modifier.h"
#include "core/components_ng/pattern/swiper/swiper_pattern.h"
#include "core/components_ng/base/view_stack_processor.h"
#include "core/components_ng/pattern/swiper/swiper_change_event.h"

extern const char _binary_arkui_arcswiper_abc_start[];
extern const char _binary_arkui_arcswiper_abc_end[];

namespace OHOS::Ace {
namespace {
static constexpr const size_t MAX_ARG_NUM = 10;
static constexpr const int32_t DEFAULT_DURATION = 400;
static constexpr const int32_t THREE_CLOCK_DIRECTION = 0;
static constexpr const int32_t SIX_CLOCK_DIRECTION = 1;
static constexpr const int32_t NINE_CLOCK_DIRECTION = 2;

ArkUINodeHandle GetArcSwiperNode()
{
    return reinterpret_cast<ArkUINodeHandle>(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
}

RefPtr<SwiperController> GetArcSwiperController()
{
    auto modifier = NG::NodeModifier::GetSwiperModifier();
    CHECK_NULL_RETURN(modifier, nullptr);
    return AceType::Claim(reinterpret_cast<SwiperController*>(modifier->create(true)));
}
} // namespace

napi_value JsCreate(napi_env env, napi_callback_info info)
{
    auto controller = GetArcSwiperController();
    auto node = GetArcSwiperNode();
    CHECK_NULL_RETURN(node, ExtNapiUtils::CreateNull(env));
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setIndicatorTypeNG(node, static_cast<ArkUI_Int32>(SwiperIndicatorType::ARC_DOT));

    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    if (argc > 0 && ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_object)) {
        NG::JsArcSwiperController* jsController = nullptr;
        napi_unwrap_s(env, argv[0], &ARC_SWIPER_CONTROLLER_TYPE_TAG, (void**)&jsController);
        if (jsController) {
            jsController->SetController(controller);
        }
    }

    return ExtNapiUtils::CreateNull(env);
}

napi_value JsIndex(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

    int32_t index = 0;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_number)) {
        index = ExtNapiUtils::GetCInt32(env, argv[0]);
    }
    index = index < 0 ? 0 : index;
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setIndexNG(GetArcSwiperNode(), index);

    return ExtNapiUtils::CreateNull(env);
}

SwiperArcDotParameters GetArcDotIndicatorInfo(napi_env env, napi_value value)
{
    SwiperArcDotParameters swiperParameters;
    auto pipelineContext = PipelineBase::GetCurrentContext();
    CHECK_NULL_RETURN(pipelineContext, swiperParameters);
    auto swiperIndicatorTheme = pipelineContext->GetTheme<SwiperIndicatorTheme>();
    CHECK_NULL_RETURN(swiperIndicatorTheme, swiperParameters);
    NG::JsArcSwiperIndicator* indicator = nullptr;
    napi_unwrap_s(env, value, &ARC_SWIPER_INDICATOR_TYPE_TAG, (void**)&indicator);

    // parse arcDirection
    swiperParameters.arcDirection = SwiperArcDirection::SIX_CLOCK_DIRECTION;
    if (indicator && indicator->HasArcDirection()) {
        swiperParameters.arcDirection = indicator->GetArcDirection();
    }
    // parse itemColor
    swiperParameters.itemColor =
        (indicator && indicator->HasItemColor()) ? indicator->GetItemColor() : swiperIndicatorTheme->GetArcItemColor();
    // parse selectedItemColor
    swiperParameters.selectedItemColor = (indicator && indicator->HasSelectedItemColor())
                                             ? indicator->GetSelectedItemColor()
                                             : swiperIndicatorTheme->GetArcSelectedItemColor();
    // parse containerColor
    swiperParameters.containerColor = (indicator && indicator->HasContainerColor())
                                          ? indicator->GetContainerColor()
                                          : swiperIndicatorTheme->GetArcContainerColor();
    // parse maskColor
    swiperParameters.maskColor =
        (indicator && indicator->HasMaskColor()) ? indicator->GetMaskColor() : swiperIndicatorTheme->GetArcMaskColor();

    return swiperParameters;
}

napi_value JsIndicator(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

    bool showIndicator = true;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_boolean)) {
        showIndicator = ExtNapiUtils::GetBool(env, argv[0]);
    } else {
        auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
        CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
        modifier->setIndicatorIsBooleanNG(GetArcSwiperNode(), false);
        SwiperArcDotParameters swiperParameters = GetArcDotIndicatorInfo(env, argv[0]);
        modifier->setArcDotIndicatorStyleNG(GetArcSwiperNode(), &swiperParameters);
    }

    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setShowIndicatorNG(GetArcSwiperNode(), showIndicator);

    return ExtNapiUtils::CreateNull(env);
}

napi_value JsDuration(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

    int32_t duration = DEFAULT_DURATION;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_number)) {
        duration = ExtNapiUtils::GetCInt32(env, argv[0]);
    }
    duration = duration < 0 ? DEFAULT_DURATION : duration;
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setDurationNG(GetArcSwiperNode(), duration);

    return ExtNapiUtils::CreateNull(env);
}

napi_value JsVertical(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

    bool isVertical = false;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_boolean)) {
        isVertical = ExtNapiUtils::GetBool(env, argv[0]);
    }
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setDirectionNG(GetArcSwiperNode(), static_cast<ArkUI_Int32>(isVertical ? Axis::VERTICAL : Axis::HORIZONTAL));

    return ExtNapiUtils::CreateNull(env);
}

napi_value JsDisableSwipe(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

    bool isDisable = false;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_boolean)) {
        isDisable = ExtNapiUtils::GetBool(env, argv[0]);
    }
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setDisableSwipeNG(GetArcSwiperNode(), isDisable);

    return ExtNapiUtils::CreateNull(env);
}

napi_value JsOnChange(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    if (!ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_function)) {
        return ExtNapiUtils::CreateNull(env);
    }
    auto asyncEvent = std::make_shared<NapiAsyncEvent>(env, argv[0]);
    std::function<void(const BaseEventInfo*)> onChange = [asyncEvent](const BaseEventInfo* info) {
        napi_handle_scope scope = nullptr;
        napi_open_handle_scope(asyncEvent->GetEnv(), &scope);
        CHECK_NULL_VOID(scope);

        const auto* eventInfo = TypeInfoHelper::DynamicCast<SwiperChangeEvent>(info);
        if (!eventInfo) {
            napi_close_handle_scope(asyncEvent->GetEnv(), scope);
            return;
        }
        napi_value arrayValue = ExtNapiUtils::CreateInt32(asyncEvent->GetEnv(), eventInfo->GetIndex());
        napi_value argv[1] = { arrayValue };
        asyncEvent->Call(1, argv);

        napi_close_handle_scope(asyncEvent->GetEnv(), scope);
    };
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setOnChangeNG(GetArcSwiperNode(), reinterpret_cast<void*>(&onChange));
    return ExtNapiUtils::CreateNull(env);
}

napi_value GetAnimationInfoJsObject(napi_env env, const AnimationCallbackInfo& info)
{
    napi_value jsObject = ExtNapiUtils::CreateObject(env);

    napi_value currentOffsetValue = ExtNapiUtils::CreateDouble(env, info.currentOffset.value_or(0.0f));
    ExtNapiUtils::SetNamedProperty(env, jsObject, "currentOffset", currentOffsetValue);
    napi_value targetOffsetValue = ExtNapiUtils::CreateDouble(env, info.targetOffset.value_or(0.0f));
    ExtNapiUtils::SetNamedProperty(env, jsObject, "targetOffset", targetOffsetValue);
    napi_value velocityValue = ExtNapiUtils::CreateDouble(env, info.velocity.value_or(0.0f));
    ExtNapiUtils::SetNamedProperty(env, jsObject, "velocity", velocityValue);

    return jsObject;
}

static  RefPtr<SwiperContentTransitionProxy> g_proxy = nullptr;

static napi_value FinishTransition(napi_env env, napi_callback_info info)
{
    void *data = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data));
    auto proxy = AceType::Claim(reinterpret_cast<SwiperContentTransitionProxy *>(data));
    if (proxy) {
        proxy->FinishTransition();
    }
    data = nullptr;
    g_proxy = nullptr;
    return nullptr;
}

static napi_value GetSwiperContentTransitionProxyJsObject(napi_env env,
                                                          const RefPtr<SwiperContentTransitionProxy>& proxy)
{
    napi_value jsObject = ExtNapiUtils::CreateObject(env);

    napi_value selectedIndex = ExtNapiUtils::CreateInt32(env, proxy->GetSelectedIndex());
    ExtNapiUtils::SetNamedProperty(env, jsObject, "selectedIndex", selectedIndex);
    napi_value index = ExtNapiUtils::CreateInt32(env, proxy->GetIndex());
    ExtNapiUtils::SetNamedProperty(env, jsObject, "index", index);
    napi_value position = ExtNapiUtils::CreateDouble(env, proxy->GetPosition());
    ExtNapiUtils::SetNamedProperty(env, jsObject, "position", position);
    napi_value mainAxisLength = ExtNapiUtils::CreateDouble(env, proxy->GetMainAxisLength());
    ExtNapiUtils::SetNamedProperty(env, jsObject, "mainAxisLength", mainAxisLength);
    const char* funName = "finishTransition";
    napi_value funcValue = nullptr;
    g_proxy = proxy;
    funcValue = ExtNapiUtils::CreateFunction(env, funName, strlen(funName),
                                             FinishTransition, (void*)Referenced::RawPtr(g_proxy));
    ExtNapiUtils::SetNamedProperty(env, jsObject, funName, funcValue);

    return jsObject;
}

napi_value JsOnAnimationStart(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    if (!ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_function)) {
        return ExtNapiUtils::CreateNull(env);
    }
    auto asyncEvent = std::make_shared<NapiAsyncEvent>(env, argv[0]);
    AnimationStartEvent onAnimationStart = [asyncEvent](int32_t index, int32_t targetIndex,
        const AnimationCallbackInfo& info) {
        napi_env env = asyncEvent->GetEnv();
        napi_handle_scope scope = nullptr;
        napi_open_handle_scope(env, &scope);
        CHECK_NULL_VOID(scope);

        napi_value arrayValueOne = ExtNapiUtils::CreateInt32(env, index);
        napi_value arrayValueTwo = ExtNapiUtils::CreateInt32(env, targetIndex);
        napi_value arrayValueThree = GetAnimationInfoJsObject(env, info);

        napi_value argv[3] = { arrayValueOne, arrayValueTwo, arrayValueThree };
        asyncEvent->Call(3, argv);

        napi_close_handle_scope(env, scope);
    };
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setOnAnimationStartNG(GetArcSwiperNode(), reinterpret_cast<void*>(&onAnimationStart));
    return ExtNapiUtils::CreateNull(env);
}

napi_value JsOnAnimationEnd(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    if (!ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_function)) {
        return ExtNapiUtils::CreateNull(env);
    }
    auto asyncEvent = std::make_shared<NapiAsyncEvent>(env, argv[0]);
    AnimationEndEvent onAnimationEnd = [asyncEvent](int32_t index, const AnimationCallbackInfo& info) {
        napi_env env = asyncEvent->GetEnv();
        napi_handle_scope scope = nullptr;
        napi_open_handle_scope(env, &scope);
        CHECK_NULL_VOID(scope);

        napi_value arrayValueOne = ExtNapiUtils::CreateInt32(env, index);
        napi_value arrayValueTwo = GetAnimationInfoJsObject(env, info);

        napi_value argv[2] = { arrayValueOne, arrayValueTwo };
        asyncEvent->Call(2, argv);

        napi_close_handle_scope(env, scope);
    };
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setOnAnimationEndNG(GetArcSwiperNode(), reinterpret_cast<void*>(&onAnimationEnd));
    return ExtNapiUtils::CreateNull(env);
}

napi_value JsOnGestureSwipe(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    if (!ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_function)) {
        return ExtNapiUtils::CreateNull(env);
    }
    auto asyncEvent = std::make_shared<NapiAsyncEvent>(env, argv[0]);
    GestureSwipeEvent onGestureSwipe = [asyncEvent](int32_t index, const AnimationCallbackInfo& info) {
        napi_env env = asyncEvent->GetEnv();
        napi_handle_scope scope = nullptr;
        napi_open_handle_scope(env, &scope);
        CHECK_NULL_VOID(scope);

        napi_value arrayValueOne = ExtNapiUtils::CreateInt32(env, index);
        napi_value arrayValueTwo = GetAnimationInfoJsObject(env, info);

        napi_value argv[2] = { arrayValueOne, arrayValueTwo };
        asyncEvent->Call(2, argv);

        napi_close_handle_scope(env, scope);
    };
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setOnGestureSwipeNG(GetArcSwiperNode(), reinterpret_cast<void*>(&onGestureSwipe));
    return ExtNapiUtils::CreateNull(env);
}

napi_value JsSetDigitalCrownSensitivity(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    int32_t sensitivity = 1;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_number)) {
        sensitivity = static_cast<int32_t>(ExtNapiUtils::GetCInt32(env, argv[0]));
    }

    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setDigitalCrownSensitivityNG(GetArcSwiperNode(), sensitivity);
    return ExtNapiUtils::CreateNull(env);
}

napi_value JsSetEffectMode(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    EdgeEffect edgeEffect = EdgeEffect::SPRING;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_number)) {
        edgeEffect = static_cast<EdgeEffect>(ExtNapiUtils::GetCInt32(env, argv[0]));
    }
    if (static_cast<int>(edgeEffect) < static_cast<int>(EdgeEffect::SPRING) ||
        static_cast<int>(edgeEffect) > static_cast<int>(EdgeEffect::NONE)) {
            edgeEffect = EdgeEffect::SPRING;
        }

    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setEdgeEffectNG(GetArcSwiperNode(), static_cast<ArkUI_Int32>(edgeEffect));
    return ExtNapiUtils::CreateNull(env);
}

napi_value JsSetCustomContentTransition(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    if (!ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_object)) {
        return ExtNapiUtils::CreateNull(env);
    }

    SwiperContentAnimatedTransition transitionInfo;
    napi_value jsTimeout = ExtNapiUtils::GetNamedProperty(env, argv[0], "timeout");
    if (ExtNapiUtils::CheckTypeForNapiValue(env, jsTimeout, napi_number)) {
        int32_t timeOut = static_cast<int32_t>(ExtNapiUtils::GetCInt32(env, jsTimeout));
        transitionInfo.timeout = timeOut < 0 ? 0 : timeOut;
    } else {
        transitionInfo.timeout = 0;
    }

    napi_value jsTransition = ExtNapiUtils::GetNamedProperty(env, argv[0], "transition");
    if (ExtNapiUtils::CheckTypeForNapiValue(env, jsTransition, napi_function)) {
        auto asyncEvent = std::make_shared<NapiAsyncEvent>(env, jsTransition);
        std::function<void(const RefPtr<SwiperContentTransitionProxy>&)> onTransition =
            [asyncEvent](const RefPtr<SwiperContentTransitionProxy>& proxy) {
            napi_env env = asyncEvent->GetEnv();
            napi_handle_scope scope = nullptr;
            napi_open_handle_scope(env, &scope);
            CHECK_NULL_VOID(scope);

            napi_value arrayValueOne = GetSwiperContentTransitionProxyJsObject(env, proxy);
            napi_value argv[1] = { arrayValueOne };
            asyncEvent->Call(1, argv);
            napi_close_handle_scope(env, scope);
        };
        transitionInfo.transition = std::move(onTransition);
    }
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setCustomContentTransitionNG(GetArcSwiperNode(), reinterpret_cast<void*>(&transitionInfo));
    return ExtNapiUtils::CreateNull(env);
}

napi_value JsSetDisableTransitionAnimation(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");

    bool isDisable = false;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_boolean)) {
        isDisable = ExtNapiUtils::GetBool(env, argv[0]);
    }
    auto modifier = NG::NodeModifier::GetSwiperCustomModifier();
    CHECK_NULL_RETURN(modifier, ExtNapiUtils::CreateNull(env));
    modifier->setDisableTransitionAnimationNG(GetArcSwiperNode(), isDisable);

    return ExtNapiUtils::CreateNull(env);
}

napi_value ShowNext(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, NULL));
    NG::JsArcSwiperController* controller = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_CONTROLLER_TYPE_TAG, (void**)&controller);
    napi_value objectValue = ExtNapiUtils::CreateNull(env);
    CHECK_NULL_RETURN(controller, objectValue);
    controller->ShowNext();
    return objectValue;
}

napi_value ShowPrevious(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, NULL));
    NG::JsArcSwiperController* controller = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_CONTROLLER_TYPE_TAG, (void**)&controller);
    napi_value objectValue = ExtNapiUtils::CreateNull(env);
    CHECK_NULL_RETURN(controller, objectValue);
    controller->ShowPrevious();
    return objectValue;
}

napi_value FinishAnimation(napi_env env, napi_callback_info info)
{
    size_t argc = MAX_ARG_NUM;
    napi_value thisVal = nullptr;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVal, nullptr));
    NG::JsArcSwiperController* controller = nullptr;
    napi_unwrap_s(env, thisVal, &ARC_SWIPER_CONTROLLER_TYPE_TAG, (void**)&controller);
    napi_value objectValue = ExtNapiUtils::CreateNull(env);
    CHECK_NULL_RETURN(controller, objectValue);

    CommonFunc onFinish = nullptr;
    if (argc > 0 && ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_function)) {
        auto asyncEvent = std::make_shared<NapiAsyncEvent>(env, argv[0]);
        onFinish = [asyncEvent]() {
            napi_handle_scope scope = nullptr;
            napi_open_handle_scope(asyncEvent->GetEnv(), &scope);
            CHECK_NULL_VOID(scope);
            asyncEvent->Call(0, nullptr);
            napi_close_handle_scope(asyncEvent->GetEnv(), scope);
        };
    }
    controller->FinishAnimation(onFinish);

    return objectValue;
}

napi_value ArcSwiperControllerConstructor(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
    auto controller = AceType::MakeRefPtr<NG::JsArcSwiperController>();
    CHECK_NULL_RETURN(controller, ExtNapiUtils::CreateNull(env));
    controller->IncRefCount();
    napi_wrap_s(
        env, thisVar, AceType::RawPtr(controller),
        [](napi_env env, void* data, void* hint) {
            auto* controller = reinterpret_cast<NG::JsArcSwiperController*>(data);
            controller->DecRefCount();
        },
        nullptr, &ARC_SWIPER_CONTROLLER_TYPE_TAG, nullptr);
    return thisVar;
}

napi_value ArcDotIndicatorConstructor(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
    auto indicator = AceType::MakeRefPtr<NG::JsArcSwiperIndicator>();
    CHECK_NULL_RETURN(indicator, ExtNapiUtils::CreateNull(env));
    indicator->IncRefCount();
    napi_wrap_s(
        env, thisVar, AceType::RawPtr(indicator),
        [](napi_env env, void* data, void* hint) {
            auto* indicator = reinterpret_cast<NG::JsArcSwiperIndicator*>(data);
            indicator->DecRefCount();
        },
        nullptr, &ARC_SWIPER_INDICATOR_TYPE_TAG, nullptr);
    return thisVar;
}

napi_value SetArcDirection(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NG::JsArcSwiperIndicator* indicator = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_INDICATOR_TYPE_TAG, (void**)&indicator);
    CHECK_NULL_RETURN(indicator, thisVar);
    SwiperArcDirection arcDirection = SwiperArcDirection::SIX_CLOCK_DIRECTION;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_number)) {
        arcDirection = static_cast<SwiperArcDirection>(ExtNapiUtils::GetCInt32(env, argv[0]));
    }

    if (static_cast<int>(arcDirection) < static_cast<int>(SwiperArcDirection::THREE_CLOCK_DIRECTION) ||
        static_cast<int>(arcDirection) > static_cast<int>(SwiperArcDirection::NINE_CLOCK_DIRECTION)) {
            arcDirection = SwiperArcDirection::SIX_CLOCK_DIRECTION;
        }
    indicator->SetArcDirection(arcDirection);
    return thisVar;
}

napi_value SetItemColor(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NG::JsArcSwiperIndicator* indicator = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_INDICATOR_TYPE_TAG, (void**)&indicator);
    CHECK_NULL_RETURN(indicator, thisVar);

    Color colorVal;
    if (ExtNapiUtils::ParseColor(env, argv[0], colorVal)) {
        indicator->SetItemColor(colorVal);
    } else {
        indicator->ResetItemColor();
    }
    return thisVar;
}

napi_value SetSelectedItemColor(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NG::JsArcSwiperIndicator* indicator = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_INDICATOR_TYPE_TAG, (void**)&indicator);
    CHECK_NULL_RETURN(indicator, thisVar);

    Color colorVal;
    if (ExtNapiUtils::ParseColor(env, argv[0], colorVal)) {
        indicator->SetSelectedItemColor(colorVal);
    } else {
        indicator->ResetSelectedItemColor();
    }
    return thisVar;
}

napi_value SetBackgroundColor(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NG::JsArcSwiperIndicator* indicator = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_INDICATOR_TYPE_TAG, (void**)&indicator);
    CHECK_NULL_RETURN(indicator, thisVar);

    Color colorVal;
    if (ExtNapiUtils::ParseColor(env, argv[0], colorVal)) {
        indicator->SetContainerColor(colorVal);
    } else {
        indicator->ResetContainerColor();
    }
    return thisVar;
}

bool ConvertGradientColor(napi_env env, napi_value value, NG::Gradient& gradient)
{
    auto jsValue = Framework::JsConverter::ConvertNapiValueToJsVal(value);
    Framework::JSLinearGradient* jsLinearGradient =
        Framework::JSRef<Framework::JSObject>::Cast(jsValue)->Unwrap<Framework::JSLinearGradient>();

    if (!jsLinearGradient || jsLinearGradient->GetGradient().empty()) {
        return false;
    }

    size_t size = jsLinearGradient->GetGradient().size();
    if (size == 1) {
        // If there is only one color, then this color is used for both the begin and end side.
        NG::GradientColor gradientColor;
        gradientColor.SetLinearColor(LinearColor(jsLinearGradient->GetGradient().front().first));
        gradientColor.SetDimension(jsLinearGradient->GetGradient().front().second);
        gradient.AddColor(gradientColor);
        gradient.AddColor(gradientColor);
        return true;
    }

    for (size_t colorIndex = 0; colorIndex < size; colorIndex++) {
        NG::GradientColor gradientColor;
        gradientColor.SetLinearColor(LinearColor(jsLinearGradient->GetGradient().at(colorIndex).first));
        gradientColor.SetDimension(jsLinearGradient->GetGradient().at(colorIndex).second);
        gradient.AddColor(gradientColor);
    }
    return true;
}

napi_value SetMaskColor(napi_env env, napi_callback_info info)
{
    napi_value thisVar = nullptr;
    size_t argc = MAX_ARG_NUM;
    napi_value argv[MAX_ARG_NUM] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NG::JsArcSwiperIndicator* indicator = nullptr;
    napi_unwrap_s(env, thisVar, &ARC_SWIPER_INDICATOR_TYPE_TAG, (void**)&indicator);
    CHECK_NULL_RETURN(indicator, thisVar);

    NG::Gradient gradient;
    if (ExtNapiUtils::CheckTypeForNapiValue(env, argv[0], napi_object) &&
        ConvertGradientColor(env, argv[0], gradient)) {
        indicator->SetMaskColor(gradient);
    } else {
        indicator->ResetMaskColor();
    }

    return thisVar;
}

napi_value InitArcSwiper(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("create", JsCreate),
        DECLARE_NAPI_FUNCTION("index", JsIndex),
        DECLARE_NAPI_FUNCTION("indicator", JsIndicator),
        DECLARE_NAPI_FUNCTION("duration", JsDuration),
        DECLARE_NAPI_FUNCTION("vertical", JsVertical),
        DECLARE_NAPI_FUNCTION("disableSwipe", JsDisableSwipe),
        DECLARE_NAPI_FUNCTION("onChange", JsOnChange),
        DECLARE_NAPI_FUNCTION("onAnimationStart", JsOnAnimationStart),
        DECLARE_NAPI_FUNCTION("onAnimationEnd", JsOnAnimationEnd),
        DECLARE_NAPI_FUNCTION("onGestureSwipe", JsOnGestureSwipe),
        DECLARE_NAPI_FUNCTION("digitalCrownSensitivity", JsSetDigitalCrownSensitivity),
        DECLARE_NAPI_FUNCTION("effectMode", JsSetEffectMode),
        DECLARE_NAPI_FUNCTION("customContentTransition", JsSetCustomContentTransition),
        DECLARE_NAPI_FUNCTION("disableTransitionAnimation", JsSetDisableTransitionAnimation),
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    return exports;
}

napi_value InitArcDotIndicator(napi_env env, napi_value exports)
{
    napi_value arcDotIndicatorClass = nullptr;
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("arcDirection", SetArcDirection),
        DECLARE_NAPI_FUNCTION("itemColor", SetItemColor),
        DECLARE_NAPI_FUNCTION("selectedItemColor", SetSelectedItemColor),
        DECLARE_NAPI_FUNCTION("backgroundColor", SetBackgroundColor),
        DECLARE_NAPI_FUNCTION("maskColor", SetMaskColor),
    };

    NAPI_CALL(env, napi_define_class(env, "ArcDotIndicator", NAPI_AUTO_LENGTH, ArcDotIndicatorConstructor, nullptr,
                       sizeof(desc) / sizeof(desc[0]), desc, &arcDotIndicatorClass));
    NAPI_CALL(env, napi_set_named_property(env, exports, "ArcDotIndicator", arcDotIndicatorClass));
    return exports;
}

napi_value InitArcDirection(napi_env env, napi_value exports)
{
    napi_value arcDirection = nullptr;
    napi_create_object(env, &arcDirection);
    napi_value prop = ExtNapiUtils::CreateInt32(env, THREE_CLOCK_DIRECTION);
    napi_set_named_property(env, arcDirection, "THREE_CLOCK_DIRECTION", prop);
    prop = ExtNapiUtils::CreateInt32(env, SIX_CLOCK_DIRECTION);
    napi_set_named_property(env, arcDirection, "SIX_CLOCK_DIRECTION", prop);
    prop = ExtNapiUtils::CreateInt32(env, NINE_CLOCK_DIRECTION);
    napi_set_named_property(env, arcDirection, "NINE_CLOCK_DIRECTION", prop);

    NAPI_CALL(env, napi_set_named_property(env, exports, "ArcDirection", arcDirection));
    return exports;
}

napi_value InitController(napi_env env, napi_value exports)
{
    napi_value arcSwiperControllerClass = nullptr;
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("showNext", ShowNext),
        DECLARE_NAPI_FUNCTION("showPrevious", ShowPrevious),
        DECLARE_NAPI_FUNCTION("finishAnimation", FinishAnimation),
    };

    NAPI_CALL(env, napi_define_class(env, "ArcSwiperController", NAPI_AUTO_LENGTH, ArcSwiperControllerConstructor,
                       nullptr, sizeof(desc) / sizeof(desc[0]), desc, &arcSwiperControllerClass));
    NAPI_CALL(env, napi_set_named_property(env, exports, "ArcSwiperController", arcSwiperControllerClass));
    return exports;
}

napi_value ExportArcSwiper(napi_env env, napi_value exports)
{
    InitArcSwiper(env, exports);
    InitController(env, exports);
    InitArcDotIndicator(env, exports);
    InitArcDirection(env, exports);

    return exports;
}

} // namespace OHOS::Ace

extern "C" ACE_FORCE_EXPORT void NAPI_arkui_ArcSwiper_GetABCCode(const char** buf, int* buflen)
{
    if (buf != nullptr) {
        *buf = _binary_arkui_arcswiper_abc_start;
    }
    if (buflen != nullptr) {
        *buflen = _binary_arkui_arcswiper_abc_end - _binary_arkui_arcswiper_abc_start;
    }
}

static napi_module arcSwiperModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = OHOS::Ace::ExportArcSwiper,
    .nm_modname = "arkui.ArcSwiper",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterArcSwiperModule()
{
    napi_module_register(&arcSwiperModule);
}