#include "list.h"


// Alokuje v pameti seznam.
list * list_alloc (void)
{
  list * L = malloc(sizeof(list));
  (* L).curent = NULL;
  (* L).first = NULL;
  (* L).last = NULL;
  (* L).n = 0;
  return L;
}

// Vrati pocet prvku v seznamu.
unsigned int list_get_size (list * L)
{

  return(unsigned int) (* L).n;

}

// Prida prvek NEW_LAST_EL na konec seznamu L.
void list_add_last (list * L, element * el)
{
  
  list_element *new_last_el = list_element_alloc(el);

  if ((*L).last!=NULL) {

    list_element * el_last = (*L).last;
    (*el_last).next = new_last_el;
    (* new_last_el).prev = el_last;
    (*L).last = new_last_el;
    (*L).n++;

  } else {

    (*L).curent = new_last_el;
    (*L).first = new_last_el;
    (*L).last = new_last_el;
    (*L).n++;

  }

}

// Prida prvek NEW_FIRST_EL na zacatek seznamu L.
void list_add_first (list * L, element * el)
{

  list_element * new_first_el = list_element_alloc(el);

  if((* L).first!=NULL) {

    list_element * el_first = (* L).first;
    (* el_first).prev = new_first_el;
    (* new_first_el).next = el_first;
    (* L).first = new_first_el;
    (* L).n++;

  } else {
    
    (* L).curent = new_first_el;
    (* L).first = new_first_el;
    (* L).last = new_first_el;
    (* L).n++;

  }

}

// Posune seznam na dalsi prvek a vrati 1 pokud se to podari 0 pokud se to nepodari.
// Duvod ze se to nepodari je kdyz je seznam na konci a jeho nasledujici prvek neexistuje.
int list_next (list * L)
{

  if((* (* L).curent).next!=NULL) {

    (* L).curent = (* (* L).curent).next;

    return 1;

  } else {

    return 0;

  }

}

// Posune seznam na predchozi prvek a vrati 1 pokud se to podari 0 pokud se to nepodari.
// Duvod ze se to nepodari je kdyz je seznam na zactku a jeho predchozi prvek neexistuje.
int list_prev (list * L)
{

  if((* (* L).curent).prev!=NULL) {

    (* L).curent = (* (* L).curent).prev;

    return 1;

  } else {

    return 0;

  }

}

// Posune seznam na prvni prvek.
int list_first (list * L)
{

  if((* L).first!=NULL) {

    (* L).curent = (* L).first;
    return 1;

  } else {

    return 0;

  }

}

// Posune seznam na posledni prvek.
int list_last (list * L)
{

  if((* L).last!=NULL) {

    (* L).curent = (* L).last;
    return 1;

  } else {

    return 0;

  }

}

// Nastavi soucasny prvek. Pokud se nepodari vrati 0 pokud ano vrati 1.
int list_set_curent (list * L, element *el)
{
  element *curent = list_get_curent(L);

  if (curent!=NULL){
    curent = el;
    return 1;
  } else {
    return 0;
  }

}

// Vrati ukazatel na aktualni prvek seznamu L.
element * list_get_curent (list * L)
{

  return(* (* L).curent).element;

}

// Vrati ukazatel na nasledujici prvek seznamu L.
element * list_get_next (list * L)
{

  return(* (* (* L).curent).next).element;

}

// Vrati ukazatel na nasledujici prvek seznamu L.
element * list_get_prev (list * L)
{

  return(* (* (* L).curent).prev).element;

}

// Vrati ukazatel na prvni prvek seznamu L.
element * list_get_first (list * L)
{

  return(* (* L).first).element;

}

// Vrati ukazatel na posledni prvek seznamu L.
element * list_get_last (list * L)
{

  return(* (* L).last).element;

}

// Odstrani aktualni prvek z pameti a ze seznamu L. Po odstraneni se posune aktualni prvek na predchozi
// pokud je PREV = 1, na nasledujici pokud je PREV = 0. Pokud je ve funkci jediny prvek tak se odstrani
// i s celym seznamem a vrati 0. jesli se dany prvek podari smazat vrati 1, jinak 0 (seznam je prazdny).
int list_free_curent (list * L, int left)
{

  if((* L).n==0) {

    return 0;

  }

  // Pokud je aktualni prvek mezi 2 prvky
  if(((* (* L).curent).next!=NULL)&((* (* L).curent).prev!=NULL)) {

    list_element * el_del = (* L).curent;

    // Posunuti aktualniho prvku
    if(left) {

      (* L).curent = (* (* L).curent).prev;

    } else {

      (* L).curent = (* (* L).curent).next;

    }

    // Odecteni jednoho prvku ktery bude odstranen
    (* L).n--;

    // Uvolneni z pameti
    list_element_free(el_del);

    return 1;

  } else {

    // Pokud je aktualni prvek jako jediny v celem seznamu
    if(((* (* L).curent).next==NULL)&((* (* L).curent).prev==NULL)) {

      // Tento prvek je jediny takze zaroven prvni, posledni a aktualni. Tim padem ho staci znicit jednou.
      list_element_free((* L).curent);
      (* L).n = 0;

      return 1;

    } else {

      // Pokud je aktualni prvek koncovy a ma za sebou prvek
      if((* (* L).curent).next==NULL) {

        list_element * el_del = (* L).curent;

        // Nastaveni noveho ukazatele predchoziho prvku na nasledujici (NULL).
        (* (* (* L).curent).prev).next = NULL;

        // Posunuti aktualniho prvku a nastaveni koncoveho prvku
        (* L).curent = (* (* L).curent).prev;
        (* L).last = (* L).curent;

        // Odecteni jednoho prvku ktery bude odstranen
        (* L).n--;

        // Uvolneni z pameti
        list_element_free(el_del);

        return 1;

        // Pokud je aktualni prvek pocatecni a ma pred sebou prvek
      } else {

        list_element * el_del = (* L).curent;

        // Nastaveni noveho ukazatele nasledujiciho prvku na predchozi (NULL).
        (* (* (* L).curent).next).prev = NULL;

        // Posunuti aktualniho prvku a nastaveni prvniho prvku
        (* L).curent = (* (* L).curent).next;
        (* L).first = (* L).curent;

        // Odecteni jednoho prvku ktery bude odstranen
        (* L).n--;
        
        // Uvolneni z pameti
        list_element_free(el_del);

        return 1;

      }

    }

  }

}

// Uvolni z pameti seznam L.
void list_free (list * L)
{
  
  if(list_get_size(L)>0) {

    list_first(L);

    while(list_free_curent(L, 0)) {

    }

  }

  // Uvolnim prazdny seznam.
  free(L);

}


// Smaze strukturu seznamu ale data zachova.
void list_throw_away (list * L)
{

  element *el;
  
  if (list_first(L)==1) {

    do {
      el = list_get_curent(L);
      if (el!=NULL) {
        element_throw_away(el);
      }
    } while (list_next(L));

  }

  free(L);
}

// Vytiskne aktualni prvek seznamu L.
void list_print_curent (list * L)
{

  if((* L).curent!=NULL) {

    puts("{element}");
    element_print((* (* L).curent).element);

  } else {

    puts("list_print_curent: Seznam nemuze byt vytisknut, protoze neni nastaven aktualni prvek (list.curent).");

  }

}

// Vytiskne aktualni prvek seznamu L s odsazeni indent.
void list_print_curent_indent (list * L, unsigned int indent)
{

  int i;

  for(i = 0; i<indent; i++) {
    printf (" ");
  }

  if((* L).curent!=NULL) {

    element_print((* (* L).curent).element);

  } else {

    puts("list_print_curent: Seznam nemuze byt vytisknut, protoze neni nastaven aktualni prvek (list.curent).");

  }

}

// Vytiskne cely seznam L.
void list_print (list * L)
{

  if(list_first(L)) {

    do {

      list_print_curent(L);

    } while(list_next(L));

  }

}

// Vytiskne cely seznam L s odsazenim INDENT.
void list_print_indent (list * L, unsigned int indent)
{

  indent++;
  printf("list (%i)\n", list_get_size(L));

  if(list_first(L)) {

    do {

      list_print_curent_indent(L, indent);

    } while(list_next(L));

  }

}

// Vytiskne informace o obsahu seznamu.
void list_print_info (list * L)
{
  list_print_info_indent(L, 0);
}

// Vytiskne informace o obsahu seznamu s odsazenim od leveho okraje.
void list_print_info_indent (list * L, unsigned int indent)
{

  printf("list (%i)\n", list_get_size(L));
  list_first(L);
  // Zvyseni odsazeni o 1.
  indent++;

  do {

    printf("list element:\n");
    element_print_info_indent(list_get_curent(L), indent);

  } while(list_next(L));

}






// Alokuje element z celeho cisla a vrati na nej ukazatel.
list_element * list_element_alloc (element * el)
{

  list_element * l_el = malloc(sizeof(list_element));
  (* l_el).next = NULL;
  (* l_el).prev = NULL;
  (* l_el).element = el;
  return l_el;

}

// Uvolni prvek seznamu.
void list_element_free (list_element * l_el)
{

  element_free((* l_el).element);

  if(((* l_el).next!=NULL)&((* l_el).prev!=NULL)) {

    (* (* l_el).next).prev = (* l_el).prev;
    (* (* l_el).prev).next = (* l_el).next;

  }

  free(l_el);

}

// Odstrani prvek seznamu ale zachova data.
void list_element_throw_away (list_element * l_el)
{

  if(((* l_el).next!=NULL)&((* l_el).prev!=NULL)) {

    (* (* l_el).next).prev = (* l_el).prev;
    (* (* l_el).prev).next = (* l_el).next;

  }

  free(l_el);
  
}

// Vrati dany prvek.
element * lis_element_get (list_element * l_el)
{

  return(* l_el).element;

}