/*
 * lettura_file_binario_in_lista.c
 */

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

typedef struct {
    char str[4];
    int num;
} t_data;

typedef struct t_list_element {
    t_data data;
    struct t_list_element * prev;
    struct t_list_element * next;
} t_list_element;

typedef struct {
    t_list_element * first;
    t_list_element * last;
} t_list;


void init_list(t_list * l)
{
    l->first = NULL;
    l->last = NULL;
}

void insert(t_list * list, t_data d)
{
    t_list_element *p;
    p = malloc(sizeof(t_list_element));
    p->data = d;
    if (list->first == NULL) {
        // the list is empty
        p->next = NULL;
        p->prev = NULL;
        list->first = list->last = p;
    }
    else {
        p->next = list->first;
        p->prev = NULL;
        list->first = p;
    }
}


// gets the head of the list, the data to insert and returns the new head of the list
void insert_in_order(t_list * list, t_data d)
{
    t_list_element * curr, *p;
    p = malloc(sizeof(t_list_element));
    p->data = d;
    if (list->first == NULL) {
        // the list is empty
        p->next = NULL;
        p->prev = NULL;
        list->first = list->last = p;
        return;
    }
    curr = list->first;
    while (curr != NULL) {
        if (strcmp(curr->data.str, d.str) > 0) {
            // position found
            if (curr == list->first) {
                // first item in list
                p->next = curr;
                p->prev = NULL;
                curr->prev = p;
                list->first = p;
                return;
            }
            else {
                // item in the middle of the list, before curr
                p->next = curr;
                p->prev = curr->prev;
                curr->prev->next = p;
                curr->prev = p;
                return;
            }
        }
        curr = curr->next;
    }
    // item at the end of the list
    p->next = NULL;
    p->prev = list->last;
    list->last->next = p;
    list->last = p;
}


// gets the filename and returns the list
void read_binary(char * filename, t_list * l)
{
    FILE * f;
    t_data d;
    f = fopen(filename, "rb");
    if (f == NULL)
        return;
    init_list(l);
    while (fread(&d, sizeof(t_data), 1, f) == 1) {
        insert(l, d);
    }
    fclose(f);
}


// gets the filename and returns the ORDERED list
void read_binary_in_order(char * filename, t_list * l)
{
    FILE * f;
    t_data d;
    f = fopen(filename, "rb");
    if (f == NULL)
        return;
    init_list(l);
    while (fread(&d, sizeof(t_data), 1, f) == 1) {
        insert_in_order(l, d);
    }
    fclose(f);
}


void print_list(t_list_element * l)
{
    if (l != NULL) {
        printf("%s, %d\n", l->data.str, l->data.num);
        print_list(l->next);
    }
}


void reverse_print_list(t_list_element * l)
{
    if (l != NULL) {
        printf("%s, %d\n", l->data.str, l->data.num);
        reverse_print_list(l->prev);
    }
}


int main(int argc, char ** argv)
{
    t_list list;
    read_binary("myfile.bin", &list);
    print_list(list.first);
    printf("--------------------\n");
    read_binary_in_order("myfile.bin", &list);
    printf("--------------------\n");
    print_list(list.first);
    printf("--------------------\n");
    reverse_print_list(list.last);
}
