#ifndef _KERNEL_LIBK_LIST_H
#define _KERNEL_LIBK_LIST_H

struct list_node_link {
  struct list_node_link* next;
  struct list_node_link* prev;
};

#define list_entry(ptr, type, member) ((type*)((char*)(ptr) - offsetof (type, member)))

#define list_append(head, new)                                                                     \
  do {                                                                                             \
    if ((new) != NULL) {                                                                           \
      (new)->next = NULL;                                                                          \
      if ((head) != NULL) {                                                                        \
        struct list_node_link* __tmp = (head);                                                     \
        while (__tmp->next != NULL) {                                                              \
          __tmp = __tmp->next;                                                                     \
        }                                                                                          \
        __tmp->next = (new);                                                                       \
        (new)->prev = __tmp;                                                                       \
      } else {                                                                                     \
        (new)->prev = NULL;                                                                        \
        (head) = (new);                                                                            \
      }                                                                                            \
    }                                                                                              \
  } while (0)

#define list_prepend(head, new)                                                                    \
  do {                                                                                             \
    if ((new) != NULL) {                                                                           \
      (new)->prev = NULL;                                                                          \
      (new)->next = (head);                                                                        \
      if ((head) != NULL) {                                                                        \
        (head)->prev = (new);                                                                      \
      }                                                                                            \
      (head) = (new);                                                                              \
    }                                                                                              \
  } while (0)

#define list_remove(head, ele)                                                                     \
  do {                                                                                             \
    if ((ele) != NULL) {                                                                           \
      if ((ele)->prev != NULL) {                                                                   \
        (ele)->prev->next = (ele)->next;                                                           \
      } else {                                                                                     \
        (head) = (ele)->next;                                                                      \
      }                                                                                            \
      if ((ele)->next != NULL) {                                                                   \
        (ele)->next->prev = (ele)->prev;                                                           \
      }                                                                                            \
      (ele)->next = NULL;                                                                          \
      (ele)->prev = NULL;                                                                          \
    }                                                                                              \
  } while (0)

#define list_find(head, out, propname, propvalue)                                                  \
  do {                                                                                             \
    (out) = NULL;                                                                                  \
    struct list_node_link* __tmp = (head);                                                         \
    while (__tmp) {                                                                                \
      if (__tmp->propname == (propvalue)) {                                                        \
        (out) = __tmp;                                                                             \
        break;                                                                                     \
      }                                                                                            \
      __tmp = __tmp->next;                                                                         \
    }                                                                                              \
  } while (0)

#define list_foreach(head, var, tmp)                                                               \
  for (var = (head), tmp = (var ? var->next : NULL); var != NULL;                                  \
       var = tmp, tmp = (var ? var->next : NULL))

#define list_foreach_index(head, var, tmp, idx)                                                    \
  for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL;                       \
       var = tmp, tmp = (var ? var->next : NULL), (idx)++)

#define list_foreach_index_limit(head, var, tmp, idx, max)                                         \
  for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL && (idx) < (max);      \
       var = tmp, tmp = (var ? var->next : NULL), (idx)++)

#define list_back(head, out)                                                                       \
  do {                                                                                             \
    (out) = NULL;                                                                                  \
    if ((head) != NULL) {                                                                          \
      struct list_node_link* __tmp = (head);                                                       \
      while (__tmp->next != NULL) {                                                                \
        __tmp = __tmp->next;                                                                       \
      }                                                                                            \
      (out) = __tmp;                                                                               \
    }                                                                                              \
  } while (0)

#define list_front(head, out)                                                                      \
  do {                                                                                             \
    (out) = NULL;                                                                                  \
    if ((head) != NULL) {                                                                          \
      struct list_node_link* __tmp = (head);                                                       \
      while (__tmp->prev != NULL) {                                                                \
        __tmp = __tmp->prev;                                                                       \
      }                                                                                            \
      (out) = __tmp;                                                                               \
    }                                                                                              \
  } while (0)

#define list_insert_after(head, pos, new)                                                          \
  do {                                                                                             \
    if ((pos) != NULL && (new) != NULL) {                                                          \
      (new)->prev = (pos);                                                                         \
      (new)->next = (pos)->next;                                                                   \
      if ((pos)->next != NULL) {                                                                   \
        (pos)->next->prev = (new);                                                                 \
      }                                                                                            \
      (pos)->next = (new);                                                                         \
    } else if ((pos) == NULL && (head) == NULL) {                                                  \
      (new)->prev = NULL;                                                                          \
      (new)->next = NULL;                                                                          \
      (head) = (new);                                                                              \
    }                                                                                              \
  } while (0)

#define list_insert_before(head, pos, new)                                                         \
  do {                                                                                             \
    if ((pos) != NULL && (new) != NULL) {                                                          \
      (new)->next = (pos);                                                                         \
      (new)->prev = (pos)->prev;                                                                   \
      if ((pos)->prev != NULL) {                                                                   \
        (pos)->prev->next = (new);                                                                 \
      } else {                                                                                     \
        (head) = (new);                                                                            \
      }                                                                                            \
      (pos)->prev = (new);                                                                         \
    } else if ((pos) == NULL && (head) == NULL) {                                                  \
      (new)->prev = NULL;                                                                          \
      (new)->next = NULL;                                                                          \
      (head) = (new);                                                                              \
    }                                                                                              \
  } while (0)

#define list_index_of(head, ele, out_idx)                                                          \
  do {                                                                                             \
    (out_idx) = -1;                                                                                \
    int __idx = 0;                                                                                 \
    struct list_node_link* __tmp = (head);                                                         \
    while (__tmp != NULL) {                                                                        \
      if (__tmp == (ele)) {                                                                        \
        (out_idx) = __idx;                                                                         \
        break;                                                                                     \
      }                                                                                            \
      __tmp = __tmp->next;                                                                         \
      __idx++;                                                                                     \
    }                                                                                              \
  } while (0)

#define list_index_of_prop(head, propname, propvalue, out_idx)                                     \
  do {                                                                                             \
    (out_idx) = -1;                                                                                \
    int __idx = 0;                                                                                 \
    struct list_node_link* __tmp = (head);                                                         \
    while (__tmp != NULL) {                                                                        \
      if (__tmp->propname == (propvalue)) {                                                        \
        (out_idx) = __idx;                                                                         \
        break;                                                                                     \
      }                                                                                            \
      __tmp = __tmp->next;                                                                         \
      __idx++;                                                                                     \
    }                                                                                              \
  } while (0)

#endif // _KERNEL_LIBK_LIST_H
