/* common/kmvariantdict.cpp
 *
 * Copyright (C) 2023 OpenKylin, Inc
 */

#include "KMVariantDict.h"

/* https://bugzilla.gnome.org/show_bug.cgi?id=766370 */
#if !GLIB_CHECK_VERSION(2, 49, 3)
#  define KAIMING_VARIANT_BUILDER_INITIALIZER \
      {                                       \
          {                                   \
              0,                              \
          }                                   \
      }
#  define KAIMING_VARIANT_DICT_INITIALIZER \
      {                                    \
          {                                \
              0,                           \
          }                                \
      }
#else
#  define KAIMING_VARIANT_BUILDER_INITIALIZER \
      {                                       \
          {                                   \
              {                               \
                  0,                          \
              }                               \
          }                                   \
      }
#  define KAIMING_VARIANT_DICT_INITIALIZER \
      {                                    \
          {                                \
              {                            \
                  0,                       \
              }                            \
          }                                \
      }
#endif

KMVariant::KMVariant() { }

KMVariant::KMVariant(const char *format, ...)
{
    va_list arg_list;
    va_start(arg_list, format);
    m_variant = g_variant_ref_sink(g_variant_new_va(format, nullptr, &arg_list));
    va_end(arg_list);
}

KMVariant::KMVariant(GVariant *variant)
{
    if (variant)
    {
        m_variant = g_variant_ref_sink(variant);
    }
}

// 不进行深拷贝
KMVariant::KMVariant(const KMVariant &other)
{
    m_variant = other.m_variant;
    if (m_variant)
    {
        g_variant_ref(m_variant);
    }
}

KMVariant::KMVariant(KMVariant &&other)
{
    m_variant = other.m_variant;
    other.m_variant = nullptr;
}

// 不进行深拷贝
KMVariant &KMVariant::operator=(const KMVariant &other)
{
    if (this != &other && m_variant != other.m_variant)
    {
        if (m_variant)
        {
            g_variant_unref(m_variant);
        }

        m_variant = other.m_variant;
        if (m_variant)
        {
            g_variant_ref(m_variant);
        }
    }

    return *this;
}

KMVariant &KMVariant::operator=(KMVariant &&other)
{
    std::swap(m_variant, other.m_variant);
    return *this;
}

// 不进行深拷贝
KMVariant &KMVariant::operator=(GVariant *variant)
{
    if (variant)
    {
        if (m_variant)
        {
            if (m_variant == variant)
            {
                // do nothing
            }
            else if (m_variant)
            {
                g_variant_unref(m_variant);
                m_variant = g_variant_ref_sink(variant);
            }
        }
        else
        {
            m_variant = g_variant_ref_sink(variant);
        }
    }
    else
    {
        if (m_variant)
            g_variant_unref(m_variant);
        m_variant = nullptr;
    }

    return *this;
}

KMVariant::~KMVariant()
{
    if (m_variant)
    {
        g_variant_unref(m_variant);
    }
}

/*
从容器GVariant实例中读取子项。这包括变体、可能值、数组、元组和字典条目。对任何其他类型的GVariant调用此函数都是错误的。
如果index_大于容器中子项的数量，则为错误。请参见g_variant_n_children（）。
对深度嵌套的值可能存在特定于实现的限制，这将导致单元元组作为子值返回，而不是进一步嵌套的子值。GVariant保证能够处理至少64个级别的嵌套。
*/
KMVariant KMVariant::getChildValue(std::size_t index)
{
    if (m_variant)
    {
        return g_variant_get_child_value(m_variant, index);
    }
    else
    {
        return KMVariant();
    }
}

/*
在字典中查找值GVariant。

此函数适用于类型为a｛s*｝的字典（同样适用于类型a｛o*｝，但为了清晰起见，我们只进一步讨论字符串大小写）。

如果dictionary的类型为a｛sv｝，那么expected_type字符串将指定变量内部的值类型。如果变量中的值具有不同的类型，则返回NULL。如果dictionary的值类型不是v，那么expected_type必须与值类型直接匹配，并用于直接解压缩值，否则将发生错误。

在任何一种情况下，若在字典中找不到关键字，则返回NULL。

如果找到了键并且值的类型正确，则返回该键。如果指定了expected_type，那么任何非NULL返回值都将具有此类型。

该功能目前通过线性扫描实现。如果您计划进行多次查找，那么GVariantDict可能会更高效。
*/
KMVariant KMVariant::lookupValue(const char *key, const char *format)
{
    if (m_variant)
    {
        return g_variant_lookup_value(m_variant, key, G_VARIANT_TYPE(format));
    }
    else
    {
        return KMVariant();
    }
}

/*
此函数是g_variant_lookup_value（）和g_variant_get（）的包装器。在返回NULL的情况下，此函数将返回FALSE。否则，它将对返回的值进行解包并返回TRUE。
format_string确定用于拆包值的C类型，还确定值是被复制还是被借用，请参阅关于GVariant格式字符串的一节。
该功能目前通过线性扫描实现。如果您计划进行多次查找，那么GVariantDict可能会更高效。
*/
bool KMVariant::lookup(const char *key, std::string &value)
{
    if (!m_variant)
    {
        return false;
    }

    // g_autofree char * contents = nullptr;
    // bool ret = g_variant_lookup(m_variant, key, "s", &contents);
    // if (ret && contents)
    //     value = contents;

    char *contents = nullptr;
    bool ret = g_variant_lookup(m_variant, key, "&s", &contents);
    if (ret && contents)
    {
        value = contents;
    }

    return ret;
}

bool KMVariant::lookup(const char *key, std::uint64_t &value)
{
    if (!m_variant)
    {
        return false;
    }

    return g_variant_lookup(m_variant, key, "t", &value);
}

bool KMVariant::lookup(const char *key, int &value)
{
    if (!m_variant)
    {
        return false;
    }

    return g_variant_lookup(m_variant, key, "i", &value);
}

bool KMVariant::lookup(const char *key, std::vector<std::string> &value)
{
    if (!m_variant)
    {
        return false;
    }

    g_autoptr(GVariantIter) iter = nullptr;
    const char *element = nullptr;
    if (g_variant_lookup(m_variant, key, "as", &iter))
    {
        while (g_variant_iter_next(iter, "&s", &element))
        {
            value.push_back(element);
        }

        return true;
    }

    return false;
}

/*
确定容器GVariant实例中的子级数。这包括变体、可能值、数组、元组和字典条目。对任何其他类型的GVariant调用此函数都是错误的。

对于变体，返回值始终为1。对于可能具有类型的值，它总是零或一。对于数组，它是数组的长度。对于元组，它是元组项的数量（仅取决于类型）。对于字典条目，它总是2

这个函数是O（1）。
*/
std::size_t KMVariant::childrenCount()
{
    if (!m_variant)
    {
        return 0;
    }

    return g_variant_n_children(m_variant);
}

/*
确定使用g_variant_store（）存储值所需的字节数。

如果值具有固定大小的类型，则此函数始终返回该固定大小。

如果值已经是串行形式，或者已经计算了大小（即：以前调用过这个函数），那么这个函数就是O（1）。否则，计算大小，这是一个在所涉及的值的数量上近似为O（n）的运算。
*/
std::size_t KMVariant::getSize()
{
    if (!m_variant)
    {
        return 0;
    }

    return g_variant_get_size(m_variant);
}

/*
返回指向GVariant实例的串行形式的指针。如果从不受信任的源读取，则返回的数据可能不是完全规范化的形式。不得释放返回的数据；只要值存在，它就一直有效。

如果值是从损坏的串行化容器中取消序列化的固定大小的值，则可能返回NULL。在这种情况下，正确的做法通常是使用适当数量的nul字节来代替值。若值的大小不是固定的，那个么就永远不会返回NULL。

在值已经是串行形式的情况下，这个函数是O（1）。如果该值还不是串行化的形式，则串行化隐式发生，并且结果的大小大约为O（n）。

要取消序列化此函数返回的数据，除了串行数据外，您还必须知道GVariant的类型，以及（如果机器可能不同）存储它的机器的字节序。
因此，包含串行GVariant在内的文件格式或网络消息必须隐式（例如“文件始终包含G_VARIANT_TYPE_VARIANT，并且始终以小字节序排列”）或显式（通过存储串行数据之外的类型和/或字节序）包含此信息。
*/
const void *KMVariant::getData()
{
    if (!m_variant)
    {
        return nullptr;
    }

    return g_variant_get_data(m_variant);
}

/*
 *  返回指向GVariant实例的串行形式的指针。此函数的语义与getData（）完全相同，只是返回的GBytes包含对变体数据的引用。需要调用方释放返回的GBytes*。
 */
GBytes *KMVariant::getDataAsBytes()
{
    if (!m_variant)
    {
        return nullptr;
    }

    return g_variant_get_data_as_bytes(m_variant);
}

void KMVariant::clear()
{
    if (m_variant)
    {
        g_variant_unref(m_variant);
        m_variant = nullptr;
    }
}

KMVariantBuilder::KMVariantBuilder(const std::string &formatOfType)
{
    m_formatOfType = formatOfType;
    m_builder = g_variant_builder_new(G_VARIANT_TYPE(formatOfType.c_str()));
}

KMVariantBuilder::~KMVariantBuilder()
{
    if (m_builder)
    {
        g_variant_builder_unref(m_builder);
    }
}

/**
 * @brief : 结束生成器过程并返回构造的值
 */
KMVariant KMVariantBuilder::end()
{
    KMVariant ret = g_variant_builder_end(m_builder);

    g_variant_builder_init(m_builder, G_VARIANT_TYPE(m_formatOfType.c_str()));

    return ret;
}

void KMVariantBuilder::addValue(KMVariant value)
{
    g_variant_builder_add_value(m_builder, value.get());
}

class KMVariantDict::Private
{
public:
    Private();
    ~Private();

    GVariantDict m_variantDict = KAIMING_VARIANT_DICT_INITIALIZER;
    bool m_isInit = true;
};

KMVariantDict::Private::Private()
{
    g_variant_dict_init(&m_variantDict, NULL);
}

KMVariantDict::Private::~Private()
{
    if (m_isInit)
    {
        g_variant_dict_clear(&m_variantDict);
    }
}

KMVariantDict::KMVariantDict()
    : d(std::make_shared<Private>())
{
}

KMVariantDict::~KMVariantDict() { }

void KMVariantDict::insert(const std::string &key, const std::string &value)
{
    if (!d->m_isInit)
    {
        g_variant_dict_init(&d->m_variantDict, NULL);
    }

    g_variant_dict_insert_value(&d->m_variantDict, key.c_str(), g_variant_new_string(value.c_str()));
}

void KMVariantDict::insert(const std::string &key, std::uint64_t value)
{
    if (!d->m_isInit)
    {
        g_variant_dict_init(&d->m_variantDict, NULL);
    }

    g_variant_dict_insert_value(&d->m_variantDict, key.c_str(), g_variant_new_uint64(value));
}

void KMVariantDict::insert(const std::string &key, std::int64_t value)
{
    if (!d->m_isInit)
    {
        g_variant_dict_init(&d->m_variantDict, NULL);
    }

    g_variant_dict_insert_value(&d->m_variantDict, key.c_str(), g_variant_new_int64(value));
}

void KMVariantDict::insert(const std::string &key, int value)
{
    if (!d->m_isInit)
    {
        g_variant_dict_init(&d->m_variantDict, NULL);
    }

    g_variant_dict_insert_value(&d->m_variantDict, key.c_str(), g_variant_new_int32(value));
}

void KMVariantDict::insert(const std::string &key, KMVariant value)
{
    if (!d->m_isInit)
    {
        g_variant_dict_init(&d->m_variantDict, NULL);
    }

    g_variant_dict_insert_value(&d->m_variantDict, key.c_str(), value.get());
}

bool KMVariantDict::remove(const std::string &key)
{
    if (!d->m_isInit)
    {
        return true;
    }

    return g_variant_dict_remove(&d->m_variantDict, key.c_str());
}

bool KMVariantDict::contains(const std::string &key) const
{
    if (!d->m_isInit)
    {
        return false;
    }

    return g_variant_dict_contains(&d->m_variantDict, key.c_str());
}

GVariantDict *KMVariantDict::get()
{
    return &d->m_variantDict;
}

KMVariantDict::operator bool() const
{
    if (!d && d->m_isInit)
    {
        return true;
    }

    return false;
}

/*
以G_VARIANT_TYPE_VARDICT类型的GVariant返回dict的当前值，并在此过程中清除它。

在此调用之后，不允许以任何方式使用dict，除了引用计数操作（在堆分配GVariantDict的情况下）或通过g_variant_dict_init（）重新初始化dict（在堆栈分配的情形下）。
*/
KMVariant KMVariantDict::end()
{
    if (!d->m_isInit)
    {
        return KMVariant();
    }

    d->m_isInit = false;
    return g_variant_dict_end(&d->m_variantDict);
}
