// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

#pragma once
#include "slint-testing.h"
#include <cmath>
#include <iostream>

// this file contains function useful for internal testing

namespace slint::private_api::testing {

inline void mock_elapsed_time(int64_t time_in_ms)
{
    cbindgen_private::slint_mock_elapsed_time(time_in_ms);
}

inline uint64_t get_mocked_time()
{
    return cbindgen_private::slint_get_mocked_time();
}

template<typename Component>
inline void send_mouse_click(const Component *component, float x, float y)
{
    cbindgen_private::slint_send_mouse_click(x, y, &component->window().window_handle());
}

template<typename Component>
inline void send_keyboard_key_text(const Component *component, const slint::SharedString &text,
                                   bool pressed)
{
    cbindgen_private::slint_send_keyboard_key_text(&text, pressed,
                                                   &component->window().window_handle());
}

template<typename Component>
inline void send_keyboard_char(const Component *component, const slint::SharedString &str,
                               bool pressed)
{
    cbindgen_private::slint_send_keyboard_char(&str, pressed, &component->window().window_handle());
}

template<typename Component>
inline void send_key_combo(const Component *component, std::vector<slint::SharedString> keys)
{
    for (const auto &key : keys) {
        send_keyboard_key_text(component, key, true);
    }
    std::reverse(std::begin(keys), std::end(keys));
    for (const auto &key : keys) {
        send_keyboard_key_text(component, key, false);
    }
}

template<typename Component>
inline void send_keyboard_string_sequence(const Component *component,
                                          const slint::SharedString &str)
{
    cbindgen_private::slint_send_keyboard_string_sequence(&str,
                                                          &component->window().window_handle());
}

inline void set_system_accent_color(slint::Color color)
{
    cbindgen_private::slint_testing_set_system_accent_color(color.as_argb_encoded());
}

template<typename Component>
inline void set_use_native_popup(const Component *component, bool native)
{
    cbindgen_private::slint_testing_use_native_popup(&component->window().window_handle(), native);
}

template<typename Component>
inline size_t active_popup_count(const Component *component)
{
    return cbindgen_private::slint_testing_active_popup_count(&component->window().window_handle());
}

#define assert_eq(A, B)                                                                            \
    slint::private_api::testing::assert_eq_impl(A, B, #A, #B, __FILE__, __LINE__)

template<typename A, std::equality_comparable_with<A> B>
void assert_eq_impl(const A &a, const B &b, const char *a_str, const char *b_str, const char *file,
                    int line)
{
    bool nok = true;
    if constexpr (std::is_integral_v<A> && std::is_integral_v<B>) {
        // Do a cast to the common type to avoid warning about signed vs. unsigned compare
        using T = std::common_type_t<A, B>;
        nok = T(a) != T(b);
    } else if constexpr (std::is_floating_point_v<A> && std::is_floating_point_v<B>) {
        const double dEpsilon = 0.000001; // or some other small number
        nok = std::fabs(a - b) > dEpsilon * std::fabs(a);
    } else {
        nok = a != b;
    }
    if (nok) {
        std::cerr << file << ":" << line << ": assert_eq FAILED!\n"
                  << a_str << ": " << a << "\n"
                  << b_str << ": " << b << std::endl;
        std::abort();
    }
}

} // namespace slint::private_api::testing
