
%module ryml

%include "stdint.i"

//-----------------------------------------------------------------------------
// this block will be pasted verbatim in the generated C++ source file

%{
// specifies that the resulting C file should be built as a python
// extension, inserting the module init code
#define SWIG_FILE_WITH_INIT

#include <c4/yml/yml.hpp>
#include <c4/yml/error.def.hpp>

namespace c4 {
namespace yml {

using substr = c4::substr;
using csubstr = c4::csubstr;

} /* namespace yml */
} /* namespace c4 */

%}


//-----------------------------------------------------------------------------

%apply (const char *STRING, size_t LENGTH) { (const char *str, size_t len) };
%apply (char *STRING, size_t LENGTH) { (char *str, size_t len) };
%newobject emit_malloc;

%typemap(in) c4::substr {
#if defined(SWIGPYTHON)
  if($input != Py_None)
  {
      if(PyMemoryView_Check($input)) // is it a memoryview?
      {
          Py_buffer const* view = PyMemoryView_GET_BUFFER($input);
          $1 = c4::substr((char*)view->buf, view->len);
      }
      else
      {
          int ok = PyObject_CheckBuffer($input);
          Py_buffer view;
          ok = ok && (0 == PyObject_GetBuffer($input, &view, PyBUF_SIMPLE | PyBUF_WRITABLE));
          if(ok)
          {
              $1 = c4::substr((char*)view.buf, view.len);
              PyBuffer_Release(&view);
          }
          else
          {
              PyErr_SetString(PyExc_TypeError, "substr: could not get mutable memory - have you passed an imutable type such as str or bytes?");
              SWIG_fail;
          }
      }
  }
  else
  {
      $1 = c4::substr(nullptr, size_t(0));
  }
#else
#error no "in" typemap defined for this export language
#endif
};

%typemap(in) c4::csubstr {
#if defined(SWIGPYTHON)
  if($input != Py_None)
  {
      if(PyMemoryView_Check($input)) // is it a memoryview?
      {
          Py_buffer const* view = PyMemoryView_GET_BUFFER($input);
          $1 = c4::csubstr((const char*)view->buf, view->len);
      }
      else
      {
          Py_buffer view;
          view.buf = nullptr;
          int ok = PyObject_CheckBuffer($input);
          ok = ok && (0 == PyObject_GetBuffer($input, &view, PyBUF_SIMPLE));
          if(ok)
          {
              $1 = c4::csubstr((const char*)view.buf, view.len);
              PyBuffer_Release(&view);
          }
          else
          {
              // https://stackoverflow.com/questions/36098984/python-3-3-c-api-and-utf-8-strings
              Py_ssize_t sz = 0;
              const char *buf = PyUnicode_AsUTF8AndSize($input, &sz);
              if(buf || sz == 0)
              {
                  $1 = c4::csubstr(buf, sz);
              }
              else
              {
                  PyErr_SetString(PyExc_TypeError, "csubstr: could not get readonly memory from python object");
                  SWIG_fail;
              }
          }
      }
  }
  else
  {
      $1 = c4::csubstr(nullptr, size_t(0));
  }
#else
#error no "in" typemap defined for this export language
#endif
};
%typemap(in) csubstr = c4::csubstr;
%typemap(in) substr = c4::substr;
%typemap(in) c4::yml::csubstr = c4::csubstr;
%typemap(in) c4::yml::substr = c4::substr;

// Copy the typecheck code for "char *".
%typemap(typecheck) c4::substr = char *;
%typemap(typecheck) c4::csubstr = const char *;


//-----------------------------------------------------------------------------

%typemap(out) c4::csubstr {
#if defined(SWIGPYTHON)
  if($1.str == nullptr) {
    $result = Py_None;
    Py_INCREF($result);
  } else {
    PyObject *obj = PyMemoryView_FromMemory((char*)$1.str, $1.len, PyBUF_READ);
    if( ! obj)
    {
        PyErr_SetString(PyExc_TypeError, "could not get readonly memory from c4::csubstr - have you passed a str?");
        SWIG_fail;
    }
    $result = obj;
  }
#else
#error no "out" typemap defined for this export language
#endif
};


//-----------------------------------------------------------------------------

// Language independent exception handler.
// KEEP THIS BEFORE THE FOLLOWING FUNCTIONS!
// see https://stackoverflow.com/a/61621747
%include <std_string.i>
%include <std_except.i>
%include <exception.i>
%include <pyabc.i>
%pythonabc(c4__yml__ExceptionParse, "Exception")
%pythonabc(c4__yml__ExceptionVisit, "Exception")
%pythonabc(c4__yml__ExceptionBasic, "Exception")
%exceptionclass c4::yml::ExceptionBasic;
%exceptionclass c4::yml::ExceptionParse;
%exceptionclass c4::yml::ExceptionVisit;
%exception {
    try {
        $action
    } catch (c4::yml::ExceptionParse const& exc) {
        SWIG_Python_Raise(SWIG_NewPointerObj(
                              (new c4::yml::ExceptionParse(exc)),
                              SWIGTYPE_p_c4__yml__ExceptionParse,
                              SWIG_POINTER_OWN),
                          "ExceptionParse",
                          SWIGTYPE_p_c4__yml__ExceptionParse);
        //std::string err = c4::yml::format_exc<std::string>(exc);
        //PyErr_SetString(SWIG_Python_ExceptionType(SWIGTYPE_p_c4__yml__ExceptionParse), c4::csubstr(err.data(), err.size()), exc.errdata_parse);
        SWIG_fail;
    } catch (c4::yml::ExceptionVisit const& exc) {
        SWIG_Python_Raise(SWIG_NewPointerObj(
                              (new c4::yml::ExceptionVisit(exc)),
                              SWIGTYPE_p_c4__yml__ExceptionVisit,
                              SWIG_POINTER_OWN),
                          "ExceptionVisit",
                          SWIGTYPE_p_c4__yml__ExceptionVisit);
        //std::string err = c4::yml::format_exc<std::string>(exc);
        //PyErr_SetString(SWIG_Python_ExceptionType(SWIGTYPE_p_c4__yml__ExceptionVisit), c4::csubstr(err.data(), err.size()), exc.errdata_visit);
        SWIG_fail;
    } catch (c4::yml::ExceptionBasic const& exc) {
        //SWIG_exception(SWIGTYPE_p_c4__yml__ExceptionBasic, c4::csubstr(exc.str, strlen(exc.str)), exc.errdata_basic);
        SWIG_Python_Raise(SWIG_NewPointerObj(
                              (new c4::yml::ExceptionBasic(exc)),
                              SWIGTYPE_p_c4__yml__ExceptionBasic,
                              SWIG_POINTER_OWN),
                          "ExceptionBasic",
                          SWIGTYPE_p_c4__yml__ExceptionBasic);
        //std::string err = c4::yml::format_exc<std::string>(exc);
        //PyErr_SetString(SWIG_Python_ExceptionType(SWIGTYPE_p_c4__yml__ExceptionBasic), c4::csubstr(err.data(), err.size()), exc.errdata_basic);
        SWIG_fail;
    } catch(std::exception const& e) {
        SWIG_exception(SWIG_RuntimeError, e.what());
    } catch(...) {
        SWIG_exception(SWIG_UnknownError, "unknown error");
    }
}


//-----------------------------------------------------------------------------

%inline %{

void parse_csubstr(c4::csubstr s, c4::yml::Tree *t)
{
    c4::yml::parse_in_arena(s, t);
}

void parse_substr(c4::substr s, c4::yml::Tree *t)
{
    c4::yml::parse_in_place(s, t);
}

char * emit_yaml_malloc(c4::yml::Tree const& t, size_t id)
{
    c4::substr buf;
    c4::substr ret = c4::yml::emit_yaml(t, id, buf, /*error_on_excess*/false);
    if(ret.str == nullptr && ret.len > 0)
    {
        // Use new[]+delete[] in SWIG.
        char * alloc = new char[ret.len + 1]; // we'll return a c-string and not a csubstr
        c4::substr alloced_buf(alloc, ret.len);
        ret = c4::yml::emit_yaml(t, id, alloced_buf, /*error_on_excess*/true);
        ret.str[ret.len] = 0;
    }
    return ret.str;
}

char * emit_json_malloc(c4::yml::Tree const& t, size_t id)
{
    c4::substr buf;
    c4::substr ret = c4::yml::emit_json(t, id, buf, /*error_on_excess*/false);
    if(ret.str == nullptr && ret.len > 0)
    {
        // Use new[]+delete[] in SWIG.
        char * alloc = new char[ret.len + 1]; // we'll return a c-string and not a csubstr
        c4::substr alloced_buf(alloc, ret.len);
        ret = c4::yml::emit_json(t, id, alloced_buf, /*error_on_excess*/true);
        ret.str[ret.len] = 0;
    }
    return ret.str;
}

size_t _compute_yaml_length(const c4::yml::Tree &t, size_t id)
{
    c4::substr buf = {};
    c4::substr ret = c4::yml::emit_yaml(t, id, buf, /*error_on_excess*/false);
    return ret.len;
}

size_t _compute_json_length(const c4::yml::Tree &t, size_t id)
{
    c4::substr buf = {};
    c4::substr ret = c4::yml::emit_json(t, id, buf, /*error_on_excess*/false);
    return ret.len;
}

bool emit_yaml_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT)
{
    c4::substr result = c4::yml::emit_yaml(t, id, s, /*error_on_excess*/false);
    *OUTPUT = result.len;
    return result.str == nullptr;
}

bool emit_json_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT)
{
    c4::substr result = c4::yml::emit_json(t, id, s, /*error_on_excess*/false);
    *OUTPUT = result.len;
    return result.str == nullptr;
}


// force a roundtrip to C++, which triggers a conversion to csubstr and returns it as a memoryview
c4::csubstr _get_as_csubstr(c4::csubstr s)
{
    //printf("_get_as_csubstr: %p[%zu]'%.*s'\n", s.str, s.len, (int)s.len, s.str);
    return s;
}

c4::csubstr  _get_as_substr(c4::substr s)
{
    //printf("_get_as_substr: %p[%zu]'%.*s'\n", s.str, s.len, (int)s.len, s.str);
    return s;
}


// helper for calling to_arena()
c4::csubstr _to_arena_buf_cpp(c4::yml::Tree &t, c4::csubstr s)
{
    return t.to_arena(s);
}


// utilities for testing
bool _same_ptr(c4::csubstr l, c4::csubstr r)
{
    return l.str == r.str;
}

bool _same_mem(c4::csubstr l, c4::csubstr r)
{
    return l.str == r.str && l.len == r.len;
}
%}


//-----------------------------------------------------------------------------

%pythoncode %{

from deprecation import deprecated


def as_csubstr(s):
    """return as a ryml::csubstr"""
    if isinstance(s, memoryview):
        return s
    return _get_as_csubstr(s)

def as_substr(s):
    """return as a ryml::ssubstr"""
    if isinstance(s, memoryview):
        return s
    return _get_as_substr(s)

def u(memview):
    """return memview as a utf8"""
    return str(memview, "utf8")


def children(tree, node=None):
    """walk (non-recursively) the children of a node"""
    assert tree is not None
    if node is None:
        node = tree.root_id()
    ch = tree.first_child(node)
    while ch != NONE:
        yield ch
        ch = tree.next_sibling(ch)


def siblings(tree, node):
    """walk (non-recursively) the siblings of a node"""
    assert tree is not None
    if node is None:
        return
    ch = tree.first_sibling(node)
    while ch != NONE:
        yield ch
        ch = tree.next_sibling(ch)


def walk(tree, node=None, depth=0):
    """walk recursively starting at a node, returning a tuple of node and depth"""
    assert tree is not None
    if node is None:
        node = tree.root_id()
    yield node, depth
    ch = tree.first_child(node)
    while ch != NONE:
       for gc, il in walk(tree, ch, depth + 1):
           yield gc, il
       ch = tree.next_sibling(ch)


@deprecated(deprecated_in="0.5.0", details="Use parse_in_arena() instead")
def parse(buf, **kwargs):
    return parse_in_arena(tree, id)


def parse_in_arena(buf, tree=None):
    """
    parse immutable YAML in the trees arena. Copy the YAML into a buffer
    in the C++ tree's arena, then parse the YAML from the trees arena.

    :param buf:
       the YAML buffer to be parsed
    :type buf: ``str``, ``bytes``, ``bytearray`` or ``memoryview``

    :param tree:
       a tree to be reused. When no tree is given, a new tree is created,
       and returned at the end.
    :type buf: ``ryml.Tree``
    """
    if tree is None:
        tree = Tree()
    parse_csubstr(buf, tree)
    return tree


def parse_in_place(buf, tree=None):
    """
    parse in place a mutable buffer containing YAML. The resulting tree
    will point into the given buffer.

    :param buf:
       the YAML buffer to be parsed
    :type buf:
       ``bytearray`` or ``memoryview`` (NOT ``str`` or ``bytes``, which are not writeable)

    :param tree:
       a tree to be reused. When no tree is given, a new tree is created,
       and returned at the end.
    :type buf: ``ryml.Tree``
    """
    if tree is None:
        tree = Tree()
    parse_substr(buf, tree)
    return tree


@deprecated(deprecated_in="0.5.0", details="Use emit_yaml() instead")
def emit(tree, id=None):
    return emit_yaml(tree, id)

def emit_yaml(tree, id=None):
    """emit the given tree as YAML, creating a new output buffer"""
    if id is None:
        id = tree.root_id()
    return emit_yaml_malloc(tree, id)

def emit_json(tree, id=None):
    """emit the given tree as JSON, creating a new output buffer"""
    if id is None:
        id = tree.root_id()
    return emit_json_malloc(tree, id)


def emit_yaml_in_place(tree, buf, id=None):
    return _emit_fn_in_place(tree, buf, id, emit_yaml_to_substr)

def emit_json_in_place(tree, buf, id=None):
    return _emit_fn_in_place(tree, buf, id, emit_json_to_substr)

@deprecated(deprecated_in="0.5.0", details="Use emit_yaml_in_place() instead")
def emit_in_place(tree, buf, id=None):
    return emit_yaml_in_place(tree, buf, id)

def _emit_fn_in_place(tree, buf, id, fn):
    if id is None:
        id = tree.root_id()
    (failed, expected_size) = fn(tree, id, buf)
    if failed:
        raise IndexError("Output buffer has {} bytes, but emit requires {} bytes".format(
            len(buf), expected_size))
    return memoryview(buf)[:expected_size]


def compute_yaml_length(tree, id=None):
    if id is None:
        id = tree.root_id()
    return _compute_yaml_length(tree, id)

def compute_json_length(tree, id=None):
    if id is None:
        id = tree.root_id()
    return _compute_json_length(tree, id)

@deprecated(deprecated_in="0.5.0", details="Use compute_yaml_length() instead")
def compute_emit_length(tree, id=None):
    return compute_yaml_length(tree, id)

@deprecated(deprecated_in="0.11.0", details="Use compute_yaml_length() instead")
def compute_emit_yaml_length(tree, id=None):
    return compute_yaml_length(tree, id)

@deprecated(deprecated_in="0.11.0", details="Use compute_json_length() instead")
def compute_emit_json_length(tree, id=None):
    return compute_json_length(tree, id)


%}


//-----------------------------------------------------------------------------


namespace c4 {
namespace yml {


constexpr const size_t NONE = (size_t)-1;

using type_bits = uint32_t;


struct Location
{
    size_t offset; ///< number of bytes from the beginning of the source buffer
    size_t line;
    size_t col;
    c4::yml::csubstr name;
};

struct ErrorDataBasic
{
    c4::yml::Location location; ///< location where the error was detected (may be from YAML or C++ source code)
    ErrorDataBasic() noexcept = default;
    ErrorDataBasic(c4::yml::Location const& cpploc_) noexcept;
};
struct ErrorDataParse
{
    c4::yml::Location cpploc; ///< location in the C++ source file where the error was detected.
    c4::yml::Location ymlloc; ///< location in the YAML source buffer where the error was detected.
    ErrorDataParse() noexcept = default;
    ErrorDataParse(c4::yml::Location const& cpploc_, c4::yml::Location const& ymlloc_) noexcept;
};
struct ErrorDataVisit
{
    c4::yml::Location cpploc;  ///< location in the C++ source file where the error was detected.
    c4::yml::Tree const* tree; ///< tree where the error was detected
    c4::yml::id_type node;     ///< node where the error was detected
    ErrorDataVisit() noexcept = default;
    ErrorDataVisit(c4::yml::Location const& cpploc_, c4::yml::Tree const *tree_ , c4::yml::id_type node_) noexcept;
};

struct ExceptionBasic : public std::exception
{
    ExceptionBasic(c4::yml::csubstr msg, c4::yml::ErrorDataBasic const& errdata_) noexcept;
    const char* what() const noexcept override { return msg; }
    c4::yml::ErrorDataBasic errdata_basic; ///< error data
    char msg[RYML_ERRMSG_SIZE];   ///< the reported error message, without location indication.
};
struct ExceptionParse : public ExceptionBasic
{
    ExceptionParse(c4::yml::csubstr msg, c4::yml::ErrorDataParse const& errdata_) noexcept;
    c4::yml::ErrorDataParse errdata_parse;
};
struct ExceptionVisit : public ExceptionBasic
{
    ExceptionVisit(c4::yml::csubstr msg, c4::yml::ErrorDataVisit const& errdata_) noexcept;
    c4::yml::ErrorDataVisit errdata_visit;
};

typedef enum : c4::yml::type_bits {
    NOTYPE  = 0,         ///< no node type or style is set
    KEY     = (1u << 0),     ///< is member of a map
    VAL     = (1u << 1),     ///< a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or SEQ
    MAP     = (1u << 2),     ///< a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
    SEQ     = (1u << 3),     ///< a seq: a parent of VAL/SEQ/MAP nodes
    DOC     = (1u << 4),     ///< a document
    STREAM  = (1u << 5)|SEQ, ///< a stream: a seq of docs
    KEYREF  = (1u << 6),     ///< a *reference: the key references an &anchor
    VALREF  = (1u << 7),     ///< a *reference: the val references an &anchor
    KEYANCH = (1u << 8),     ///< the key has an &anchor
    VALANCH = (1u << 9),     ///< the val has an &anchor
    KEYTAG  = (1u << 10),    ///< the key has a tag
    VALTAG  = (1u << 11),    ///< the val has a tag
    KEYNIL  = (1u << 12),    ///< the key is null (eg `{ : b}` results in a null key)
    VALNIL  = (1u << 13),    ///< the val is null (eg `{a : }` results in a null val)
    _TYMASK = (1u << 14)-1u, ///< all the bits up to here
    //
    // unfiltered flags:
    //
    KEY_UNFILT  = (1u << 14), ///< the key scalar was left unfiltered; the parser was set not to filter. @see ParserOptions
    VAL_UNFILT  = (1u << 15), ///< the val scalar was left unfiltered; the parser was set not to filter. @see ParserOptions
    //
    // style flags:
    //
    FLOW_SL     = (1u << 16), ///< mark container with single-line flow style (seqs as '[val1,val2], maps as '{key: val,key2: val2}')
    FLOW_ML     = (1u << 17), ///< (NOT IMPLEMENTED, work in progress) mark container with multi-line flow style (seqs as '[\n  val1,\n  val2\n], maps as '{\n  key: val,\n  key2: val2\n}')
    BLOCK       = (1u << 18), ///< mark container with block style (seqs as '- val\n', maps as 'key: val')
    KEY_LITERAL = (1u << 19), ///< mark key scalar as multiline, block literal |
    VAL_LITERAL = (1u << 20), ///< mark val scalar as multiline, block literal |
    KEY_FOLDED  = (1u << 21), ///< mark key scalar as multiline, block folded >
    VAL_FOLDED  = (1u << 22), ///< mark val scalar as multiline, block folded >
    KEY_SQUO    = (1u << 23), ///< mark key scalar as single quoted '
    VAL_SQUO    = (1u << 24), ///< mark val scalar as single quoted '
    KEY_DQUO    = (1u << 25), ///< mark key scalar as double quoted "
    VAL_DQUO    = (1u << 26), ///< mark val scalar as double quoted "
    KEY_PLAIN   = (1u << 27), ///< mark key scalar as plain scalar (unquoted, even when multiline)
    VAL_PLAIN   = (1u << 28), ///< mark val scalar as plain scalar (unquoted, even when multiline)
    //
    // type combination masks:
    //
    KEYVAL  = KEY|VAL,
    KEYSEQ  = KEY|SEQ,
    KEYMAP  = KEY|MAP,
    DOCMAP  = DOC|MAP,
    DOCSEQ  = DOC|SEQ,
    DOCVAL  = DOC|VAL,
    //
    // style combination masks:
    //
    SCALAR_LITERAL = KEY_LITERAL|VAL_LITERAL,
    SCALAR_FOLDED  = KEY_FOLDED|VAL_FOLDED,
    SCALAR_SQUO    = KEY_SQUO|VAL_SQUO,
    SCALAR_DQUO    = KEY_DQUO|VAL_DQUO,
    SCALAR_PLAIN   = KEY_PLAIN|VAL_PLAIN,
    KEYQUO         = KEY_SQUO|KEY_DQUO|KEY_FOLDED|KEY_LITERAL, ///< key style is one of ', ", > or |
    VALQUO         = VAL_SQUO|VAL_DQUO|VAL_FOLDED|VAL_LITERAL, ///< val style is one of ', ", > or |
    KEY_STYLE      = KEY_LITERAL|KEY_FOLDED|KEY_SQUO|KEY_DQUO|KEY_PLAIN, ///< mask of all the scalar styles for key (not container styles!)
    VAL_STYLE      = VAL_LITERAL|VAL_FOLDED|VAL_SQUO|VAL_DQUO|VAL_PLAIN, ///< mask of all the scalar styles for val (not container styles!)
    SCALAR_STYLE   = KEY_STYLE|VAL_STYLE,
    CONTAINER_STYLE_FLOW  = FLOW_SL|FLOW_ML,
    CONTAINER_STYLE_BLOCK = BLOCK,
    CONTAINER_STYLE       = FLOW_SL|FLOW_ML|BLOCK,
    STYLE          = SCALAR_STYLE | CONTAINER_STYLE,
    //
    // mixed masks
    _KEYMASK = KEY | KEYQUO | KEYANCH | KEYREF | KEYTAG,
    _VALMASK = VAL | VALQUO | VALANCH | VALREF | VALTAG,
} NodeType_e;

constexpr NodeType_e operator|  (NodeType_e lhs, NodeType_e rhs) noexcept;
constexpr NodeType_e operator&  (NodeType_e lhs, NodeType_e rhs) noexcept;
constexpr NodeType_e operator>> (NodeType_e bits, uint32_t n) noexcept;
constexpr NodeType_e operator<< (NodeType_e bits, uint32_t n) noexcept;
constexpr NodeType_e operator~  (NodeType_e bits) noexcept;
NodeType_e& operator&= (NodeType_e &subject, NodeType_e bits) noexcept;
NodeType_e& operator|= (NodeType_e &subject, NodeType_e bits) noexcept;


/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

struct NodeType
{
public:

    NodeType_e type;

public:

    NodeType() noexcept;
    NodeType(NodeType_e t) noexcept;
    NodeType(type_bits t) noexcept;

    bool has_any(NodeType_e t) const noexcept;
    bool has_all(NodeType_e t) const noexcept;
    bool has_none(NodeType_e t) const noexcept;

    void set(NodeType_e t) noexcept;
    void add(NodeType_e t) noexcept;
    void rem(NodeType_e t) noexcept;
    void addrem(NodeType_e bits_to_add, NodeType_e bits_to_remove) noexcept;

    void clear() noexcept;

public:

    /** @name node type queries
     * @{ */

    /** return a preset string based on the node type */
    const char *type_str() const noexcept;
    /** return a preset string based on the node type */
    static const char* type_str(NodeType_e t) noexcept;

    /** fill a string with the node type flags. If the string is small, returns {null, len} */
    c4::csubstr type_str(c4::substr buf) const noexcept;

    /** fill a string with the node type flags. If the string is small, returns {null, len}  */
    static c4::csubstr type_str(c4::substr buf, NodeType_e t) noexcept;

public:

    /** @name node type queries
     * @{ */

    bool is_notype()         const noexcept;
    bool is_stream()         const noexcept;
    bool is_doc()            const noexcept;
    bool is_container()      const noexcept;
    bool is_map()            const noexcept;
    bool is_seq()            const noexcept;
    bool has_key()           const noexcept;
    bool has_val()           const noexcept;
    bool is_val()            const noexcept;
    bool is_keyval()         const noexcept;
    bool key_is_null()       const noexcept;
    bool val_is_null()       const noexcept;
    bool has_key_tag()       const noexcept;
    bool has_val_tag()       const noexcept;
    bool has_key_anchor()    const noexcept;
    bool has_val_anchor()    const noexcept;
    bool has_anchor()        const noexcept;
    bool is_key_ref()        const noexcept;
    bool is_val_ref()        const noexcept;
    bool is_ref()            const noexcept;

    bool is_key_unfiltered() const noexcept;
    bool is_val_unfiltered() const noexcept;

    /** @} */

public:

    /** @name container+scalar style queries
     * @{ */

    bool is_container_styled() const noexcept;
    bool is_block() const noexcept;
    bool is_flow_sl() const noexcept;
    bool is_flow_ml() const noexcept;
    bool is_flow() const noexcept;

    bool is_key_styled() const noexcept;
    bool is_val_styled() const noexcept;
    bool is_key_literal() const noexcept;
    bool is_val_literal() const noexcept;
    bool is_key_folded() const noexcept;
    bool is_val_folded() const noexcept;
    bool is_key_squo() const noexcept;
    bool is_val_squo() const noexcept;
    bool is_key_dquo() const noexcept;
    bool is_val_dquo() const noexcept;
    bool is_key_plain() const noexcept;
    bool is_val_plain() const noexcept;
    bool is_key_quoted() const noexcept;
    bool is_val_quoted() const noexcept;
    bool is_quoted() const noexcept;

    void set_container_style(NodeType_e style) noexcept;
    void set_key_style(NodeType_e style) noexcept;
    void set_val_style(NodeType_e style) noexcept;

    /** @} */
};


/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

#ifndef RYML_ID_TYPE
/** The type of a node id in the YAML tree. In the future, the default
 * will likely change to int32_t, which was observed to be faster.
 * @see id_type */
#define RYML_ID_TYPE size_t
#endif


/** The type of a node id in the YAML tree; to override the default
 * type, define the macro @ref RYML_ID_TYPE to a suitable integer
 * type. */
using id_type = RYML_ID_TYPE;

struct Tree
{
    Tree();
    Tree(id_type node_type, size_t arena_capacity=RYML_DEFAULT_TREE_ARENA_CAPACITY);
    ~Tree();

    void reserve(id_type node_capacity=RYML_DEFAULT_TREE_CAPACITY);
    void reserve_arena(size_t arena_capacity=RYML_DEFAULT_TREE_ARENA_CAPACITY);
    void clear();
    void clear_arena();

    bool   empty() const;
    id_type size() const;
    id_type capacity() const;
    id_type slack() const;

    size_t arena_size() const;
    size_t arena_capacity() const;
    size_t arena_slack() const;

    void resolve();

public:

    // getters

    NodeType_e  type(id_type node) const;
    const char* type_str(id_type node) const;

    c4::csubstr key       (id_type node) const;
    c4::csubstr key_tag   (id_type node) const;
    c4::csubstr key_ref   (id_type node) const;
    c4::csubstr key_anchor(id_type node) const;
    c4::yml::NodeScalar keysc(id_type node) const;

    c4::csubstr val       (id_type node) const;
    c4::csubstr val_tag   (id_type node) const;
    c4::csubstr val_ref   (id_type node) const;
    c4::csubstr val_anchor(id_type node) const;
    c4::yml::NodeScalar valsc(id_type node) const;

public:

    // node predicates

    bool type_has_any(id_type node, NodeType_e bits) const;
    bool type_has_all(id_type node, NodeType_e bits) const;
    bool type_has_none(id_type node, NodeType_e bits) const;

    bool is_stream(id_type node) const;
    bool is_doc(id_type node) const;
    bool is_container(id_type node) const;
    bool is_map(id_type node) const;
    bool is_seq(id_type node) const;
    bool has_key(id_type node) const;
    bool has_val(id_type node) const;
    bool is_val(id_type node) const;
    bool is_keyval(id_type node) const;
    bool has_key_tag(id_type node) const;
    bool has_val_tag(id_type node) const;
    bool has_key_anchor(id_type node) const;
    bool has_val_anchor(id_type node) const;
    bool has_anchor(id_type node) const;
    bool is_key_ref(id_type node) const;
    bool is_val_ref(id_type node) const;
    bool is_ref(id_type node) const;

    bool parent_is_seq(id_type node) const;
    bool parent_is_map(id_type node) const;

    bool has_anchor(id_type node, c4::csubstr a) const;
    bool key_is_null(id_type node) const;
    bool val_is_null(id_type node) const;

public:

    // hierarchy predicates

    bool is_root(id_type node) const;

    bool has_parent(id_type node) const;
    bool has_child(id_type node, c4::csubstr key) const;
    //bool has_child(id_type node, id_type ch) const;
    bool has_children(id_type node) const;
    bool has_sibling(id_type node, c4::csubstr key) const;
    //bool has_sibling(id_type node, id_type sib) const;
    bool has_other_siblings(id_type node) const;

public:

    // hierarchy getters

    id_type root_id() const;

    id_type parent(id_type node) const;
    id_type prev_sibling(id_type node) const;
    id_type next_sibling(id_type node) const;
    id_type num_children(id_type node) const;
    id_type child_pos(id_type node, id_type ch) const;
    id_type first_child(id_type node) const;
    id_type last_child(id_type node) const;
    id_type child(id_type node, id_type pos) const;
    id_type find_child(id_type node, c4::csubstr key) const;
    id_type num_siblings(id_type node) const;
    id_type num_other_siblings(id_type node) const;
    id_type sibling_pos(id_type node, id_type sib) const;
    id_type first_sibling(id_type node) const;
    id_type last_sibling(id_type node) const;
    id_type sibling(id_type node, id_type pos) const;
    id_type find_sibling(id_type node, c4::csubstr key) const;

public:

    /** @name node style predicates and modifiers. see the corresponding predicate in NodeType */
    /** @{ */

    bool is_container_styled(id_type node) const;
    bool is_block(id_type node) const;
    bool is_flow_sl(id_type node) const;
    bool is_flow_ml(id_type node) const;
    bool is_flow(id_type node) const;

    bool is_key_styled(id_type node) const;
    bool is_val_styled(id_type node) const;
    bool is_key_literal(id_type node) const;
    bool is_val_literal(id_type node) const;
    bool is_key_folded(id_type node) const;
    bool is_val_folded(id_type node) const;
    bool is_key_squo(id_type node) const;
    bool is_val_squo(id_type node) const;
    bool is_key_dquo(id_type node) const;
    bool is_val_dquo(id_type node) const;
    bool is_key_plain(id_type node) const;
    bool is_val_plain(id_type node) const;
    bool is_key_quoted(id_type node) const;
    bool is_val_quoted(id_type node) const;
    bool is_quoted(id_type node) const;

    void set_container_style(id_type node, NodeType_e style);
    void set_key_style(id_type node, NodeType_e style);
    void set_val_style(id_type node, NodeType_e style);

    /** @} */

public:

    void set_key_tag(id_type node, c4::csubstr tag);
    void set_key_anchor(id_type node, c4::csubstr anchor);
    void set_val_anchor(id_type node, c4::csubstr anchor);
    void set_key_ref   (id_type node, c4::csubstr ref   );
    void set_val_ref   (id_type node, c4::csubstr ref   );

    void set_val_tag(id_type node, c4::csubstr tag);
    void rem_key_anchor(id_type node);
    void rem_val_anchor(id_type node);
    void rem_key_ref   (id_type node);
    void rem_val_ref   (id_type node);
    void rem_anchor_ref(id_type node);

    // these functions do not work correctly with the typemape for csubstr:
    //void to_keyval(id_type node, c4::csubstr key, c4::csubstr val, int more_flags=0);
    //void to_map(id_type node, c4::csubstr key, int more_flags=0);
    //void to_seq(id_type node, c4::csubstr key, int more_flags=0);
    //void to_val(id_type node, c4::csubstr val, int more_flags=0);
    //void to_stream(id_type node, int more_flags=0);
    //void to_map(id_type node, int more_flags=0);
    //void to_seq(id_type node, int more_flags=0);
    //void to_doc(id_type node, int more_flags=0);
    //void _set_key(id_type node, c4::csubstr key, int more_flags=0);
    //void _set_val(id_type node, c4::csubstr val, int more_flags=0);
    //
    // so we do the following:
    %extend {
        void _to_val(id_type node, c4::csubstr s, int flags) { $self->to_val(node, s, flags); }
        void _to_keyval(id_type node, c4::csubstr key, c4::csubstr val, int more_flags) { $self->to_keyval(node, key, val, more_flags); }
        void _to_map0(id_type node, int more_flags) { $self->to_map(node, more_flags); }
        void _to_seq0(id_type node, int more_flags) { $self->to_seq(node, more_flags); }
        void _to_map1(id_type node, c4::csubstr key, int more_flags) { $self->to_map(node, key, more_flags); }
        void _to_seq1(id_type node, c4::csubstr key, int more_flags) { $self->to_seq(node, key, more_flags); }
        void __set_key(id_type node, c4::csubstr key, int more_flags) { $self->_set_key(node, key, more_flags); }
        void __set_val(id_type node, c4::csubstr val, int more_flags) { $self->_set_val(node, val, more_flags); }
        void _to_stream(id_type node, int more_flags) { $self->to_stream(node, more_flags); }
        void _to_doc(id_type node, int more_flags) { $self->to_doc(node, more_flags); }
        %pythoncode %{
            # this is needed to ensure the csubstr typemap applies to the overloads
            def to_val(self, node, val, more_flags=0):
                self._to_val(node, val, more_flags)
            def to_keyval(self, node, key, val, more_flags=0):
                self._to_keyval(node, key, val, more_flags)
            def to_map(self, node, key=None, more_flags=0):
                if isinstance(key, int):
                    self._to_map0(node, key | more_flags)
                elif key is None:
                    self._to_map0(node, more_flags)
                else:
                    self._to_map1(node, key, more_flags)
            def to_seq(self, node, key=None, more_flags=0):
                if isinstance(key, int):
                    self._to_seq0(node, key | more_flags)
                elif key is None:
                    self._to_seq0(node, more_flags)
                else:
                    self._to_seq1(node, key, more_flags)
            def _set_key(self, node, key, more_flags=0):
                self.__set_key(node, key, more_flags)
            def _set_val(self, node, val, more_flags=0):
                self.__set_val(node, val, more_flags)
            def to_stream(self, node, more_flags=0):
                self._to_stream(node, more_flags)
            def to_doc(self, node, more_flags=0):
                self._to_doc(node, more_flags)
        %}
    }

public:

    /** create and insert a new child of "parent". insert after the (to-be)
     * sibling "after", which must be a child of "parent". To insert as the
     * first child, set after to NONE */
    id_type insert_child(id_type parent, id_type after);
    id_type prepend_child(id_type parent);
    id_type  append_child(id_type parent);

public:

    //! create and insert a new sibling of n. insert after "after"
    id_type insert_sibling(id_type node, id_type after);
    id_type prepend_sibling(id_type node);
    id_type  append_sibling(id_type node);

public:

    //! remove an entire branch at once: ie remove the children and the node itself
    void remove(id_type node);

    //! remove all the node's children, but keep the node itself
    void remove_children(id_type node);

public:

    void reorder();

    /** change the node's position in the parent */
    void move(id_type node, id_type after);

    /** change the node's parent and position */
    void move(id_type node, id_type new_parent, id_type after);
    /** change the node's parent and position */
    id_type move(Tree * src, id_type node, id_type new_parent, id_type after);

    /** recursively duplicate the node */
    id_type duplicate(id_type node, id_type new_parent, id_type after);
    /** recursively duplicate a node from a different tree */
    id_type duplicate(Tree const* src, id_type node, id_type new_parent, id_type after);

    /** recursively duplicate the node's children (but not the node) */
    void duplicate_children(id_type node, id_type parent, id_type after);
    /** recursively duplicate the node's children (but not the node), where the node is from a different tree */
    void duplicate_children(Tree const* src, id_type node, id_type parent, id_type after);

    void duplicate_contents(id_type node, id_type where);

    /** duplicate the node's children (but not the node) in a new parent, but
     * omit repetitions where a duplicated node has the same key (in maps) or
     * value (in seqs). If one of the duplicated children has the same key
     * (in maps) or value (in seqs) as one of the parent's children, the one
     * that is placed closest to the end will prevail. */
    void duplicate_children_no_rep(id_type node, id_type parent, id_type after);

public:

    %extend {
        // to_arena() is a template. We use an overload set instead, as it
        // will let SWIG generate the appropriate dispatch code for us.
        c4::csubstr _to_arena_fundamental(uint8_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(int8_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(uint16_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(int16_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(uint32_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(int32_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(uint64_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(int64_t val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(float val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_fundamental(double val) { return $self->to_arena(val); }
        c4::csubstr _to_arena_null() { return $self->to_arena(nullptr); }
        c4::csubstr _to_arena_buffer(c4::csubstr buf) { return $self->to_arena(buf); }
        %pythoncode %{
            def to_arena(self, val):
                if isinstance(val, (str, bytes, bytearray, memoryview)):
                    return _to_arena_buf_cpp(self, as_csubstr(val))
                elif val is None:
                    return self._to_arena_null()
                else:
                    return self._to_arena_fundamental(val)
        %}
    }
};

} // namespace yml
} // namespace c4
