// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef CLASSDECORATOR_P_H
#define CLASSDECORATOR_P_H

#include <pysidemacros.h>

#include <sbkpython.h>

#include <array>
#include <string>

/// Helpers for class decorators with parameters
namespace PySide::ClassDecorator {

/// Base class for private objects of class decorators with parameters
class PYSIDE_API DecoratorPrivate
{
public:
    virtual ~DecoratorPrivate();

    /// Virtual function which is passed the decorated class type
    /// \param args Decorated class type argument
    /// \return class with reference count increased if the call was successful,
    ///         else nullptr
    virtual PyObject *tp_call(PyObject *self, PyObject *args, PyObject * /* kw */) = 0;

    /// Virtual function which is passed the decorator parameters
    /// \param args Decorator arguments
    /// \return 0 if the parameters are correct
    virtual int tp_init(PyObject *self, PyObject *args, PyObject *kwds) = 0;
    virtual const char *name() const = 0;

    /// Helper that returns DecoratorPrivate instance from a PyObject
    template <class DerivedPrivate>
    static DerivedPrivate *get(PyObject *o)
    { return static_cast<DerivedPrivate *>(DecoratorPrivate::getPrivate(o)); }

protected:
    /// Check mode for the arguments of the call operator
    enum class CheckMode { None, WrappedType, QObjectType };

    static DecoratorPrivate *getPrivate(PyObject *o);

    /// Helper for checking the arguments of the call operator
    /// \param args Arguments
    /// \param checkMode Type check mode
    /// \return The type object extracted from args tuple (borrowed reference)
    ///         if the argument is a matching type
    PyObject *tp_call_check(PyObject *args,
                            CheckMode checkMode = CheckMode::QObjectType) const;
};

/// Base class for private objects of class decorator with a string parameter
class PYSIDE_API StringDecoratorPrivate : public DecoratorPrivate
{
public:
    /// Init function that retrieves the string parameter using convertToString()
    int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;

    const std::string &string() const { return m_string; }

protected:
    /// Helper function that retrieves the string parameter
    /// \param self self
    /// \param args Arguments
    /// \return 0 if the parameter is correct, else -1 (for tp_init())
    int convertToString(PyObject *self, PyObject *args);

private:
    std::string m_string;
};

/// Base class for private objects of class decorator with a type parameter
class PYSIDE_API TypeDecoratorPrivate : public DecoratorPrivate
{
public:
    /// Init function that retrieves the type parameter using convertToType()
    int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;

    PyTypeObject *type() const { return m_type; }

protected:
    /// Helper function that retrieves the type parameter
    /// \param self self
    /// \param args Arguments
    /// \return 0 if the parameter is correct, else -1 (for tp_init())
    int convertToType(PyObject *self, PyObject *args);

private:
    PyTypeObject *m_type = nullptr;
};

} // namespace PySide::ClassDecorator

extern "C"
{
LIBSHIBOKEN_API void Sbk_object_dealloc(PyObject *self);

/// Python type for class decorators with DecoratorPrivate
struct PYSIDE_API PySideClassDecorator
{
    PyObject_HEAD
    PySide::ClassDecorator::DecoratorPrivate *d;
};
};

namespace PySide::ClassDecorator {

/// Helper template providing the methods (slots) for class decorators
template <class DecoratorPrivate>
struct Methods
{
    static PyObject *tp_new(PyTypeObject *subtype)
    {
        auto *result = reinterpret_cast<PySideClassDecorator *>(subtype->tp_alloc(subtype, 0));
        result->d = new DecoratorPrivate;
        return reinterpret_cast<PyObject *>(result);
    }

    static void tp_free(void *self)
    {
        auto pySelf = reinterpret_cast<PyObject *>(self);
        auto decorator = reinterpret_cast<PySideClassDecorator *>(self);
        delete decorator->d;
        Py_TYPE(pySelf)->tp_base->tp_free(self);
    }

    static PyObject *tp_call(PyObject *self, PyObject *args, PyObject *kwds)
    {
        auto *decorator = reinterpret_cast<PySideClassDecorator *>(self);
        return decorator->d->tp_call(self, args, kwds);
    }

    static int tp_init(PyObject *self, PyObject *args, PyObject *kwds)
    {
        auto *decorator = reinterpret_cast<PySideClassDecorator *>(self);
        return decorator->d->tp_init(self, args, kwds);
    }

    using TypeSlots = std::array<PyType_Slot, 6>;

    static TypeSlots typeSlots()
    {
        return { {{Py_tp_call, reinterpret_cast<void *>(tp_call)},
                  {Py_tp_init, reinterpret_cast<void *>(tp_init)},
                  {Py_tp_new, reinterpret_cast<void *>(tp_new)},
                  {Py_tp_free, reinterpret_cast<void *>(tp_free)},
                  {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)},
                  {0, nullptr}}
               };
    }
};

} // namespace PySide::ClassDecorator

#endif // CLASSDECORATOR_P_H
