// GList.h
#ifndef GLIST_H_INCLUDED
#define GLIST_H_INCLUDED

#ifdef _cplusplus
extern "C" {
#endif

typedef struct _GList GList;

struct _GList
{
    void* data;
    GList *next;
    GList *prev;
};

typedef void (*GFunc)(void* data, void* user_data);
typedef void (*GDestroyNodify)(void* data);
typedef int (*GCompareFunc)(void* a, void* b);

GList* g_list_alloc(void);
void g_list_free(GList *list);
void g_list_free_full(GList *list, GDestroyNodify free_func);

GList* g_list_append(GList *list, void* data);
GList* g_list_prepend(GList *list, void* data);
GList* g_list_insert(GList *list, void* data, int position);
GList* g_list_remove(GList *list, const void* data);
GList* g_list_find(GList *list, const void* data);
int g_list_index(GList *list, const void* data);
GList* g_list_nth(GList *list, unsigned int n);
void g_list_foreach(GList *list, GFunc func, void* user_data);
GList* g_list_first(GList *list);
GList* g_list_last(GList *list);
unsigned int g_list_length(GList *list);
GList* g_list_copy(GList *list);

#ifdef _cplusplus
}
#endif

#endif // GLIST_H_INCLUDED

// GList.c
#include "GList.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

GList* g_list_alloc(void)
{
    GList *list = (GList*)malloc(sizeof(GList));
    if (list)
    {
        memset(list, 0, sizeof(GList));
    }
    return list;
}

void g_list_free(GList *list)
{
    if (list)
    {
        free(list);
        list = NULL;
    }
}

void g_list_free_full(GList *list, GDestroyNodify free_func)
{
    g_list_foreach(list, (GFunc)free_func, NULL);
    g_list_free(list);
}

// Adds a new element on to the end of the list.
GList* g_list_append(GList *list, void* data)
{
    GList *new_list;
    GList *last;

    new_list = g_list_alloc();
    new_list->data = data;
    new_list->next = NULL;

    if (list)
    {
        last = g_list_last(list);
        last->next = new_list;
        new_list->prev = last;

        return list;
    }
    else
    {
        new_list->prev = NULL;

        return new_list;
    }
}

// Adds a new element on to the start of the list.
GList* g_list_prepend(GList *list, void* data)
{
    GList *new_list;

    new_list = g_list_alloc();
    new_list->data = data;
    new_list->next = list;

    if (list)
    {
        new_list->prev = list->prev;
        if (list->prev)
        {
            list->prev->next = new_list;
        }
        list->prev = new_list;
    }
    else
    {
        new_list->prev = NULL;
    }

    return new_list;
}

GList* g_list_insert(GList *list, void* data, int position)
{
    GList *new_list;
    GList *tmp_list;

    if (position < 0)
    {
        return g_list_append(list, data);
    }
    else if (position == 0)
    {
        return g_list_prepend(list, data);
    }

    tmp_list = g_list_nth(list, position);
    if (!tmp_list)
    {
        g_list_append(list, data);
    }

    new_list = g_list_alloc();
    new_list->data = data;
    new_list->prev = tmp_list->prev;
    if (tmp_list->prev)
    {
        tmp_list->prev->next = new_list;
    }
    new_list->next = tmp_list;
    tmp_list->prev = new_list;

    if (tmp_list == list)
    {
        return new_list;
    }
    else
    {
        return list;
    }
}

GList* g_list_remove(GList *list, const void* data)
{
    GList *tmp;

    tmp = list;
    while (tmp)
    {
        if (tmp->data != data)
        {
            tmp = tmp->next;
        }
        else
        {
            if (tmp->prev)
            {
                tmp->prev->next = tmp->next;
            }

            if (tmp->next)
            {
                tmp->next->prev = tmp->prev;
            }

            if (list == tmp)
            {
                list = list->next;
            }

            g_list_free(tmp);
            break;
        }
    }

    return list;
}

GList* g_list_find(GList *list, const void* data)
{
    while (list)
    {
        if (list->data == data)
        {
            break;
        }
        list = list->next;
    }

    return list;
}

int g_list_index(GList *list, const void* data)
{
    int i = 0;

    while (list)
    {
        if (list->data == data)
        {
            return i;
        }
        i++;
        list = list->next;
    }

    return -1;
}

GList* g_list_nth(GList *list, unsigned int n)
{
    while ((n-- > 0) && list)
    {
        list = list->next;
    }

    return list;
}

void g_list_foreach(GList *list, GFunc func, void* user_data)
{
    while (list)
    {
        GList *next = list->next;
        (*func)(list->data, user_data);
        list = next; // list = list->next;
    }
}

GList* g_list_first(GList *list)
{
    if (list)
    {
        while (list->prev)
        {
            list = list->prev;
        }
    }

    return list;
}

GList* g_list_last(GList *list)
{
    if (list)
    {
        while (list->next)
        {
            list = list->next;
        }
    }

    return list;
}

unsigned int g_list_length(GList *list)
{
    unsigned int length = 0;

    while (list)
    {
        length++;
        list = list->next;
    }

    return length;
}

GList* g_list_copy(GList *list)
{
    GList *new_list = NULL;

    if (list)
    {
        GList *last;

        new_list = g_list_alloc();
        new_list->data = list->data;
        new_list->prev = NULL;
        last = new_list;
        list = list->next;
        while (list)
        {
            last->next = g_list_alloc();
            last->next->prev = last;
            last = last->next;
            last->data = list->data;
            list = list->next;
        }
        last->next = NULL;
    }

    return new_list;
}

// main.c
#include <stdio.h>
#include <stdlib.h>
#include "GList.h"

void ShowList(void* data, void* user_data);

/// test
int main()
{
    GList *list = NULL;
    list = g_list_append(list, "one");
    list = g_list_append(list, "two");
    list = g_list_append(list, "three");
    list = g_list_prepend(list, "four");
    list = g_list_insert(list, "five", 3);

    GList *first = g_list_first(list);
    g_list_foreach(first, ShowList, NULL);

    int num = g_list_length(first);
    printf("num = %d\n", num);

    GList *last = g_list_last(list);
    printf("last = %s\n", (char*)last->data);

    printf("---------------copy---------------\n");
    first = g_list_first(list);
    GList *cpy = g_list_copy(first);
    g_list_foreach(cpy, ShowList, NULL);

    return 0;
}

void ShowList(void* data, void* user_data)
{
    printf("%s\n", (char*)data);
}