FEATURE complete metadata generation
diff --git a/src/mod_netconf.c b/src/mod_netconf.c
index 0cbb875..12f1023 100644
--- a/src/mod_netconf.c
+++ b/src/mod_netconf.c
@@ -119,6 +119,7 @@
                                const char *format, json_object **err);
 static void node_add_metadata_recursive(struct lyd_node *data_tree, struct lys_module *module,
                                         json_object *data_json_parent);
+static void node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent);
 
 static void
 signal_handler(int sign)
@@ -370,6 +371,579 @@
     pthread_rwlock_unlock(&session_lock);
 }
 
+static void
+node_metadata_text(const char *text, const char *name, json_object *parent)
+{
+    json_object *obj;
+
+    if (!text) {
+        return;
+    }
+
+    obj = json_object_new_string(text);
+    json_object_object_add(parent, name, obj);
+}
+
+static void
+node_metadata_restr(struct lys_restr *restr, const char *name, json_object *parent)
+{
+    json_object *obj;
+
+    if (!restr) {
+        return;
+    }
+
+    obj = json_object_new_string(restr->expr);
+    json_object_object_add(parent, name, obj);
+}
+
+static void
+node_metadata_must(uint8_t must_size, struct lys_restr *must, json_object *parent)
+{
+    uint8_t i;
+    json_object *array, *obj;
+
+    if (!must_size || !must) {
+        return;
+    }
+
+    array = json_object_new_array();
+
+    for (i = 0; i < must_size; ++i) {
+        obj = json_object_new_string(must[i].expr);
+        json_object_array_add(array, obj);
+    }
+
+    json_object_object_add(parent, "must", array);
+}
+
+static void
+node_metadata_basic(struct lys_node *node, json_object *parent)
+{
+    json_object *obj;
+
+    /* description */
+    node_metadata_text(node->dsc, "description", parent);
+
+    /* reference */
+    node_metadata_text(node->ref, "reference", parent);
+
+    /* config */
+    if (node->flags & LYS_CONFIG_R) {
+        obj = json_object_new_boolean(0);
+    } else {
+        obj = json_object_new_boolean(1);
+    }
+    json_object_object_add(parent, "config", obj);
+
+    /* status */
+    if (node->flags & LYS_STATUS_DEPRC) {
+        obj = json_object_new_string("deprecated");
+    } else if (node->flags & LYS_STATUS_OBSLT) {
+        obj = json_object_new_string("obsolete");
+    } else {
+        obj = json_object_new_string("current");
+    }
+    json_object_object_add(parent, "status", obj);
+
+    /* mandatory */
+    if (node->flags & LYS_MAND_TRUE) {
+        obj = json_object_new_boolean(1);
+    } else {
+        obj = json_object_new_boolean(0);
+    }
+    json_object_object_add(parent, "mandatory", obj);
+
+    /* NACM extensions */
+    if (node->nacm) {
+        if (node->nacm & LYS_NACM_DENYW) {
+            obj = json_object_new_string("default-deny-write");
+        } else {
+            obj = json_object_new_string("default-deny-all");
+        }
+        json_object_object_add(parent, "ext", obj);
+    }
+}
+
+static void
+node_metadata_when(struct lys_when *when, json_object *parent)
+{
+    json_object *obj;
+
+    if (!when) {
+        return;
+    }
+
+    obj = json_object_new_string(when->cond);
+    json_object_object_add(parent, "when", obj);
+}
+
+static void
+node_metadata_children(struct lys_node *node, json_object *parent)
+{
+    json_object *child_array = NULL, *choice_array = NULL, *obj;
+    struct lys_node *child;
+
+    if (!node->child) {
+        return;
+    }
+
+    LY_TREE_FOR(node->child, child) {
+        if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)) {
+            obj = json_object_new_string(child->name);
+            if (!child_array) {
+                child_array = json_object_new_array();
+            }
+            json_object_array_add(child_array, obj);
+        } else if (child->nodetype == LYS_CHOICE) {
+            obj = json_object_new_string(child->name);
+            if (!choice_array) {
+                choice_array = json_object_new_array();
+            }
+            json_object_array_add(choice_array, obj);
+        }
+    }
+
+    if (child_array) {
+        json_object_object_add(parent, "children", child_array);
+    }
+    if (choice_array) {
+        json_object_object_add(parent, "choice", choice_array);
+    }
+}
+
+static void
+node_metadata_cases(struct lys_node_choice *choice, json_object *parent)
+{
+    json_object *array, *obj;
+    struct lys_node *child;
+
+    if (!choice->child) {
+        return;
+    }
+
+    array = json_object_new_array();
+
+    LY_TREE_FOR(choice->child, child) {
+        if (child->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_CASE)) {
+            obj = json_object_new_string(child->name);
+            json_object_array_add(array, obj);
+        }
+    }
+
+    json_object_object_add(parent, "cases", array);
+}
+
+static void
+node_metadata_min_max(uint32_t min, uint32_t max, json_object *parent)
+{
+    json_object *obj;
+
+    if (min) {
+        obj = json_object_new_int(min);
+        json_object_object_add(parent, "min-elements", obj);
+    }
+
+    if (max) {
+        obj = json_object_new_int(max);
+        json_object_object_add(parent, "max-elements", obj);
+    }
+}
+
+static void
+node_metadata_ident_recursive(struct lys_ident *ident, json_object *array)
+{
+    struct lys_ident_der *cur;
+    json_object *obj;
+
+    if (!ident) {
+        return;
+    }
+
+    obj = json_object_new_string(ident->name);
+    json_object_array_add(array, obj);
+
+    for (cur = ident->der; cur; cur = cur->next) {
+        node_metadata_ident_recursive(cur->ident, array);
+    }
+}
+
+static void
+node_metadata_type(struct lys_type *type, struct lys_module *module, json_object *parent)
+{
+    json_object *obj, *array, *item;
+    char *str;
+    int i;
+
+    /* built-in YANG type */
+    if (!type->der->module) {
+        switch (type->base) {
+        case LY_TYPE_BINARY:
+            node_metadata_text("binary", "type", parent);
+            node_metadata_restr(type->info.binary.length, "length", parent);
+            break;
+        case LY_TYPE_BITS:
+            node_metadata_text("bits", "type", parent);
+
+            array = json_object_new_array();
+            for (i = 0; i < type->info.bits.count; ++i) {
+                item = json_object_new_object();
+                obj = json_object_new_string(type->info.bits.bit[i].name);
+                json_object_object_add(item, "name", obj);
+                obj = json_object_new_int(type->info.bits.bit[i].pos);
+                json_object_object_add(item, "position", obj);
+                json_object_array_add(array, item);
+            }
+            json_object_object_add(parent, "bits", array);
+            break;
+        case LY_TYPE_BOOL:
+            node_metadata_text("bool", "type", parent);
+            break;
+        case LY_TYPE_DEC64:
+            node_metadata_text("decimal64", "type", parent);
+            node_metadata_restr(type->info.dec64.range, "range", parent);
+            obj = json_object_new_int(type->info.dec64.dig);
+            json_object_object_add(parent, "fraction-digits", obj);
+            break;
+        case LY_TYPE_EMPTY:
+            node_metadata_text("empty", "type", parent);
+            break;
+        case LY_TYPE_ENUM:
+            node_metadata_text("enumeration", "type", parent);
+
+            array = json_object_new_array();
+            for (i = 0; i < type->info.enums.count; ++i) {
+                obj = json_object_new_string(type->info.enums.enm[i].name);
+                json_object_array_add(array, obj);
+            }
+            json_object_object_add(parent, "enumval", array);
+            break;
+        case LY_TYPE_IDENT:
+            node_metadata_text("identityref", "type", parent);
+
+            array = json_object_new_array();
+            node_metadata_ident_recursive(type->info.ident.ref, array);
+            json_object_object_add(parent, "identityval", array);
+            break;
+        case LY_TYPE_INST:
+            node_metadata_text("instance-identifier", "type", parent);
+            if (type->info.inst.req == -1) {
+                obj = json_object_new_boolean(0);
+            } else {
+                obj = json_object_new_boolean(1);
+            }
+            json_object_object_add(parent, "require-instance", obj);
+            break;
+        case LY_TYPE_LEAFREF:
+            node_metadata_text("leafref", "type", parent);
+            node_metadata_text(type->info.lref.path, "path", parent);
+            break;
+        case LY_TYPE_STRING:
+            node_metadata_text("string", "type", parent);
+            node_metadata_restr(type->info.str.length, "length", parent);
+            if (type->info.str.pat_count) {
+                array = json_object_new_array();
+                for (i = 0; i < type->info.str.pat_count; ++i) {
+                    obj = json_object_new_string(type->info.str.patterns[i].expr);
+                    json_object_array_add(array, obj);
+                }
+                json_object_object_add(parent, "pattern", array);
+            }
+            break;
+        case LY_TYPE_UNION:
+            node_metadata_text("union", "type", parent);
+            array = json_object_new_array();
+            for (i = 0; i < type->info.uni.count; ++i) {
+                obj = json_object_new_object();
+                node_metadata_type(&type->info.uni.types[i], module, obj);
+                json_object_array_add(array, obj);
+            }
+            json_object_object_add(parent, "types", array);
+            break;
+        case LY_TYPE_INT8:
+            node_metadata_text("int8", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_UINT8:
+            node_metadata_text("uint8", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_INT16:
+            node_metadata_text("int16", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_UINT16:
+            node_metadata_text("uint16", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_INT32:
+            node_metadata_text("int32", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_UINT32:
+            node_metadata_text("uint32", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_INT64:
+            node_metadata_text("int64", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        case LY_TYPE_UINT64:
+            node_metadata_text("uint64", "type", parent);
+            node_metadata_restr(type->info.num.range, "range", parent);
+            break;
+        default:
+            ERROR("Internal: unknown type (%s:%d)", __FILE__, __LINE__);
+            break;
+        }
+
+    /* typedef */
+    } else {
+        if (!module || !type->module_name || !strcmp(type->module_name, module->name)) {
+            node_metadata_text(type->der->name, "type", parent);
+        } else {
+            asprintf(&str, "%s:%s", type->module_name, type->der->name);
+            node_metadata_text(str, "type", parent);
+            free(str);
+        }
+        obj = json_object_new_object();
+        node_metadata_typedef(type->der, obj);
+        json_object_object_add(parent, "typedef", obj);
+    }
+}
+
+static void
+node_metadata_typedef(struct lys_tpdf *tpdf, json_object *parent)
+{
+    json_object *obj;
+
+    /* description */
+    node_metadata_text(tpdf->dsc, "description", parent);
+
+    /* reference */
+    node_metadata_text(tpdf->ref, "reference", parent);
+
+    /* status */
+    if (tpdf->flags & LYS_STATUS_DEPRC) {
+        obj = json_object_new_string("deprecated");
+    } else if (tpdf->flags & LYS_STATUS_OBSLT) {
+        obj = json_object_new_string("obsolete");
+    } else {
+        obj = json_object_new_string("current");
+    }
+    json_object_object_add(parent, "status", obj);
+
+    /* type */
+    node_metadata_type(&tpdf->type, tpdf->module, parent);
+
+    /* units */
+    node_metadata_text(tpdf->units, "units", parent);
+
+    /* default */
+    node_metadata_text(tpdf->dflt, "default", parent);
+}
+
+static void
+node_metadata_container(struct lys_node_container *cont, json_object *parent)
+{
+    json_object *obj;
+
+    /* element type */
+    obj = json_object_new_string("container");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)cont, parent);
+
+    /* must */
+    node_metadata_must(cont->must_size, cont->must, parent);
+
+    /* presence */
+    node_metadata_text(cont->presence, "presence", parent);
+
+    /* when */
+    node_metadata_when(cont->when, parent);
+
+    /* children & choice */
+    node_metadata_children((struct lys_node *)cont, parent);
+}
+
+static void
+node_metadata_choice(struct lys_node_choice *choice, json_object *parent)
+{
+    json_object *obj;
+
+    /* element type */
+    obj = json_object_new_string("choice");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)choice, parent);
+
+    /* default */
+    node_metadata_text(choice->dflt->name, "default", parent);
+
+    /* when */
+    node_metadata_when(choice->when, parent);
+
+    /* cases */
+    node_metadata_cases(choice, parent);
+}
+
+static void
+node_metadata_leaf(struct lys_node_leaf *leaf, json_object *parent)
+{
+    json_object *obj;
+    struct lys_node_list *list;
+    int is_key, i;
+
+    /* element type */
+    obj = json_object_new_string("leaf");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)leaf, parent);
+
+    /* type */
+    node_metadata_type(&leaf->type, leaf->module, parent);
+
+    /* units */
+    node_metadata_text(leaf->units, "units", parent);
+
+    /* default */
+    node_metadata_text(leaf->dflt, "default", parent);
+
+    /* must */
+    node_metadata_must(leaf->must_size, leaf->must, parent);
+
+    /* when */
+    node_metadata_when(leaf->when, parent);
+
+    /* iskey */
+    is_key = 0;
+    list = (struct lys_node_list *)lys_parent((struct lys_node *)leaf);
+    if (list && (list->nodetype == LYS_LIST)) {
+        for (i = 0; i < list->keys_size; ++i) {
+            if (list->keys[i] == leaf) {
+                is_key = 1;
+                break;
+            }
+        }
+    }
+    obj = json_object_new_boolean(is_key);
+    json_object_object_add(parent, "iskey", obj);
+}
+
+static void
+node_metadata_leaflist(struct lys_node_leaflist *llist, json_object *parent)
+{
+    json_object *obj;
+
+    /* element type */
+    obj = json_object_new_string("leaf-list");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)llist, parent);
+
+    /* type */
+    node_metadata_type(&llist->type, llist->module, parent);
+
+    /* units */
+    node_metadata_text(llist->units, "units", parent);
+
+    /* must */
+    node_metadata_must(llist->must_size, llist->must, parent);
+
+    /* when */
+    node_metadata_when(llist->when, parent);
+
+    /* min/max-elements */
+    node_metadata_min_max(llist->min, llist->max, parent);
+}
+
+static void
+node_metadata_list(struct lys_node_list *list, json_object *parent)
+{
+    json_object *obj, *array;
+    int i;
+    unsigned int j;
+
+    /* element type */
+    obj = json_object_new_string("list");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)list, parent);
+
+    /* must */
+    node_metadata_must(list->must_size, list->must, parent);
+
+    /* when */
+    node_metadata_when(list->when, parent);
+
+    /* min/max-elements */
+    node_metadata_min_max(list->min, list->max, parent);
+
+    /* keys */
+    if (list->keys_size) {
+        array = json_object_new_array();
+        for (i = 0; i < list->keys_size; ++i) {
+            obj = json_object_new_string(list->keys[i]->name);
+            json_object_array_add(array, obj);
+        }
+        json_object_object_add(parent, "keys", array);
+    }
+
+    /* unique */
+    if (list->unique_size) {
+        array = json_object_new_array();
+        for (i = 0; i < list->unique_size; ++i) {
+            for (j = 0; j < list->unique[i].expr_size; ++j) {
+                obj = json_object_new_string(list->unique[i].expr[j]);
+                json_object_array_add(array, obj);
+            }
+        }
+        json_object_object_add(parent, "unique", array);
+    }
+}
+
+static void
+node_metadata_anyxml(struct lys_node_anyxml *anyxml, json_object *parent)
+{
+    json_object *obj;
+
+    /* element type */
+    obj = json_object_new_string("anyxml");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)anyxml, parent);
+
+    /* must */
+    node_metadata_must(anyxml->must_size, anyxml->must, parent);
+
+    /* when */
+    node_metadata_when(anyxml->when, parent);
+
+}
+
+static void
+node_metadata_case(struct lys_node_case *cas, json_object *parent)
+{
+    json_object *obj;
+
+    /* element type */
+    obj = json_object_new_string("case");
+    json_object_object_add(parent, "eltype", obj);
+
+    /* shared info */
+    node_metadata_basic((struct lys_node *)cas, parent);
+
+    /* when */
+    node_metadata_when(cas->when, parent);
+}
+
 /**
  * \defgroup netconf_operations NETCONF operations
  * The list of NETCONF operations that mod_netconf supports.
@@ -1349,60 +1923,6 @@
     return res;
 }
 
-static void
-node_metadata_children(struct lys_node *node, json_object *parent)
-{
-    json_object *array, *array_item;
-    struct lys_node *child;
-
-    array = json_object_new_array();
-
-    LY_TREE_FOR(node->child, child) {
-        array_item = json_object_new_string(child->name);
-        json_object_array_add(array, array_item);
-    }
-
-    json_object_object_add(parent, "children", array);
-}
-
-static void
-node_metadata_container(struct lys_node_container *cont, json_object *parent)
-{
-    json_object *obj;
-
-    /* element type */
-    obj = json_object_new_string("container");
-    json_object_object_add(parent, "eltype", obj);
-
-    /* config */
-    if (cont->flags & LYS_CONFIG_R) {
-        obj = json_object_new_boolean(0);
-    } else {
-        obj = json_object_new_boolean(1);
-    }
-    json_object_object_add(parent, "config", obj);
-
-    /* TODO */
-    /* description */
-
-    /* if-feature */
-
-    /* must */
-
-    /* presence */
-
-    /* reference */
-
-    /* status */
-
-    /* typedef */
-
-    /* when */
-
-    /* children */
-    node_metadata_children((struct lys_node *)cont, parent);
-}
-
 static int
 node_add_metadata(struct lys_node *node, struct lys_module *module, json_object *parent)
 {
@@ -1432,19 +1952,31 @@
         case LYS_CONTAINER:
             node_metadata_container((struct lys_node_container *)node, meta_obj);
             break;
-        /* TODO LYS_AUGMENT
-        LYS_CHOICE
-        LYS_LEAF
-        LYS_LEAFLIST
-        LYS_LIST
-        LYS_ANYXML
-        LYS_GROUPING
-        LYS_CASE
+        case LYS_CHOICE:
+            node_metadata_choice((struct lys_node_choice *)node, meta_obj);
+            break;
+        case LYS_LEAF:
+            node_metadata_leaf((struct lys_node_leaf *)node, meta_obj);
+            break;
+        case LYS_LEAFLIST:
+            node_metadata_leaflist((struct lys_node_leaflist *)node, meta_obj);
+            break;
+        case LYS_LIST:
+            node_metadata_list((struct lys_node_list *)node, meta_obj);
+            break;
+        case LYS_ANYXML:
+            node_metadata_anyxml((struct lys_node_anyxml *)node, meta_obj);
+            break;
+        case LYS_CASE:
+            node_metadata_case((struct lys_node_case *)node, meta_obj);
+            break;
+        /* TODO
         LYS_INPUT
         LYS_OUTPUT
-        LYS_NOTIF
-        LYS_RPC
-        LYS_USES*/
+        LYS_RPC*/
+        default:
+            ERROR("Internal: unuxpected nodetype (%s:%d)", __FILE__, __LINE__);
+            break;
     }
 
     /* just a precaution */