/*
    COLLECTION LIBRARY

    Header file for collection interface.

    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009

    Collection Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Collection Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Collection Library.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef COLLECTION_H
#define COLLECTION_H

#include <stdint.h>

#ifndef EOK
#define EOK 0
#endif

#define COL_TYPE_STRING          0x00000001 /* For elements of type string the
                                               lenght includes the trailing 0 */
#define COL_TYPE_BINARY          0x00000002
#define COL_TYPE_INTEGER         0x00000004
#define COL_TYPE_UNSIGNED        0x00000008
#define COL_TYPE_LONG            0x00000010
#define COL_TYPE_ULONG           0x00000020
#define COL_TYPE_DOUBLE          0x00000040
#define COL_TYPE_BOOL            0x00000080
#define COL_TYPE_COLLECTION      0x00000100 /* The item of this type denotes
                                               that starting element of a
                                               collection */
#define COL_TYPE_COLLECTIONREF   0x00000200 /* An item of this type is a pointer
                                               to an existing external
                                               collection */
#define COL_TYPE_END             0x10000000 /* Special type that denotes the end
                                               of the collection. Useful when
                                               traversing collections */
#define COL_TYPE_ANY             0x0FFFFFFF /* Special type that denotes any.
                                               Useful when traversing
                                               collections */


/* Default class for a free form collection */
#define COL_CLASS_DEFAULT      0

/* The modes that define how one collection can be added to another */

#define COL_ADD_MODE_REFERENCE 0    /* The collection will contain a pointer
                                     * to another */
#define COL_ADD_MODE_EMBED     1    /* The collection will become part of
                                     * another collection.
                                     * After this operation the handle should
                                     * not be used or freed.
                                     * Can't be done more than once.
                                     * If the collection is referenced by
                                     * another collection, the operation will
                                     * fail. */
#define COL_ADD_MODE_CLONE     2    /* Creates a deep copy of a collection with
                                     * its sub collections */
#define COL_ADD_MODE_FLAT      3    /* Creates a deep copy of a collection with
                                     * its sub collections flattening and NOT
                                     * resolving duplicates.
                                     */
#define COL_ADD_MODE_FLATDOT   4    /* Creates a deep copy of a collection with
                                     * its sub collections flattening and NOT
                                     * resolving duplicates. Names are contructed
                                     * in dotted notation.
                                     * For example the subcollection
                                     * named "sub" containing "foo" and
                                     * "bar" will be flattened as:
                                     * "sub.foo", "sub.bar".
                                     */

/* Modes how the collection is traversed */
#define COL_TRAVERSE_DEFAULT  0x00000000  /* Traverse all items */
#define COL_TRAVERSE_ONELEVEL 0x00000001  /* Flag to traverse only top level
                                           * ignored if the IGNORE flag is
                                           * specified */
#define COL_TRAVERSE_END      0x00000002  /* Call the handler once more when the
                                           * end of the collection is reached.
                                           * Good for processing nested
                                           * collections.
                                           */
#define COL_TRAVERSE_IGNORE   0x00000004  /* Ignore sub collections as if none
                                           * is present */
#define COL_TRAVERSE_FLAT     0x00000008  /* Flatten the collection. */


/* Additional iterator flags
 * NOTE: ignored by traverse functions */
#define COL_TRAVERSE_SHOWSUB  0x00010000  /* Include headers of sub collections
                                           * By default iterators return just
                                           * references and skips headers.
                                           * Ignored if the ONELEVEL flag is
                                           * specified and not ignored.
                                           * Ignored if the FLAT flag is
                                           * specified. */
#define COL_TRAVERSE_ONLYSUB  0x00020000  /* Show the header of the sub
                                           * collection instead of reference.
                                           * Ignored if the ONELEVEL flag is
                                           * specified and not ignored.
                                           * Ignored if the FLAT flag is
                                           * specified. */

/* NOTE COL_TRAVERSE_FLAT, COL_TRAVERSE_SHOWSUB, COL_TRAVERSE_ONLYSUB
 * are mutually exclusive flags. If combined together
 * results will be unpredictable.
 * DO NOT MIX THEM IN ONE ITERATOR.
 */


/* Modes accepted by copy collection function */
#define COL_COPY_NORMAL         0    /* Deep copy. Referenced collections
                                      * of the donor are copied as sub
                                      * collections.
                                      */
#define COL_COPY_FLAT           1    /* Deep copy. Collection is flattend. */
#define COL_COPY_FLATDOT        2    /* Deep copy. Collection is flattend.
                                      * Names are concatenated with dot.
                                      */
#define COL_COPY_KEEPREF        3    /* Deep copy but leave references
                                      * as references.
                                      */
#define COL_COPY_TOP            4    /* Copy only top level
                                      */

/* Match values */
#define COL_NOMATCH 0
#define COL_MATCH   1

/* Deapth for iteraor depth allocation block */
#define STACK_DEPTH_BLOCK   15

/* Public declaration of the private data */
#ifndef COLLECTION_PRIV_H
/* Structure that holds one property. */
struct collection_item;

/* Your implementation can assume that following members
 * will always be members of the collection_item.
 * but you should use get_item_xxx functions to get them.
 *   char *property;
 *   int property_len;
 *   int type;
 *   int length;
 *   void *data;
 */


/* Internal iterator structure */
struct collection_iterator;
#endif /* COLLECTION_PRIV_H */

/* IMPORTANT - the collection is a set of items of different type.
 * There is always a header item in any collection that starts the collection.
 * Most of the functions in the interface (unless explicitly stated otherwise)
 * assume that the collection_item * argument points to the header element.
 * Passing in elements extracted from the middle of a collection to functions
 * that expect header elements is illegal. There might be not enough checking
 * at the moment but this will be enforced in future versions of the library.

 * IMPORTANT - To better understand how collections work imagine travel bags.
 * They usually come in different sizes and one can put a bag in a bag when
 * they put away to the shelf in a garage or closet. Collection is such bag
 * except that you can put other bags into each other even if they are not
 * empty.
 * When you put items into a bag you do not see the contents of the bag.
 * You just hold the bag. How many other bags inside this bag you do not know.
 * But you might know that you put a "wallet" somewhere there.
 * You ask the bag you hold: "find my wallet and give it to me".
 * get_item function will return you the item that is your "wallet".
 * You can then change something or just get information about the item you
 * retrieved. But in most cases you do not the wallet itself. You want to get
 * something from the wallet or put something into it. IMO money would be an
 * obvious choice. To do this you use update_xxx_property functions.
 * There might be a bag somewhere deep and you might want to add something to
 * it. add_xxx_property_xxx functions allow you to specify sub collection you
 * want the item to be added to. If this sub collection argument is NULL top
 * level collection is assumed.
 * The search in the collections users a "x!y!z" notation to refer to an item (or
 * property). You can search for "wallet" and it will find any first instance of
 * the "wallet" in your luggage. But you might have two wallets. One is yours and
 * another is your significant other's. So you might say find "my!wallet".
 * It will find wallet in your bad (collection) named "my". This collection can
 * be many levels deep inside other collections. You do not need to know the
 * full path to get to it. But if you have the full path you can use the fill
 * path like this "luggage!newbags!my!wallet".
 * It is useful to be able to put bags into bags as well as get them out of each
 * other. When the collection is created the header keeps a reference count on
 * how many copies of the collection are known to the world. So one can put a
 * collection into collection and give up its access to it (embed) of still hold
 * to the reference. By embedding the collection the caller effectively gives
 * up its responsibility to destroy the collection after it is used.
 * By extracting reference from an internal collection the caller gains access
 * to the collection directly and thus has responsibility to destroy it after
 * use.
 * Characters with codes less than space in ASCII table are illegal for property
 * names.
 * Character '!' also illegal in property name and reserved for "x!y!z" notation.
 */

/* Function that creates a named collection */
int col_create_collection(struct collection_item **ci,
                          const char *name,
                          unsigned cclass);

/* Function that destroys a collection */
void col_destroy_collection(struct collection_item *ci);

/* Family of functions that add properties to a collection */
/* See details about subcollection argument above. */
/* Family includes the following convinience functions: */
/* Add a string property to collection. The length should include the
 * terminating 0 */
int col_add_str_property(struct collection_item *ci,
                         const char *subcollection,
                         const char *property,
                         const char *string,
                         int length);
/* Add a binary property to collection.  */
int col_add_binary_property(struct collection_item *ci,
                            const char *subcollection,
                            const char *property,
                            void *binary_data,
                            int length);
/* Add an int property to collection. */
int col_add_int_property(struct collection_item *ci,
                         const char *subcollection,
                         const char *property,
                         int number);
/* Add an unsigned int property. */
int col_add_unsigned_property(struct collection_item *ci,
                              const char *subcollection,
                              const char *property,
                              unsigned int number);
/* Add a long property. */
int col_add_long_property(struct collection_item *ci,
                          const char *subcollection,
                          const char *property,
                          long number);
/* Add an unsigned long property. */
int col_add_ulong_property(struct collection_item *ci,
                           const char *subcollection,
                           const char *property,
                           unsigned long number);
/* Add a double property. */
int col_add_double_property(struct collection_item *ci,
                            const char *subcollection,
                            const char *property,
                            double number);
/* Add a bool property. */
int col_add_bool_property(struct collection_item *ci,
                          const char *subcollection,
                          const char *property,
                          unsigned char logical);

/* Add any property */
int col_add_any_property(struct collection_item *ci,    /* A collection of items */
                         const char *subcollection,     /* Subcollection */
                         const char *property,          /* Name */
                         int type,                      /* Data type */
                         void *data,                    /* Pointer to the data */
                         int length);                   /* Length of the data. For
                                                         * strings it includes the
                                                         * trailing 0 */

/* The functions that add an item and immediately return you this item
 * in the ret_ref parameter */
int col_add_str_property_with_ref(struct collection_item *ci,
                                  const char *subcollection,
                                  const char *property,
                                  char *string, int length,
                                  struct collection_item **ret_ref);
int col_add_binary_property_with_ref(struct collection_item *ci,
                                     const char *subcollection,
                                     const char *property,
                                     void *binary_data, int length,
                                     struct collection_item **ret_ref);
int col_add_int_property_with_ref(struct collection_item *ci,
                                  const char *subcollection,
                                  const char *property, int number,
                                  struct collection_item **ret_ref);
int col_add_unsigned_property_with_ref(struct collection_item *ci,
                                       const char *subcollection,
                                       const char *property, unsigned int number,
                                       struct collection_item **ret_ref);
int col_add_long_property_with_ref(struct collection_item *ci,
                                   const char *subcollection,
                                   const char *property, long number,
                                   struct collection_item **ret_ref);
int col_add_ulong_property_with_ref(struct collection_item *ci,
                                    const char *subcollection,
                                    const char *property, unsigned long number,
                                    struct collection_item **ret_ref);
int col_add_double_property_with_ref(struct collection_item *ci,
                                     const char *subcollection,
                                     const char *property, double number,
                                     struct collection_item **ret_ref);
int col_add_bool_property_with_ref(struct collection_item *ci,
                                   const char *subcollection,
                                   const char *property, unsigned char logical,
                                   struct collection_item **ret_ref);
int col_add_any_property_with_ref(struct collection_item *ci,
                                  const char *subcollection,
                                  const char *property,
                                  int type, void *data, int length,
                                  struct collection_item **ret_ref);

/* Update functions */
/* All update functions search the property using the search algorithm
 * described at the top of the header file.
 * Use same "x!y" notation to specify a property.
 */
/* Update a string property in the collection.
 * Length should include the terminating 0  */
int col_update_str_property(struct collection_item *ci,
                            const char *property,
                            int mode_flags,
                            char *string,
                            int length);
/* Update a binary property in the collection.  */
int col_update_binary_property(struct collection_item *ci,
                               const char *property,
                               int mode_flags,
                               void *binary_data,
                               int length);
/* Update an int property in the collection. */
int col_update_int_property(struct collection_item *ci,
                            const char *property,
                            int mode_flags,
                            int number);
/* Update an unsigned int property. */
int col_update_unsigned_property(struct collection_item *ci,
                                 const char *property,
                                 int mode_flags,
                                 unsigned int number);
/* Update a long property. */
int col_update_long_property(struct collection_item *ci,
                             const char *property,
                             int mode_flags,
                             long number);
/* Update an unsigned long property. */
int col_update_ulong_property(struct collection_item *ci,
                              const char *property,
                              int mode_flags,
                              unsigned long number);
/* Update a double property. */
int col_update_double_property(struct collection_item *ci,
                               const char *property,
                               int mode_flags,
                               double number);
/* Update a double property. */
int col_update_bool_property(struct collection_item *ci,
                             const char *property,
                             int mode_flags,
                             unsigned char logical);

/* Update property in the collection */
int col_update_property(struct collection_item *ci,   /* A collection of items */
                        const char *property_to_find, /* Name to match */
                        int type,                     /* Data type */
                        void *new_data,               /* Pointer to the data */
                        int length,                   /* Length of the data. For
                                                       * strings, it includes the
                                                       * trailing 0 */
                        int mode_flags);              /* How to traverse the collection  */


/* Add collection to collection */
int col_add_collection_to_collection(struct collection_item *ci,            /* Collection handle to with we add
                                                                             * another collection */
                                 const char *sub_collection_name,           /* Name of the sub collection
                                                                             * to which collection needs to be
                                                                             * added as a property.
                                                                             * If NULL high level collection is assumed. */
                                 const char *as_property,                   /* Name of the collection property.
                                                                             * If NULL, same property as the name of
                                                                             * the collection being added will be used. */
                                 struct collection_item *collection_to_add, /* Collection to add */
                                 int mode);                                 /* How this collection needs to be added */

/* Create a deep copy of the current collection.
 * Wraps the function below.
 * The acceptable modes are defined at the top.
 */
int col_copy_collection(struct collection_item **collection_copy,
                        struct collection_item *collection_to_copy,
                        const char *name_to_use,
                        int copy_mode);

/* Callback used in the next function */
typedef int (*col_copy_cb)(struct collection_item *item,
                           void *ext_data,
                           int *skip);

/* Create a deep copy of the current collection.
 * Calls caller provided callback before
 * copying each item's data.
 * The acceptable modes are defined at the top.
 */
int col_copy_collection_with_cb(struct collection_item **collection_copy,
                                struct collection_item *collection_to_copy,
                                const char *name_to_use,
                                int copy_mode,
                                col_copy_cb copy_cb,
                                void *ext_data);

/* Signature of the callback that needs to be used when
   traversing a collection or looking for a specific item */
typedef int (*col_item_fn)(const char *property,   /* The name of the property will be passed in this parameter. */
                           int property_len, /* Length of the property name will be passed in this parameter. */
                           int type,         /* Type of the data will be passed in this parameter */
                           void *data,       /* Pointer to the data will be passed in this parameter */
                           int length,       /* Length of data will be passed in this parameter */
                           void *custom_dat, /* Custom data will be passed in this parameter */
                           int *stop);       /* Pointer to variable where the handler can put non zero to stop processing */

/* Function to traverse the entire collection including optionally sub collections */
int col_traverse_collection(struct collection_item *ci,    /* Collection to traverse */
                            int mode_flags,                /* Flags defining how to traverse */
                            col_item_fn item_handler,      /* Handler called for each item */
                            void *custom_data);            /* Custom data passed around */

/* Search function. Looks up an item in the collection based on the property.
 * Actually it is a traverse function with special traversing logic.
 * It is the responsibility of the handler to set something in the custom data
 * to indicate that the item was found.
 * Function will not return error if the item is not found.
 * It is the responsibility of the calling application to check
 * the data passed in custom_data and see if the item was found and
 * that the action was performed.
 */
int col_get_item_and_do(struct collection_item *ci,  /* A collection of items */
                        const char *property_to_find,/* Name to match */
                        int type,                    /* Type filter */
                        int mode_flags,              /* How to traverse the collection */
                        col_item_fn item_handler,    /* Function to call when the item is found */
                        void *custom_data);          /* Custom data passed around */

/* Convenience function to get individual item */
/* Caller should be aware that this is not a copy of the item
 * but the pointer to actual item stored in the collection.
 * The returned pointer should never be altered or freed by caller of the function.
 * The caller should be sure that the collection does not go out of scope
 * while the pointer to its data is in use.
 * Working with the internals of the collection item structure directly
 * may cause problems in future if the internal implementation changes.
 * The caller needs to be aware that function does not return
 * error if item is not found. The caller needs to check if
 * item is not NULL to determine whether something was found.
 */
int col_get_item(struct collection_item *ci,       /* Collection to find things in */
                 const char *property_to_find,     /* Name to match */
                 int type,                         /* Type filter */
                 int mode_flags,                   /* How to traverse the collection */
                 struct collection_item **item);   /* Found item */

/* Group of functions that allows retrieving individual elements of the collection_item
 * hiding the internal implementation.
 */
const char *col_get_item_property(struct collection_item *ci, int *property_len);
int col_get_item_type(struct collection_item *ci);
int col_get_item_length(struct collection_item *ci);
void *col_get_item_data(struct collection_item *ci);
uint64_t col_get_item_hash(struct collection_item *ci);

/* Calculates hash of the string using internal hashing
 * algorithm. Populates "length" with length
 * of the string not counting 0.
 * Length argument can be NULL.
 * If sub_len is greater than zero
 * this value is used to count how many characters
 * from string should be included into hash
 * calculation.
 */
uint64_t col_make_hash(const char *string, int sub_len, int *length);

/* Compare two items.
 * The second item is evaluated against the first.
 * Function returns 0 if two items are the same
 * and non-zero otherwise.
 * The in_flags is a bit mask that defines
 * how the items should be compared.
 * See below the list of conbstants
 * defined for this purpose.
 * If items are different they might be orderable
 * or not. For example one can order items by name
 * but not by type.
 * If the result of the function is non-zero
 * the out_flags (if provided) will be
 * set to indicate if the second item is greater
 * then the first.
 */
int col_compare_items(struct collection_item *first,
                      struct collection_item *second,
                      unsigned in_flags,
                      unsigned *out_flags);

/********* Possible valies for input flags ********/
/* How to compare properties?
 * The following 4 flags are mutually exclusive
 */
#define COL_CMPIN_PROP_EQU    0x000000004 /* Properties should be same */
#define COL_CMPIN_PROP_BEG    0x000000005 /* Properties start same */
#define COL_CMPIN_PROP_MID    0x000000006 /* One is substring of another */
#define COL_CMPIN_PROP_END    0x000000007 /* One property ends with another */

/* Make sure that there is a dot.
 * Useful with _BEG, _MID and _END flags to check that the there is
 * a dot (if present) in the right place (before, after or both).
 * For example the first item is named "foo.bar" and the second
 * is "bar". Using _END the "bar" will be found but if _DOT flag is
 * used too the function will also check if there was a "." before the found
 * string in this case.
 * Ignored in case of _EQU.
 */
#define COL_CMPIN_PROP_DOT     0x000000008

/* Compare property lenghts */
#define COL_CMPIN_PROP_LEN     0x000000010

/* Compare types */
#define COL_CMPIN_TYPE         0x000000020

/* Compare data len */
#define COL_CMPIN_DATA_LEN     0x000000040

/* Compare data (up to the length of the second one)
 * if type is the same. If type is different
 * function will assume data is different
 * without performing actual comparison.
 */
#define COL_CMPIN_DATA         0x000000080

/********* Possible values for output flags *********/
/* If _EQU was specified and the property of the second item
 * is greater the following bit will be set
 */
#define COL_CMPOUT_PROP_STR    0x00000001
/* If we were told to compare property lengths
 * and second is longer this bit will be set
 */
#define COL_CMPOUT_PROP_LEN    0x00000002
/* If we were told to compare data lengths
 * and second is longer this bit will be set
 */
#define COL_CMPOUT_DATA_LEN    0x00000004
/* If we were told to compare data
 * and types are the same then
 * if the second one is greater this bit will
 * be set. If data is binary flag is never set
 */
#define COL_CMPOUT_DATA    0x00000008


/* Sort collection.
 * cmp_flags are the same as in_flags for the compare
 * function. The sort_flags is an OR of the costants
 * defined below.
 * If the subcollections are included in sorting
 * each collection is sorted separately (this is not a global sort).
 * It might be dangerous to sort subcollections if
 * subcollection is not owned by current collection.
 * If it is a reference to an external collection
 * there might be an issue. To skip the collections that
 * externally referenced use MYSUB flag.
 * Keep in mind that if the collection
 * has two references to the same other
 * collection it is impossible to detect
 * this situation. If MYSUB is used in this
 * case such collection will be ignored
 * If MYSUB is not used the collection
 * will be sorted more than once.
 */
int col_sort_collection(struct collection_item *col,
                        unsigned cmp_flags,
                        unsigned sort_flags);

/* Sort flags */
#define COL_SORT_ASC    0x00000000
#define COL_SORT_DESC   0x00000001
#define COL_SORT_SUB    0x00000002
#define COL_SORT_MYSUB  0x00000004

/* If you want to modify the item that you got as a result of iterating through collection
 * or by calling col_get_item(). If you want to rename item provide a new name in the property
 * argument. If you want the data to remain unchanged use 0 as length parameter.
 * If item is a reference or collection the call will return error.
 * Previous type and data of the item is destroyed.
 */
int col_modify_item(struct collection_item *item,
                    const char *property,
                    int type,
                    const void *data,
                    int length);

/* Rename the item */
int col_modify_item_property(struct collection_item *item,
                             const char *property);

/* Convenience functions that wrap modify_item().
 * They always assign new value.
 * To rename the property just use modify_item_property();
 */
int col_modify_str_item(struct collection_item *item,
                        const char *property,
                        const char *string,
                        int length);
int col_modify_binary_item(struct collection_item *item,
                           const char *property,
                           void *binary_data,
                           int length);
int col_modify_bool_item(struct collection_item *item,
                         const char *property,
                         unsigned char logical);
int col_modify_int_item(struct collection_item *item,
                        const char *property,
                        int number);
int col_modify_long_item(struct collection_item *item,
                         const char *property,
                         long number);
int col_modify_ulong_item(struct collection_item *item,
                          const char *property,
                          unsigned long number);
int col_modify_unsigned_item(struct collection_item *item,
                             const char *property,
                             unsigned number);
int col_modify_double_item(struct collection_item *item,
                           const char *property,
                           double number);

/* Delete property from the collection. */
/* It is recomended to use a more efficient function
 * col_remove_item() for the same purpose if
 * the property is unique or if the collection
 * has a known structure.
 * This function has some advantage only
 * if it is not known where propery
 * resides and what is the structure of the collection.
 * In this case "foo.bar.baz" notation
 * can be used in the property_to_find argument to find
 * and delete the property "baz" that is in sub collection "bar"
 * which is in turn a part of collection "foo".
 */
int col_delete_property(struct collection_item *ci,    /* A collection of items */
                        const char *property_to_find,  /* Name to match */
                        int type,                      /* Type filter */
                        int mode_flags);               /* How to traverse the collection  */


/* Convenience function to check if the property is indeed in the collection */
int col_is_item_in_collection(struct collection_item *ci,   /* Collection to find things in */
                              const char *property_to_find, /* Name to match */
                              int type,                     /* Type filter */
                              int mode_flags,               /* How to traverse the collection */
                              int *found);                  /* Boolean that turns to nonzero if the match is found */


/* Get collection - get a pointer to a collection included into another collection.
 * If the collection_to_find is NULL function reterns a reference to the top level collection.
 * Delete extracted collection after use to decrease reference count.
 */
int col_get_collection_reference(struct collection_item *ci,          /* High level collection */
                                 struct collection_item **acceptor,   /* The pointer that will accept extracted handle */
                                 const char *collection_to_find);     /* Name to of the collection */

/* Get collection - if current item is a reference get a real collection from it. */
/* Delete extracted collection after use to decrease reference count. */
int col_get_reference_from_item(struct collection_item *ci,          /* Reference element of the high level collection */
                                struct collection_item **acceptor);  /* The pointer that will accept extracted handle */

/* Bind iterator to a collection */
int col_bind_iterator(struct collection_iterator **iterator, /* The iterator to bind */
                      struct collection_item *ci,            /* Collection to bind iterator to */
                      int mode_flags);                       /* How the collection needs to be iterated */

/* Unbind the iterator from the collection */
void col_unbind_iterator(struct collection_iterator *iterator);

/* Get items from the collection one by one following the tree */
int col_iterate_collection(struct collection_iterator *iterator,
                           struct collection_item **item);

/* Stop processing this subcollection and move to the next item in the
 * collection 'level' levels up.
 * The 'Level' parameter indicates how many levels up you want to jump.
 * If 0 - call is a no op.
 * If the depth is less then requested level the iterator will
 * get to the 0 level and next call to col_iterate_collection
 * will return NULL item.
 */
int col_iterate_up(struct collection_iterator *iterator, unsigned level);

/* How deep are we relative to the top level.
 * This function might report depth that might look misleading.
 * The reason is that traverse flags affect the internal
 * level we are on at each moment.
 * For example the default traverse behavior is to show
 * references to the sub collections.
 * So when the item reference is returned the
 * depth automatically adjusted to level inside the sub collection.
 * So if function is called in this situation the level returned will
 * denote the level inside collection.
 * Now imagine that this collection is empty so the attempt to read
 * element will push you automatically one level up (in absence of the
 * COL_TRAVERSE_END flag). If in this situation you encounter another
 * collection the reference will be returned and level automatically
 * adjusted to level inside the collection.
 * The point is that the level is reliable only after
 * a data item was returned.
 * To avoid this ambiguity another function is introduced.
 * See below.
*/
int col_get_iterator_depth(struct collection_iterator *iterator, int *depth);

/* Returns item depth for the last read item.
 * Returns 0 if no item has been read yet.
 */
int col_get_item_depth(struct collection_iterator *iterator, int *depth);

/* Pins down the iterator to loop around current point */
void col_pin_iterator(struct collection_iterator *iterator);

/* Rewinds iterator to the beginning */
void col_rewind_iterator(struct collection_iterator *iterator);

/* Set collection class */
int col_set_collection_class(struct collection_item *item, /* Collection */
                             unsigned cclass);             /* Collection class */

/* Get collection class */
int col_get_collection_class(struct collection_item *item, /* Collection */
                             unsigned *cclass);            /* Collection class */


/* Get collection count */
int col_get_collection_count(struct collection_item *item, /* Collection */
                             unsigned *count);             /* Number of elements in
                                                            * this collection.
                                                            * Each subcollection is
                                                            * counted as 1 element.
                                                            * Header is also counted.
                                                            */

/* Convenience function to check if the collection is of the specific class */
/* In case of internal error assumes that collection is not of the right class */
int col_is_of_class(struct collection_item *item,  /* Collection */
                    unsigned cclass);              /* Class of the collection */


/*
 * Series of collection functions that allow using collection as a stack or a FIFO
 */


/* Extract the item from the collection */
/* The header will not be considered for extraction. */
int col_extract_item(struct collection_item *ci,        /* Top collection */
                     const char *subcollection,         /* Sub collection */
                     int disposition,                   /* Which to extract */
                     const char *refprop,               /* Property to relate to */
                     int idx,                           /* Index of the property to extract. See notes. */
                     int type,                          /* Type filter */
                     struct collection_item **ret_ref); /* Returns the reference back */

/* Similar extraction function as above just considers only one level. */
int col_extract_item_from_current(struct collection_item *ci,        /* Top collection */
                                  int disposition,                   /* Which to extract */
                                  const char *refprop,               /* Property to relate to */
                                  int idx,                           /* Index of the property to extract. See notes. */
                                  int type,                          /* Type filter */
                                  struct collection_item **ret_ref); /* Returns the reference back */

/* Remove item (property) from collection.
 * It is similar to delete_property function but allows more specific
 * information about what item (property) to remove.
 * The header will not be considered for deletion.
 */
int col_remove_item(struct collection_item *ci,        /* Top collection */
                    const char *subcollection,         /* Sub collection */
                    int disposition,                   /* Which to remove */
                    const char *refprop,               /* Property to relate to */
                    int idx,                           /* Index of the property to remove. See notes. */
                    int type);                         /* Type filter */

/* Similar function as above just considers only one level. */
int col_remove_item_from_current(struct collection_item *ci,        /* Top collection */
                                 int disposition,                   /* Which to remove */
                                 const char *refprop,               /* Property to relate to */
                                 int idx,                           /* Index of the property to remove. See notes. */
                                 int type);                         /* Type filter */


/* Insert item to the collection */
/* WARNING: Only use this function to insert items
 * that were extracted using extract_item().
 * NEVER use it with items that were returned
 * by col_get_item() or col_add_xxx_property_with_ref() or
 * with col_insert_xxx_property_with_ref().
 * The fundamental difference is that when you extracted item
 * using col_extract_item() it stops to be managed by a collection.
 * With such item you can:
 * a) Insert this item into another (or same) collection
 * b) Get item information using col_get_item_xxx() functions.
 * c) Destroy item using col_delete_item().
 * You are required to do either a) or c) with such item.
 */
int col_insert_item(struct collection_item *collection, /* Top collection */
                    const char *subcollection,          /* Sub collection */
                    struct collection_item *item,       /* Item to insert */
                    int disposition,                    /* What should be the position of the item */
                    const char *refprop,                /* Property to relate to */
                    int idx,                            /* Index of the property to extract. See notes. */
                    unsigned flags);                    /* Flags that control naming issues */

/* Insert the item into the top level collection (similar to the function above)
 * but does not provide access to the sub collection.
 */
int col_insert_item_into_current(struct collection_item *collection,
                                 struct collection_item *item,
                                 int disposition,
                                 const char *refprop,
                                 int idx,
                                 unsigned flags);



/* Function to delete the item */
void col_delete_item(struct collection_item *item);

/* Possible dispositions for insert, extract and delete function(s).
 * Not all of these dispositions are implemented day one.
 * If disposition is not implemented the function
 * will return error ENOSYS.
 */
#define COL_DSP_END             0 /* Add property to the end of the collection */
                                  /* Extract or delete last property in collection */
#define COL_DSP_FRONT           1 /* Add property to the top of the collection */
                                  /* Extract or delete firat property in collection */
#define COL_DSP_BEFORE          2 /* Add property before other named property */
                                  /* Extract or delete property that is before given
                                   * property. If the given property is first
                                   * in the list ENOENT is returned.
                                   */
#define COL_DSP_AFTER           3 /* Add property immediately after other named property */
                                  /* Delete or extract property immediately after given
                                   * property. If the given property is last in the list
                                   * ENOENT is returned.
                                   */
#define COL_DSP_INDEX           4 /* Add, extract or delete property using index.
                                   * See notes below.
                                   */
/* NOTE ABOUT USING: COL_DSP_INDEX. */
/* The COL_DSP_INDEX adds the item as N-th item after header in the list.
 * Index is zero based.
 * If there are less than N items in the list the item is added to the end.
 * The index value of 0 means that the item will be added immediately
 * after the header. Index of 1 will mean that it is added after first data item and so on.
 *
 * In case of extraction or deletion the N-th item of the collection
 * will be extracted or deleted.
 * Index is zero based.
 * If there are less than N+1 items in the list the function will return ENOENT.
 */

/* The following three dispositions operate only with list of duplicate
 * properties that are going one after another.
 * In case of addition the property name is taken from the item
 * and the value refprop is ignored.
 * In case of extraction or deletion the property name is taken
 * from the refprop.
 */
#define COL_DSP_FIRSTDUP        5 /* Add property as a first dup of the given property */
                                  /* In case of extraction or deletion extracts or deletes
                                   * given property.
                                   */
#define COL_DSP_LASTDUP         6 /* Add property as a last dup of the given property */
                                  /* In case of extraction or deletion extracts or deletes
                                   * last duplicate property in the uninterrupted sequence of
                                   * properties with the same name.
                                   */
#define COL_DSP_NDUP            7 /* Add property as a N-th dup (0- based) of the given property. */
                                  /* In case of extraction or deletion extracts or deletes
                                   * N-th (0-based) duplicate of the given property.
                                   * If index is greater than number of duplicate
                                   * properties in sequence ENOENT is returned.
                                   * See more details below.
                                   */

/* Other dispositions might be possible in future. */

/* The COL_DSP_NDUP is used in case of the multi value property
 * to add a new property with the same name into specific place
 * in the list of properties with the same name.
 * The index of 0 will mean to add the property before the first instance of the property with the same name.
 * If the property does not exist ENOENT will be returned.
 * If the index is greater than the last property with the same name the item will be added
 * immediately after last property with the same name.
 */

/* Flags that can be used with insert functions */
#define COL_INSERT_NOCHECK      0   /* This is the default mode - no dup checks on insert */
#define COL_INSERT_DUPOVER      1   /* Check for dup name and overwrite - position ignored */
#define COL_INSERT_DUPOVERT     2   /* Check for dup name and type and overwrite - position ignored */
#define COL_INSERT_DUPERROR     3   /* Return error EEXIST if the entry with the same name exists */
#define COL_INSERT_DUPERRORT    4   /* Return error EEXIST if the entry with the same name and type exists */
#define COL_INSERT_DUPMOVE      5   /* Check for dups, overwrite, extracts and
                                     * then move to the position requested */
#define COL_INSERT_DUPMOVET     6   /* Check for dup name and type, overwrite, extracts
                                     * and then move to the position requested */

/* In future can be made more complex */

/* NOTE ABOUT FLAGS: Use of the DUP checking flags is costly since it requires a forward look up of the whole
 * collection before the item is inserted. Do not use it until it is absolutely necessary.
 */



/* The attributes in the collection are always added to the end.
 * The family of the insert_xxx(functions) provides
 * much more flexibility than add_xxx_property_xxx() functions.
 */

/* Insert property with reference */
int col_insert_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                 const char *subcollection,         /* Sub collection */
                                 int disposition,                   /* Where to insert */
                                 const char *refprop,               /* Property to relate to */
                                 int idx,                           /* Index of the property to add */
                                 unsigned flags,                    /* Flags that control naming issues */
                                 const char *property,              /* Name */
                                 int type,                          /* Data type */
                                 const void *data,                  /* Pointer to the data */
                                 int length,                        /* Length of the data. For
                                                                     * strings it includes the
                                                                     * trailing 0
                                                                     */
                                 struct collection_item **ret_ref); /* Returns the reference back */


int col_insert_str_property(struct collection_item *ci,        /* A collection of items */
                            const char *subcollection,         /* Sub collection */
                            int disposition,                   /* Where to insert */
                            const char *refprop,               /* Property to relate to */
                            int idx,                           /* Index of the property to add */
                            unsigned flags,                    /* Flags that control naming issues */
                            const char *property,              /* Name */
                            const char *string,                /* String */
                            int length);                       /* Length */

int col_insert_binary_property(struct collection_item *ci,        /* A collection of items */
                               const char *subcollection,         /* Sub collection */
                               int disposition,                   /* Where to insert */
                               const char *refprop,               /* Property to relate to */
                               int idx,                           /* Index of the property to add */
                               unsigned flags,                    /* Flags that control naming issues */
                               const char *property,              /* Name */
                               void *binary_data,                 /* Binary data */
                               int length);                       /* Length */


int col_insert_int_property(struct collection_item *ci,        /* A collection of items */
                            const char *subcollection,         /* Sub collection */
                            int disposition,                   /* Where to insert */
                            const char *refprop,               /* Property to relate to */
                            int idx,                           /* Index of the property to add */
                            unsigned flags,                    /* Flags that control naming issues */
                            const char *property,              /* Name */
                            int number);                       /* Integer */


int col_insert_unsinged_property(struct collection_item *ci,        /* A collection of items */
                                 const char *subcollection,         /* Sub collection */
                                 int disposition,                   /* Where to insert */
                                 const char *refprop,               /* Property to relate to */
                                 int idx,                           /* Index of the property to add */
                                 unsigned flags,                    /* Flags that control naming issues */
                                 const char *property,              /* Name */
                                 unsigned number);                  /* Unsigned */


int col_insert_long_property(struct collection_item *ci,        /* A collection of items */
                             const char *subcollection,         /* Sub collection */
                             int disposition,                   /* Where to insert */
                             const char *refprop,               /* Property to relate to */
                             int idx,                           /* Index of the property to add */
                             unsigned flags,                    /* Flags that control naming issues */
                             const char *property,              /* Name */
                             long number);                      /* Long */

int col_insert_ulong_property(struct collection_item *ci,        /* A collection of items */
                              const char *subcollection,         /* Sub collection */
                              int disposition,                   /* Where to insert */
                              const char *refprop,               /* Property to relate to */
                              int idx,                           /* Index of the property to add */
                              unsigned flags,                    /* Flags that control naming issues */
                              const char *property,              /* Name */
                              unsigned long number);             /* Unsigned long */

int col_insert_double_property(struct collection_item *ci,        /* A collection of items */
                               const char *subcollection,         /* Sub collection */
                               int disposition,                   /* Where to insert */
                               const char *refprop,               /* Property to relate to */
                               int idx,                           /* Index of the property to add */
                               unsigned flags,                    /* Flags that control naming issues */
                               const char *property,              /* Name */
                               double number);                    /* Double */

int col_insert_bool_property(struct collection_item *ci,        /* A collection of items */
                             const char *subcollection,         /* Sub collection */
                             int disposition,                   /* Where to insert */
                             const char *refprop,               /* Property to relate to */
                             int idx,                           /* Index of the property to add */
                             unsigned flags,                    /* Flags that control naming issues */
                             const char *property,              /* Name */
                             unsigned char logical);            /* Bool */



int col_insert_str_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                     const char *subcollection,         /* Sub collection */
                                     int disposition,                   /* Where to insert */
                                     const char *refprop,               /* Property to relate to */
                                     int idx,                           /* Index of the property to add */
                                     unsigned flags,                    /* Flags that control naming issues */
                                     const char *property,              /* Name */
                                     char *string,                      /* String */
                                     int length,                        /* Length */
                                     struct collection_item **ret_ref); /* Returns the reference back */

int col_insert_binary_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                        const char *subcollection,         /* Sub collection */
                                        int disposition,                   /* Where to insert */
                                        const char *refprop,               /* Property to relate to */
                                        int idx,                           /* Index of the property to add */
                                        unsigned flags,                    /* Flags that control naming issues */
                                        const char *property,              /* Name */
                                        void *binary_data,                 /* Binary data */
                                        int length,                        /* Length */
                                        struct collection_item **ret_ref); /* Returns the reference back */


int col_insert_int_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                     const char *subcollection,         /* Sub collection */
                                     int disposition,                   /* Where to insert */
                                     const char *refprop,               /* Property to relate to */
                                     int idx,                           /* Index of the property to add */
                                     unsigned flags,                    /* Flags that control naming issues */
                                     const char *property,              /* Name */
                                     int number,                        /* Integer */
                                     struct collection_item **ret_ref); /* Returns the reference back */


int col_insert_unsinged_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                          const char *subcollection,         /* Sub collection */
                                          int disposition,                   /* Where to insert */
                                          const char *refprop,               /* Property to relate to */
                                          int idx,                           /* Index of the property to add */
                                          unsigned flags,                    /* Flags that control naming issues */
                                          const char *property,              /* Name */
                                          unsigned number,                   /* Unsigned */
                                          struct collection_item **ret_ref); /* Returns the reference back */

int col_insert_long_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                      const char *subcollection,         /* Sub collection */
                                      int disposition,                   /* Where to insert */
                                      const char *refprop,               /* Property to relate to */
                                      int idx,                           /* Index of the property to add */
                                      unsigned flags,                    /* Flags that control naming issues */
                                      const char *property,              /* Name */
                                      long number,                       /* Long */
                                      struct collection_item **ret_ref); /* Returns the reference back */

int col_insert_ulong_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                       const char *subcollection,         /* Sub collection */
                                       int disposition,                   /* Where to insert */
                                       const char *refprop,               /* Property to relate to */
                                       int idx,                           /* Index of the property to add */
                                       unsigned flags,                    /* Flags that control naming issues */
                                       const char *property,              /* Name */
                                       unsigned long number,              /* Unsigned long */
                                       struct collection_item **ret_ref); /* Returns the reference back */

int col_insert_double_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                        const char *subcollection,         /* Sub collection */
                                        int disposition,                   /* Where to insert */
                                        const char *refprop,               /* Property to relate to */
                                        int idx,                           /* Index of the property to add */
                                        unsigned flags,                    /* Flags that control naming issues */
                                        const char *property,              /* Name */
                                        double number,                     /* Double */
                                        struct collection_item **ret_ref); /* Returns the reference back */

int col_insert_bool_property_with_ref(struct collection_item *ci,        /* A collection of items */
                                      const char *subcollection,         /* Sub collection */
                                      int disposition,                   /* Where to insert */
                                      const char *refprop,               /* Property to relate to */
                                      int idx,                           /* Index of the property to add */
                                      unsigned flags,                    /* Flags that control naming issues */
                                      const char *property,              /* Name */
                                      unsigned char logical,             /* Bool */
                                      struct collection_item **ret_ref); /* Returns the reference back */


#endif