/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2013 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#ifndef LIBCOUCHBASE_LIST_H
#define LIBCOUCHBASE_LIST_H 1

#ifdef __cplusplus
extern "C" {
#endif

    /* Circular list implementation
     *
     * Could be used to implement queues, stacks etc.
     *
     * Declare list in your structure:
     *
     *      typedef struct {
     *          lcb_list_t list;
     *          lcb_uint32_t msec;
     *          void (*callback)(struct timer *tm);
     *      } lcb_timer_t;
     *
     * Initialize head of list:
     *
     *      lcb_timer_t tm;
     *      lcb_list_init(&tm.list);
     *
     * Add new items:
     *
     *      lcb_timer_t *t;
     *      t->msec = 2000;
     *      t->callback = my_cb;
     *      lcb_list_append(&tm.list, t);
     *
     * Iterate over items:
     *
     *      lcb_timer_t *t, *n;
     *
     *      for (ii = tm.list.next; ii != &tm.list; ii = ii->next) {
     *          t = LCB_LIST_ITEM(ii, lcb_timer_t, list);
     *          printf("timeout: %d\n", t->msec);
     *      }
     *
     *      LCB_LIST_FOR(ii, &tm.list) {
     *          t = LCB_LIST_ITEM(ii, lcb_timer_t, list);
     *          printf("timeout: %d\n", t->msec);
     *      }
     *
     *      LCB_LIST_SAFE_FOR(ii, n, &tm.list) {
     *          t = LCB_LIST_ITEM(ii, lcb_timer_t, list);
     *          printf("timeout: %d\n", t->msec);
     *      }
     */
    typedef struct lcb_list_s lcb_list_t;
    struct lcb_list_s {
        lcb_list_t *next;
        lcb_list_t *prev;
    };

    typedef struct lcb_clist_s {
        lcb_list_t *next;
        lcb_list_t *prev;
        lcb_size_t size;
    } lcb_clist_t;

    typedef int (*lcb_list_cmp_fn)(lcb_list_t *a, lcb_list_t *b);

    void lcb_list_init(lcb_list_t *list);
    void lcb_list_prepend(lcb_list_t *list, lcb_list_t *item);
    void lcb_list_append(lcb_list_t *list, lcb_list_t *item);
    void lcb_list_delete(lcb_list_t *item);
    lcb_list_t *lcb_list_shift(lcb_list_t *list);
    lcb_list_t *lcb_list_pop(lcb_list_t *list);
    int lcb_list_contains(lcb_list_t *list, lcb_list_t *item);
    void lcb_list_add_sorted(lcb_list_t *list, lcb_list_t *item, lcb_list_cmp_fn cmp);

    /** Definitions for type safety. Rather than macros */
    void lcb_clist_init(lcb_clist_t*);
    void lcb_clist_append(lcb_clist_t*, lcb_list_t*);
    void lcb_clist_prepend(lcb_clist_t*, lcb_list_t*);
    void lcb_clist_delete(lcb_clist_t*,lcb_list_t*);
    lcb_list_t* lcb_clist_shift(lcb_clist_t*);
    lcb_list_t* lcb_clist_pop(lcb_clist_t*);


#define LCB_LIST_IS_EMPTY(list) \
    ((list) == (list)->next && (list) == (list)->prev)

#define LCB_LIST_ITEM(ptr, type, member) \
    ((type *) (void *) ((char *)(ptr) - offsetof(type, member)))

#define LCB_LIST_FOR(pos, list) \
    for (pos = (list)->next; pos != (list); pos = pos->next)


#define LCB_LIST_SAFE_FOR(pos, n, list) \
    for (pos = (list)->next, n = pos->next; pos != (list); pos = n, n = pos->next)

#define LCB_LIST_HAS_NEXT(ll, item) \
    ((item)->next != ll)

#define LCB_CLIST_SIZE(cl) (cl)->size

#define LCB_LIST_TAIL(list) \
    ((LCB_LIST_IS_EMPTY(list)) ? NULL : (list)->prev)

#define LCB_LIST_HEAD(list) \
    ((LCB_LIST_IS_EMPTY(list)) ? NULL : (list)->next)

#ifdef __cplusplus
}
#endif
#endif
