/* linkedlist-generic.c -- Generische einfach verkettete Liste
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* Definition eines Funktionstyps zum Iterieren */
typedef void (*generic_function)(void *node_data, void *function_data);

/* Strukturdefinitionen */

struct generic_node {
  void *node_data;
  struct generic_node *next;
};

struct generic_list {
  /* Listenkopf */
  struct generic_node *head;
  /* Funktion zum Loeschen eines Elements: */
  generic_function delete_function;
  void *delete_function_data;
};

/* Konstruktor */

struct generic_list *new_generic_list(generic_function delete_function,
				      void *delete_function_data)
{
  struct generic_list *list = malloc(sizeof(struct generic_list));
  list->head = NULL;
  list->delete_function = delete_function;
  list->delete_function_data = delete_function_data;
  return list;
}

/* Destruktor */

void free_generic_list(struct generic_list *list)
{
  struct generic_node *node = list->head;
  free(list);
  while (node != NULL) {
    struct generic_node *next = node->next;
    list->delete_function(node->node_data,
			  list->delete_function_data);
    free(node);
    node = next;
  }
}

/* Einfuegen eines Datenelements */

void generic_list_push(struct generic_list *list, void *node_data)
{
  struct generic_node *node = malloc(sizeof(struct generic_node));
  node->node_data = node_data;
  node->next = list->head;
  list->head = node;
}

/* Funktion zum Iterieren */

void generic_list_for_each(struct generic_list *list,
			   generic_function function,
			   void *function_data)
{
  struct generic_node *node = list->head;
  while (node != NULL) {
    (*function)(node->node_data, function_data);
    node = node->next;
  }
}

/* Zeichenketten-Liste; Gesamtlaenge aller Zeichenketten: */

static void free_string(void *node_data, void *function_data)
{
  char *text = node_data;
  free(text);
}

struct generic_list *new_string_list()
{
  return new_generic_list(free_string, NULL);
}

static void add_to_length(void *node_data, void *function_data)
{
  char *text = node_data;	/* Impliziter Typecast */
  int *length_p = function_data;
  (*length_p) += strlen(text);
}
			  
int total_length(struct generic_list *list)
{
  int length = 0;
  generic_list_for_each(list, add_to_length, &length);
  return length;
}

/* Integer-Liste; Produkt aller Zahlen */

static void free_int(void *node_data, void *function_data)
{
  /* Kein dynamischer Speicher benutzt -- also nichts freizugeben */
}

struct generic_list *new_int_list()
{
  return new_generic_list(free_int, NULL);
}

void do_multiply(void *node_data, void *function_data)
{
  int factor = (int) node_data; /* Expliziter Typecast */
  int *result_p = function_data;
  (*result_p) *= factor;
}

int product(struct generic_list *list)
{
  int result = 1;
  generic_list_for_each(list, do_multiply, &result);
  return result;
}

/* Hauptprozedur */

int main ()
{
  {
    /* Zeichenketten-Liste */
    struct generic_list *list = new_string_list();
    generic_list_push(list, strdup("Hello")); /* Impliziter Typecast */
    generic_list_push(list, strdup("Good-bye"));
    printf("Gesamtlaenge: %d\n", total_length(list));
    free_generic_list(list);
  }
  {
    /* Integer-Liste */
    struct generic_list *list = new_int_list();
    generic_list_push(list, (void *) 7); /* Expliziter Typecast */
    generic_list_push(list, (void *) 673);
    printf("Produkt: %d\n", product(list));
    free_generic_list(list);
  }
  return 0;
}
