// Copyright (C) 2018-2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

#include <pybind11/pybind11.h>

#include <filesystem>
#include <variant>

#ifdef _MSC_VER
// Warning occurred at the junction of pybind11
// and the templates inside ov::Any.
// Generated by operator `==` inside pybind::handle.
#pragma warning( push )
    #pragma warning( disable: 4996 )
#elif defined(__GNUC__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "openvino/core/any.hpp"
#ifdef _MSC_VER
    #pragma warning( pop )
#elif defined(__GNUC__)
    #pragma GCC diagnostic pop
#endif

#include "openvino/core/type/element_type.hpp"
#include "openvino/runtime/properties.hpp"
#include "openvino/runtime/auto/properties.hpp"
#include "openvino/pass/serialize.hpp"

namespace py = pybind11;

namespace Common {
namespace utils {

class OutPyBuffer : public std::streambuf {
public:
    OutPyBuffer(py::object bytes_io_stream) : m_py_stream{std::move(bytes_io_stream)} {}

protected:
    std::streamsize xsputn(const char_type* s, std::streamsize n) override {
        return m_py_stream.attr("write")(py::bytes(s, n)).cast<std::streamsize>();
    }

    int_type overflow(int_type c) override {
        char as_char = c;
        if (m_py_stream.attr("write")(py::bytes(&as_char, 1)).cast<int>() != 1) {
            return traits_type::eof();
        }
        return c;
    }

    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) override {
        const int python_direction = [&]{
            switch (dir) {
                case std::ios_base::beg:
                    return 0;
                case std::ios_base::cur:
                    return 1;
                case std::ios_base::end:
                    return 2;
                default:
                    return -1;
            }
        }();
        if (python_direction == -1) {
            return pos_type(off_type(-1));
        }
        const auto abs_pos = m_py_stream.attr("seek")(off, python_direction).cast<int64_t>();
        return pos_type(abs_pos);
    }

    pos_type seekpos(pos_type pos, std::ios_base::openmode which) override {
        return seekoff(pos, std::ios_base::beg, which);
    }

private:
    py::object m_py_stream;
};

    enum class PY_TYPE : int { UNKNOWN = 0, STR, INT, FLOAT, BOOL, PARTIAL_SHAPE, MODEL_DISTRIBUTION_POLICY };

    struct EmptyList {};

    template <typename T>
    // Checks element type of list and set
    PY_TYPE check_container_element_type(const T& container);

    py::object from_ov_any_no_leaves(const ov::Any& any);

    py::object from_ov_any_map_no_leaves(const ov::Any& almost_map);

    py::object from_ov_any_map(const ov::AnyMap& map);

    py::object from_ov_any(const ov::Any& any);

    std::map<std::string, ov::Any> properties_to_any_map(const std::map<std::string, py::object>& properties);

    std::string convert_path_to_string(const py::object& path);

    std::shared_ptr<ov::Model> convert_to_model(const py::object& obj);

    void deprecation_warning(const std::string& function_name, const std::string& version = std::string(), const std::string& message = std::string(), int stacklevel=2);

    void raise_not_implemented();

    bool py_object_is_any_map(const py::object& py_obj);

    ov::AnyMap py_object_to_any_map(const py::object& py_obj);

    std::unordered_map<std::string, ov::Any> py_object_to_unordered_any_map(const py::object& py_obj);

    ov::Any py_object_to_any(const py::object& py_obj);

    ov::pass::Serialize::Version convert_to_version(const std::string& version);

    std::shared_ptr<py::function> wrap_pyfunction(py::function f_callback);

    std::shared_ptr<py::object> wrap_pyobject_to_sp(py::object obj);

    std::filesystem::path to_fs_path(const py::object& path);
}; // namespace utils
}; // namespace Common

namespace pybind11 {
namespace ov_extension {
    void conditional_keep_alive_impl(size_t Nurse, size_t Patient, size_t Condition, detail::function_call &call, handle ret);

    // Keep patient alive while nurse lives
    template <size_t Nurse, size_t Patient, size_t Condition>
    struct conditional_keep_alive {};

}; // namespace ov_extension

    // Process a conditional_keep_alive call policy -- invokes conditional_keep_alive_impl during the the pre-call handler
    template <size_t Nurse, size_t Patient, size_t Condition>
    struct detail::process_attribute<ov_extension::conditional_keep_alive<Nurse, Patient, Condition>>
        : public detail::process_attribute_default<ov_extension::conditional_keep_alive<Nurse, Patient, Condition>> {
        static void precall(function_call &call) {
            ov_extension::conditional_keep_alive_impl(Nurse, Patient, Condition, call, handle());
        }
    };

}; // namespace pybind11
