/* KInterbasDB Python Package - Implementation of LIFO Linked List
 *
 * Version 3.3
 *
 * The following contributors hold Copyright (C) over their respective
 * portions of code (see license.txt for details):
 *
 * [Original Author (maintained through version 2.0-0.3.1):]
 *   1998-2001 [alex]  Alexander Kuznetsov   <alexan@users.sourceforge.net>
 * [Maintainers (after version 2.0-0.3.1):]
 *   2001-2002 [maz]   Marek Isalski         <kinterbasdb@maz.nu>
 *   2002-2007 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
 * [Contributors:]
 *   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
 *   2001-2002 [janez] Janez Jere            <janez.jere@void.si>
 */

#ifndef _KISUPPORT_LIFO_LINKED_LIST_H
#define _KISUPPORT_LIFO_LINKED_LIST_H

#define LIFO_LINKED_LIST_DEFINE_CONS( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
    allocation_func \
  ) \
    static int _ ## ListType ## _cons(ListTypeQualified **list_slot, \
        ContainedTypeQualified *cont, ListTypeQualified *next \
      ) \
    { \
      /* Maintain a pointer to the previous occupant of the 'into' slot so we \
       * can restore it if the mem alloc fails. */ \
      ListTypeQualified *prev_occupant = *list_slot; \
      *list_slot = allocation_func(sizeof(ListType)); \
      if (*list_slot == NULL) { \
        *list_slot = prev_occupant; \
        return -1; \
      } \
      /* Initialize the two fields of the new node: */ \
      (*list_slot)->contained = cont; \
      (*list_slot)->next = next; \
      return 0; \
    }


#define LIFO_LINKED_LIST_DEFINE_ADD( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified \
  ) \
    static int ListType ## _add( \
        ListTypeQualified **list_slot, ContainedTypeQualified *cont \
      ) \
    { \
      assert (list_slot != NULL); \
      \
      if (*list_slot == NULL) { \
        if (_ ## ListType ## _cons(list_slot, cont, NULL) != 0) { \
          return -1; \
        } \
      } else { \
        ListTypeQualified *prev_head = *list_slot; \
        if (_ ## ListType ## _cons(list_slot, cont, prev_head) != 0) { \
          return -1; \
        } \
      } \
      \
      assert ((*list_slot)->contained == cont); \
      \
      return 0; \
    }

#define LIFO_LINKED_LIST_DEFINE_REMOVE( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
    deallocation_func \
  ) \
    static int ListType ## _remove( \
        ListTypeQualified **list_slot, ContainedTypeQualified *cont, \
        boolean object_if_missing \
      ) \
    { \
      ListTypeQualified *nodeBack; \
      ListTypeQualified *nodeForward; \
      \
      /* Traverse the linked list looking for a node whose ->reader points to \
       * cont: */ \
      nodeBack = nodeForward = *list_slot; \
      while (nodeForward != NULL && nodeForward->contained != cont) { \
        nodeBack = nodeForward; \
        nodeForward = nodeForward->next; \
      } \
      if (nodeForward == NULL) { \
        if (!object_if_missing) { \
          return 0; \
        } else { \
          /* Note that this calls the Py_* API; GIL must be held. */ \
          raise_exception(InternalError, \
              # ListType "_remove: node was not in list" \
            ); \
          return -1; \
        } \
      } \
      \
      /* Unlink nodeForward: */ \
      if (nodeBack == nodeForward) { \
        /* We found the desired node in the first position of the linked \
         * list. */ \
        *list_slot = nodeForward->next; \
      } else { \
        nodeBack->next = nodeForward->next; \
      } \
      deallocation_func((ListType *) nodeForward); \
      \
      return 0; \
    }

#define LIFO_LINKED_LIST_DEFINE_RELEASE__WITH_ALLOWED_TO_RAISE_SPEC( \
    allowed_to_raise, \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
    deallocation_func \
  ) \
    static int ListType ## _release(ListTypeQualified **list_slot) \
    { \
      ListTypeQualified *list; \
      assert (list_slot != NULL); \
      list = *list_slot; \
      if (list == NULL) { \
        /* It's already been released. */ \
        return 0; \
      } \
      \
      do { \
        assert (list->contained != NULL); \
        \
        /* Direct the contained object not to try to unlink itself upon \
         * closure, because that's the very thing we're doing! */ \
        if (ContainedType ## _untrack(list->contained, allowed_to_raise) != 0) { \
          /* We weren't able to collect this node properly.  Make sure \
           * list_slot points to this node (which is currently the head of the \
           * linked list) so that the entire linked list isn't lost. */ \
          list_slot = &list; \
          return -1; \
        } else { \
          ListTypeQualified *next_list = list->next; \
          deallocation_func((ListType *) list); \
          list = next_list; \
        } \
      } while (list != NULL); \
      \
      *list_slot = NULL; \
      return 0; \
    }

#define LIFO_LINKED_LIST_DEFINE_RELEASE( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
    deallocation_func \
  ) \
    LIFO_LINKED_LIST_DEFINE_RELEASE__WITH_ALLOWED_TO_RAISE_SPEC(TRUE, \
      ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
      deallocation_func \
    )

#define LIFO_LINKED_LIST_DEFINE_TRAVERSE( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified \
  ) \
    static int ListType ## _traverse(ListTypeQualified *node_start, \
        ListType ## MappedFunction modifier \
      ) \
    { \
      ListTypeQualified *node_cur = node_start; \
      ListTypeQualified *node_prev = NULL; \
      \
      while (node_cur != NULL) { \
        if (modifier(node_prev, node_cur) != 0) { \
          goto fail; \
        } \
        node_prev = node_cur; \
        node_cur = node_cur->next; \
      } \
      \
      return 0; \
      \
      fail: \
        assert (PyErr_Occurred()); \
        return -1; \
    }

#define LIFO_LINKED_LIST_DEFINE_TRAVERSE_NOQUAL(ListType, ContainedType) \
  LIFO_LINKED_LIST_DEFINE_TRAVERSE( \
      ListType, ListType, ContainedType, ContainedType \
    )

#define LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
    allocation_func, deallocation_func \
  ) \
    LIFO_LINKED_LIST_DEFINE_CONS( \
        ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
        allocation_func \
      ) \
    LIFO_LINKED_LIST_DEFINE_ADD( \
        ListType, ListTypeQualified, ContainedType, ContainedTypeQualified \
      ) \
    LIFO_LINKED_LIST_DEFINE_REMOVE( \
        ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
        deallocation_func \
      ) \
    LIFO_LINKED_LIST_DEFINE_RELEASE( \
        ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
        deallocation_func \
      )

#define LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_PYALLOC( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified \
  ) \
    /* The GIL must be held during method calls to this linked list type. */ \
    LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_( \
        ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
        kimem_main_malloc, kimem_main_free \
      )

#define LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_PYALLOC_NOQUAL( \
    ListType, ContainedType \
  ) \
    LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_PYALLOC( \
        ListType, ListType, ContainedType, ContainedType \
      )

#define LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_SYSALLOC( \
    ListType, ListTypeQualified, ContainedType, ContainedTypeQualified \
  ) \
    /* The GIL does not need to be held during method calls to this linked \
     * list type. */ \
    LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_( \
        ListType, ListTypeQualified, ContainedType, ContainedTypeQualified, \
        kimem_plain_malloc, kimem_plain_free \
      )

#define LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_SYSALLOC_NOQUAL( \
    ListType, ContainedType \
  ) \
    LIFO_LINKED_LIST_DEFINE_BASIC_METHODS_SYSALLOC( \
        ListType, ListType, ContainedType, ContainedType \
      )

PyObject *pyob_TrackerToList(AnyTracker *tracker) {
  PyObject *py_list = PyList_New(0);
  if (py_list == NULL) { goto fail; }

  while (tracker != NULL) {
    PyObject *element = (PyObject *) tracker->contained;
    assert (element != NULL);

    if (PyList_Append(py_list, element) != 0) { goto fail; }

    tracker = tracker->next;
  }

  return py_list;
  fail:
    assert (PyErr_Occurred());
    Py_XDECREF(py_list);
    return NULL;
} // pyob_TrackerToList

#endif /* not def _KISUPPORT_LIFO_LINKED_LIST_H */
