/*
 * 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 <ani.h>
#include <array>
#include <iostream>
#include <string>
#include <vector>

#include "frameworks/base/utils/utils.h"
#include "frameworks/bridge/common/utils/engine_helper.h"
#include "frameworks/core/components_ng/pattern/toast/toast_layout_property.h"

std::string ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str)
{
    ani_size strSize;
    env->String_GetUTF8Size(ani_str, &strSize);
   
    std::vector<char> buffer(strSize + 1);
    char* utf8Buffer = buffer.data();

    ani_size bytesWritten = 0;
    env->String_GetUTF8(ani_str, utf8Buffer, strSize + 1, &bytesWritten);
    
    utf8Buffer[bytesWritten] = '\0';
    std::string content = std::string(utf8Buffer);
    return content;
}
bool GetToastMessage(ani_env *env, ani_object options, std::string& messageString)
{
    static const char *className = "@ohos.prompt.prompt.ShowToastOptions";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        return false;
    }

    ani_ref message_ref;
    if (ANI_OK != env->Object_GetPropertyByName_Ref(options, "message", &message_ref)) {
        return false;
    }

    auto message_str = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(message_ref));
    messageString = message_str;
    return true;
}
bool GetToastDuration(ani_env *env, ani_object options, int32_t& durationInt)
{
    static const char *className = "@ohos.prompt.prompt.ShowToastOptions";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return false;
    }
    ani_ref duration_ref;
    if (ANI_OK != env->Object_GetPropertyByName_Ref(options, "duration", &duration_ref)) {
        return false;
    }
    ani_boolean isUndefined;
    if (ANI_OK != env->Reference_IsUndefined(duration_ref, &isUndefined)) {
        return false;
    }
    if (isUndefined) {
        return false;
    }
    ani_double duration;
    if (ANI_OK != env->Object_CallMethodByName_Double(
        static_cast<ani_object>(duration_ref), "toDouble", ":d", &duration)) {
        return false;
    }
    durationInt = static_cast<int32_t>(duration);
    return true;
}
bool GetToastBottom(ani_env *env, ani_object options, std::string& bottomString)
{
    static const char *className = "@ohos.prompt.prompt.ShowToastOptions";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        return false;
    }
    ani_ref bottom_ref;
    if (ANI_OK != env->Object_GetPropertyByName_Ref(options, "bottom", &bottom_ref)) {
        return false;
    }
    ani_boolean isUndefined;
    if (ANI_OK != env->Reference_IsUndefined(bottom_ref, &isUndefined)) {
        return false;
    }
    if (isUndefined) {
        return false;
    }
    ani_class stringClass;
    env->FindClass("std.core.String", &stringClass);

    ani_class numberClass;
    env->FindClass("std.core.Numeric", &numberClass);

    ani_boolean isString;
    env->Object_InstanceOf(static_cast<ani_object>(bottom_ref), stringClass, &isString);
    if (isString) {
        auto stringContent = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(bottom_ref));
        bottomString = stringContent;
        return true;
    }
    ani_boolean isNumber;
    env->Object_InstanceOf(static_cast<ani_object>(bottom_ref), numberClass, &isNumber);
    if (isNumber) {
        ani_double duration;
        if (ANI_OK !=env->Object_CallMethodByName_Double(
            static_cast<ani_object>(bottom_ref), "toDouble", ":d", &duration)) {
            return false;
        }
        double botm = static_cast<double>(duration);
        bottomString = std::to_string(botm);
    }
    return true;
}
static void showToast([[maybe_unused]] ani_env* env, ani_object options)
{
    std::string messageInfo;
    std::string bottomInfo;
    int32_t durationInfo = 0;
    ani_boolean isUndefined;
    env->Reference_IsUndefined(options, &isUndefined);
    if (isUndefined) {
        return;
    }
    GetToastMessage(env, options, messageInfo);
    std::cout << "GetToastMessage :" << messageInfo.c_str() << std::endl;
    GetToastDuration(env, options, durationInfo);
    std::cout << "GetToastDuration :" << durationInfo << std::endl;
    GetToastBottom(env, options, bottomInfo);
    std::cout << "GetToastBottom :" << bottomInfo.c_str() << std::endl;
    auto toastInfo = OHOS::Ace::NG::ToastInfo { .message = messageInfo,
        .duration = durationInfo,
        .bottom = bottomInfo,
        .showMode = OHOS::Ace::NG::ToastShowMode::DEFAULT,
        .alignment = -1,
        .offset = std::nullopt };
    auto delegate = OHOS::Ace::EngineHelper::GetCurrentDelegateSafely();
    if (delegate) {
        delegate->ShowToast(toastInfo, nullptr);
    }
}
ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        return ANI_ERROR;
    }

    ani_namespace ns;
    if (ANI_OK != env->FindNamespace("@ohos.prompt.prompt", &ns)) {
        return ANI_ERROR;
    }
    std::array methods = {
        ani_native_function {"showToast", nullptr, reinterpret_cast<void *>(showToast)},
    };
    if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) {
        return ANI_ERROR;
    }

    *result = ANI_VERSION_1;
    return ANI_OK;
}