lys_getnext(const struct lys_node *last, const struct lys_node *parent, const struct lys_module *module, int options)
{
    const struct lys_node *next, *aug_parent;
    struct lys_node **snode;

    if ((!parent && !module) || (module && module->type) || (parent && (parent->nodetype == LYS_USES) && !(options & LYS_GETNEXT_PARENTUSES))) {
        LOGARG;
        return NULL;
    }

    if (!last) {
        /* first call */

        /* get know where to start */
        if (parent) {
            /* schema subtree */
            snode = lys_child(parent, LYS_UNKNOWN);
            /* do not return anything if the augment does not have any children */
            if (!snode || !(*snode) || ((parent->nodetype == LYS_AUGMENT) && ((*snode)->parent != parent))) {
                return NULL;
            }
            next = last = *snode;
        } else {
            /* top level data */
            if (!(options & LYS_GETNEXT_NOSTATECHECK) && (module->disabled || !module->implemented)) {
                /* nothing to return from a disabled/imported module */
                return NULL;
            }
            next = last = module->data;
        }
    } else if ((last->nodetype == LYS_USES) && (options & LYS_GETNEXT_INTOUSES) && last->child) {
        /* continue with uses content */
        next = last->child;
    } else {
        /* continue after the last returned value */
        next = last->next;
    }

repeat:
    if (parent && (parent->nodetype == LYS_AUGMENT) && next) {
        /* do not return anything outside the parent augment */
        aug_parent = next->parent;
        do {
            while (aug_parent && (aug_parent->nodetype != LYS_AUGMENT)) {
                aug_parent = aug_parent->parent;
            }
            if (aug_parent) {
                if (aug_parent == parent) {
                    break;
                }
                aug_parent = ((struct lys_node_augment *)aug_parent)->target;
            }

        } while (aug_parent);
        if (!aug_parent) {
            return NULL;
        }
    }
    while (next && (next->nodetype == LYS_GROUPING)) {
        if (options & LYS_GETNEXT_WITHGROUPING) {
            return next;
        }
        next = next->next;
    }

    if (!next) {     /* cover case when parent is augment */
        if (!last || last->parent == parent || lys_parent(last) == parent) {
            /* no next element */
            return NULL;
        }
        last = lys_parent(last);
        next = last->next;
        goto repeat;
    } else {
        last = next;
    }

    if (!(options & LYS_GETNEXT_NOSTATECHECK) && lys_is_disabled(next, 0)) {
        next = next->next;
        goto repeat;
    }

    switch (next->nodetype) {
    case LYS_INPUT:
    case LYS_OUTPUT:
        if (options & LYS_GETNEXT_WITHINOUT) {
            return next;
        } else if (next->child) {
            next = next->child;
        } else {
            next = next->next;
        }
        goto repeat;

    case LYS_CASE:
        if (options & LYS_GETNEXT_WITHCASE) {
            return next;
        } else if (next->child) {
            next = next->child;
        } else {
            next = next->next;
        }
        goto repeat;

    case LYS_USES:
        /* go into */
        if (options & LYS_GETNEXT_WITHUSES) {
            return next;
        } else if (next->child) {
            next = next->child;
        } else {
            next = next->next;
        }
        goto repeat;

    case LYS_RPC:
    case LYS_ACTION:
    case LYS_NOTIF:
    case LYS_LEAF:
    case LYS_ANYXML:
    case LYS_ANYDATA:
    case LYS_LIST:
    case LYS_LEAFLIST:
        return next;

    case LYS_CONTAINER:
        if (!((struct lys_node_container *)next)->presence && (options & LYS_GETNEXT_INTONPCONT)) {
            if (next->child) {
                /* go into */
                next = next->child;
            } else {
                next = next->next;
            }
            goto repeat;
        } else {
            return next;
        }

    case LYS_CHOICE:
        if (options & LYS_GETNEXT_WITHCHOICE) {
            return next;
        } else if (next->child) {
            /* go into */
            next = next->child;
        } else {
            next = next->next;
        }
        goto repeat;

    default:
        /* we should not be here */
        return NULL;
    }
}