#include "dlist.h"

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

struct dlist *dlist_init(void)
{
    struct dlist *dlist = malloc(sizeof(struct dlist));
    if(!dlist) return NULL;
    dlist->size = 0;
    dlist->head = NULL;
    dlist->tail = NULL;
    return dlist;
}

void dlist_clear(struct dlist *list)
{
    if(!list) return;

    struct node *tmp = list->head;
    struct node *next;

    while(tmp != NULL){
        next = tmp->next;
        free(tmp);
        tmp = next;
    }
    free(list);
}

size_t dlist_length(struct dlist *l)
{
    if(!l) return 0;

    size_t count = 0;
    struct node *tmp = l->head;
    while(tmp != NULL){
        count ++;
        tmp = tmp->next;
    }
    return count;
}

void dlist_push_front(struct dlist *l, int data)
{
    if(!l) return;

    struct node *node = malloc(sizeof(struct node));
    if(!node) return;

    node->data = data;
    node->prev = NULL;
    
    if(l->head == NULL){
        node->next = NULL;
        //l->head = node;
        l->tail = node;
    }
    else{
        node->next = l->head;
        l->head->prev = node;
        //l->head = node;
    }

    l->head = node;
    l->size ++;
}

void dlist_push_back(struct dlist *l, int data)
{
    if(!l)return;
    struct node *node = malloc(sizeof(struct node));
    if(!node) return;

    node->data = data;
    node->next = NULL;

    if(l->head == NULL){
        node->prev = NULL;
        l->head = node;    

    }
    else{
        node->prev = l->tail;
        l->tail->next = node;
    }

    l->tail = node;
    l->size ++;


}

void dlist_remove(struct dlist *list, int data)
{
      if(!list || !list->head)return;

      struct node *tmp = list->head;
      //struct node *next;

      while(tmp != NULL && tmp->data != data){
        tmp = tmp->next;
      }

      if(tmp == NULL) return;

      if(tmp->prev != NULL) tmp->prev->next = tmp->next;
      else list->head = tmp->next;

      if(tmp->next != NULL)tmp->next->prev = tmp->prev;
      else list->tail = tmp->prev;


      //if(tmp->next == NULL && tmp->data != data) return;

      //if(tmp->data == data && tmp->next == NULL){
      //  tmp->prev->next = NULL;  
       // free(tmp);
        //return;
      //}
      //tmp->next->prev = tmp->prev;
      //tmp->prev->next = tmp->next;
      list->size --;
      free(tmp);

}

struct dlist *dlist_from_str(char *str)
{
    if(!str) return dlist_init();
    
    struct dlist *dlist = dlist_init();


    size_t len = strlen(str);
    size_t index = 0;
    for(size_t i = 0; i < len; i ++){
        
        if(str[i] == '+' || str[i] == '-'){
            char s = str[i];
            i ++;
            
            if(i >= len) break;

            int value = atoi(&str[i]);

            if(s == '+') dlist_push_back(dlist, value);
            else dlist_remove(dlist, value);
            index = i;

            while(i < len && str[index] >= '0' && str[index] <= '9'){
                if(i + 1 < len && str[index + 1] >= '0' && str[index + 1] <= '9') index ++;
                else break;
            }                           
        }
    }

    return dlist;
}

struct node *dlist_find(struct dlist *list, int data)
{
    if(!list) return NULL;

    struct node *tmp = list->head;

    while(tmp->next != NULL && tmp->data != data){
        tmp = tmp->next;
    }

    if(tmp->data == data) return tmp;

    return NULL;
}
/*
void dlist_print(struct dlist *l)
{
  
}
*/

void dlist_concat(struct dlist *l1, struct dlist *l2)
{
    if(!l1) return ;
    if(!l2) return ;
    if (!l1 && !l2) return ;
    
    struct node *tmp = l2->head;
    for (size_t i = 0; i < l2->size; i ++){
      dlist_push_back(l1, tmp->data);
      tmp = tmp->next;
    }

    dlist_clear(l2);
}

void dlist_insert(struct dlist *l, int data, int i)
{
    if(!l) return ;
    size_t o = i;
    if(o > l->size) return;
    
    if(i == 0) dlist_push_front(l, data);
    else if (o == l->size) dlist_push_back(l, data);
    else{
        
        struct node *tmp = l->head;
        
        for(size_t z = 0; z < o; z ++){
            tmp = tmp->next;
        }
    struct node *node = malloc(sizeof(struct node));
    if(!node)return;
    node->data = data;
    
    node->next = tmp;
    node->prev = tmp->prev;
    if(tmp->prev) tmp->prev->next = node;
    tmp->prev = node;
    
    
    l->size ++;
    }
}
/*
int main(){
struct dlist *l = dlist_init();
printf("length = %zu\n", l->size); // 0
printf("head = %p\n", (void *)l->head); // (nil) or NULL
printf("tail = %p\n", (void *)l->tail); // (nil) or NULL
dlist_clear(l);
// l is now invalid


    struct dlist *l = dlist_init();
size_t len = dlist_length(l);
printf("Length: %zu\n", len); // 0
dlist_clear(l);


    struct dlist *l = dlist_init(); // l: NULL
dlist_push_front(l, 10);              // l: 10 -> NULL
printf("%i\n",l->head->data);
dlist_push_front(l, 20);              // l: 20 -> 10 -> NULL
dlist_clear(l);


    struct dlist *l = dlist_init();
dlist_push_back(l, 10); // l: 10 -> NULL
printf("%i\n", l->tail->data);
dlist_push_back(l, 20); // l: 10 -> 20 -> NULL
printf("%i\n", l->tail->data);
dlist_clear(l);

    struct dlist *l = dlist_init();
dlist_push_back(l, 1);
dlist_push_back(l, 2);
dlist_push_back(l, 3);
dlist_push_back(l, 2);
                        // l: 1 -> 2 -> 3 -> 2 -> NULL
dlist_remove(l, 2);      // l: 1 -> 3 -> 2 -> NULL
dlist_remove(l, 2);      // l: 1 -> 3 -> NULL
dlist_remove(l, 2);      // l: 1 -> 3 -> NULL
printf("%i -> %i\n", l->head->data, l->tail->data);
dlist_clear(l);


    dlist_from_str("+2+10+42+2+223-2"); // NULL<->10<->42<->2<->223<->NULL
dlist_from_str("-2+10+42+2+223-2"); // NULL<->10<->42<->223<->NULL
dlist_from_str(""); // Return an empty list (size = 0, head = NULL, tail = NULL)
dlist_from_str("-2-10-42-2-223"); // Return an empty list too


    struct dlist *l = dlist_init();
dlist_push_back(l, 1);
dlist_push_back(l, 2);
dlist_push_back(l, 3);
dlist_push_back(l, 2);
                            // l: 1 -> 2 -> 3 -> 2 -> NULL
struct node *n;
n = dlist_find(l, 1);        // n->data == 1
printf("%i\n", n->data);
n = dlist_find(l, 3);        // n->data == 3
n = dlist_find(l, 223);      // n == NULL
dlist_clear(l);


    struct dlist *l1 = dlist_init();
for (int i = 0 ; i < 5 ; i++)
    dlist_push_back(l1, i);
struct dlist *l2 = dlist_init();
for (int i = 0 ; i < 5 ; i++)
    dlist_push_front(l2, i);
                                          // l1: 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> NULL
                                          // l2: 4 <-> 3 <-> 2 <-> 1 <-> 0 <-> NULL
dlist_concat(l1, l2);                      // l1: 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> 4 <-> 3 <-> 2 <-> 1 <-> 0 <-> NULL
                                          // l1->size = 10
struct node *tmp = l1->head;
for(size_t i = 0; i < l1->size; i ++){
    printf("%i - ", tmp->data);
    tmp = tmp->next;
}
free(tmp);
dlist_clear(l1); // If you freed correctly, no memory leaks should be displayed on your terminal

struct dlist *l = dlist_init();
for (int i = 0 ; i < 5 ; i++)
    dlist_push_back(l, i);
                                          // l: 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> NULL
dlist_insert(l, -4, 0);                    // l: -4 <-> 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> NULL
dlist_insert(l, 5, 6);                     // l: -4 <-> 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> 5 <-> NULL
dlist_insert(l, 5, 12);                    // l: -4 <-> 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> 5 <-> NULL
struct node *tmp = l->head;
for(size_t i = 0; i < l->size; i ++){
    printf("%i - ", tmp->data);
    tmp = tmp->next;
}
dlist_clear(l);



}*/
