/*
 * This file Copyright (C) 2008-2014 Mnemosyne LLC
 *
 * It may be used under the GNU GPL versions 2 or 3
 * or any future license endorsed by Mnemosyne LLC.
 *
 */

#pragma once

#include <inttypes.h> /* int64_t */

#include "tr-macros.h"
#include "quark.h"

TR_BEGIN_DECLS

struct evbuffer;

struct tr_error;

/**
 * @addtogroup tr_variant Variant
 *
 * An object that acts like a union for
 * integers, strings, lists, dictionaries, booleans, and floating-point numbers.
 * The structure is named tr_variant due to the historical reason that it was
 * originally tightly coupled with bencoded data. It currently supports
 * being parsed from, and serialized to, both bencoded notation and json notation.
 *
 * @{
 */

typedef enum
{
    TR_STRING_TYPE_QUARK,
    TR_STRING_TYPE_HEAP,
    TR_STRING_TYPE_BUF
}
tr_string_type;

/* these are PRIVATE IMPLEMENTATION details that should not be touched.
 * I'll probably change them just to break your code! HA HA HA!
 * it's included in the header for inlining and composition */
struct tr_variant_string
{
    tr_string_type type;
    tr_quark quark;
    size_t len;
    union
    {
        char buf[16];
        char const* str;
    }
    str;
};

/* these are PRIVATE IMPLEMENTATION details that should not be touched.
 * I'll probably change them just to break your code! HA HA HA!
 * it's included in the header for inlining and composition */
enum
{
    TR_VARIANT_TYPE_INT = 1,
    TR_VARIANT_TYPE_STR = 2,
    TR_VARIANT_TYPE_LIST = 4,
    TR_VARIANT_TYPE_DICT = 8,
    TR_VARIANT_TYPE_BOOL = 16,
    TR_VARIANT_TYPE_REAL = 32
};

/* These are PRIVATE IMPLEMENTATION details that should not be touched.
 * I'll probably change them just to break your code! HA HA HA!
 * it's included in the header for inlining and composition */
typedef struct tr_variant
{
    char type;

    tr_quark key;

    union
    {
        bool b;

        double d;

        int64_t i;

        struct tr_variant_string s;

        struct
        {
            size_t alloc;
            size_t count;
            struct tr_variant* vals;
        } l;
    }
    val;
}
tr_variant;

void tr_variantFree(tr_variant*);

/***
****  Serialization / Deserialization
***/

typedef enum
{
    TR_VARIANT_FMT_BENC,
    TR_VARIANT_FMT_JSON,
    TR_VARIANT_FMT_JSON_LEAN /* saves bandwidth by omitting all whitespace. */
}
tr_variant_fmt;

int tr_variantToFile(tr_variant const* variant, tr_variant_fmt fmt, char const* filename);

char* tr_variantToStr(tr_variant const* variant, tr_variant_fmt fmt, size_t* len);

struct evbuffer* tr_variantToBuf(tr_variant const* variant, tr_variant_fmt fmt);

/* TR_VARIANT_FMT_JSON_LEAN and TR_VARIANT_FMT_JSON are equivalent here. */
bool tr_variantFromFile(tr_variant* setme, tr_variant_fmt fmt, char const* filename, struct tr_error** error);

/* TR_VARIANT_FMT_JSON_LEAN and TR_VARIANT_FMT_JSON are equivalent here. */
int tr_variantFromBuf(tr_variant* setme, tr_variant_fmt fmt, void const* buf, size_t buflen, char const* optional_source,
    char const** setme_end);

static inline int tr_variantFromBenc(tr_variant* setme, void const* buf, size_t buflen)
{
    return tr_variantFromBuf(setme, TR_VARIANT_FMT_BENC, buf, buflen, NULL, NULL);
}

static inline int tr_variantFromBencFull(tr_variant* setme, void const* buf, size_t buflen, char const* source,
    char const** setme_end)
{
    return tr_variantFromBuf(setme, TR_VARIANT_FMT_BENC, buf, buflen, source, setme_end);
}

static inline int tr_variantFromJsonFull(tr_variant* setme, void const* buf, size_t buflen, char const* source,
    char const** setme_end)
{
    return tr_variantFromBuf(setme, TR_VARIANT_FMT_JSON, buf, buflen, source, setme_end);
}

static inline int tr_variantFromJson(tr_variant* setme, void const* buf, size_t buflen)
{
    return tr_variantFromBuf(setme, TR_VARIANT_FMT_JSON, buf, buflen, NULL, NULL);
}

static inline bool tr_variantIsType(tr_variant const* b, int type)
{
    return b != NULL && b->type == type;
}

/***
****  Strings
***/

static inline bool tr_variantIsString(tr_variant const* b)
{
    return b != NULL && b->type == TR_VARIANT_TYPE_STR;
}

bool tr_variantGetStr(tr_variant const* variant, char const** setme_str, size_t* setme_len);

void tr_variantInitStr(tr_variant* initme, void const* str, size_t str_len);
void tr_variantInitQuark(tr_variant* initme, tr_quark const quark);
void tr_variantInitRaw(tr_variant* initme, void const* raw, size_t raw_len);

bool tr_variantGetRaw(tr_variant const* variant, uint8_t const** raw_setme, size_t* len_setme);

/***
****  Real Numbers
***/

static inline bool tr_variantIsReal(tr_variant const* v)
{
    return v != NULL && v->type == TR_VARIANT_TYPE_REAL;
}

void tr_variantInitReal(tr_variant* initme, double value);
bool tr_variantGetReal(tr_variant const* variant, double* value_setme);

/***
****  Booleans
***/

static inline bool tr_variantIsBool(tr_variant const* v)
{
    return v != NULL && v->type == TR_VARIANT_TYPE_BOOL;
}

void tr_variantInitBool(tr_variant* initme, bool value);
bool tr_variantGetBool(tr_variant const* variant, bool* setme);

/***
****  Ints
***/

static inline bool tr_variantIsInt(tr_variant const* v)
{
    return v != NULL && v->type == TR_VARIANT_TYPE_INT;
}

void tr_variantInitInt(tr_variant* variant, int64_t value);
bool tr_variantGetInt(tr_variant const* val, int64_t* setme);

/***
****  Lists
***/

static inline bool tr_variantIsList(tr_variant const* v)
{
    return v != NULL && v->type == TR_VARIANT_TYPE_LIST;
}

void tr_variantInitList(tr_variant* list, size_t reserve_count);
void tr_variantListReserve(tr_variant* list, size_t reserve_count);

tr_variant* tr_variantListAdd(tr_variant* list);
tr_variant* tr_variantListAddBool(tr_variant* list, bool addme);
tr_variant* tr_variantListAddInt(tr_variant* list, int64_t addme);
tr_variant* tr_variantListAddReal(tr_variant* list, double addme);
tr_variant* tr_variantListAddStr(tr_variant* list, char const* addme);
tr_variant* tr_variantListAddQuark(tr_variant* list, tr_quark const addme);
tr_variant* tr_variantListAddRaw(tr_variant* list, void const* addme_value, size_t addme_len);
tr_variant* tr_variantListAddList(tr_variant* list, size_t reserve_count);
tr_variant* tr_variantListAddDict(tr_variant* list, size_t reserve_count);
tr_variant* tr_variantListChild(tr_variant* list, size_t pos);

bool tr_variantListRemove(tr_variant* list, size_t pos);
size_t tr_variantListSize(tr_variant const* list);

/***
****  Dictionaries
***/

static inline bool tr_variantIsDict(tr_variant const* v)
{
    return v != NULL && v->type == TR_VARIANT_TYPE_DICT;
}

void tr_variantInitDict(tr_variant* initme, size_t reserve_count);
void tr_variantDictReserve(tr_variant* dict, size_t reserve_count);
bool tr_variantDictRemove(tr_variant* dict, tr_quark const key);

tr_variant* tr_variantDictAdd(tr_variant* dict, tr_quark const key);
tr_variant* tr_variantDictAddReal(tr_variant* dict, tr_quark const key, double value);
tr_variant* tr_variantDictAddInt(tr_variant* dict, tr_quark const key, int64_t value);
tr_variant* tr_variantDictAddBool(tr_variant* dict, tr_quark const key, bool value);
tr_variant* tr_variantDictAddStr(tr_variant* dict, tr_quark const key, char const* value);
tr_variant* tr_variantDictAddQuark(tr_variant* dict, tr_quark const key, tr_quark const val);
tr_variant* tr_variantDictAddList(tr_variant* dict, tr_quark const key, size_t reserve_count);
tr_variant* tr_variantDictAddDict(tr_variant* dict, tr_quark const key, size_t reserve_count);
tr_variant* tr_variantDictSteal(tr_variant* dict, tr_quark const key, tr_variant* value);
tr_variant* tr_variantDictAddRaw(tr_variant* dict, tr_quark const key, void const* value, size_t len);

bool tr_variantDictChild(tr_variant* dict, size_t pos, tr_quark* setme_key, tr_variant** setme_value);
tr_variant* tr_variantDictFind(tr_variant* dict, tr_quark const key);
bool tr_variantDictFindList(tr_variant* dict, tr_quark const key, tr_variant** setme);
bool tr_variantDictFindDict(tr_variant* dict, tr_quark const key, tr_variant** setme_value);
bool tr_variantDictFindInt(tr_variant* dict, tr_quark const key, int64_t* setme);
bool tr_variantDictFindReal(tr_variant* dict, tr_quark const key, double* setme);
bool tr_variantDictFindBool(tr_variant* dict, tr_quark const key, bool* setme);
bool tr_variantDictFindStr(tr_variant* dict, tr_quark const key, char const** setme, size_t* len);
bool tr_variantDictFindRaw(tr_variant* dict, tr_quark const key, uint8_t const** setme_raw, size_t* setme_len);

/* this is only quasi-supported. don't rely on it too heavily outside of libT */
void tr_variantMergeDicts(tr_variant* dict_target, tr_variant const* dict_source);

/***
****
****
***/

/**
***
**/

/* @} */

TR_END_DECLS