#ifndef _List_h
#define _List_h

#include <stdlib.h>

#include "wtrace/werror.h"

#ifndef false
#define false 0
#define true 1
#endif

typedef struct T T;
typedef struct Node Node;

typedef struct T {
  Node* actualPtr;  // Die aktuelle Position in der Liste;
  Node* startPtr;  // Pointer auf Start of List.
  Node* endPtr;  // Pointer auf End of List.
  int actualPos;
  int startPos;
  int endPos;
} T;

typedef struct Node {
  Node* Next;
  Node* Prev;
  T* Pointer;
} Node;

void empty(T* this);  // Leert die Liste.

void emptyWithElementDelete(T* this);  // Leert die Liste und loesch die Elemente.

T* first(T* this);  // Setzt AktListenPosition auf das erste Element
                    // und liefert den Pointer darauf.

T* last(T* this);  // Setzt AktListenPosition auf das letzte Element.
                   // und liefert den Pointer darauf.
                   // das letzte Element ist immer leer

T* next(T* this);  // Erhoeht AktListenPosition um eins.
                   // und liefert den Pointer darauf.

T* previous(T* this);  // Erniedrigt AktListenPostion um eins.
                       // und liefert den Pointer darauf.

int isFirst(T* this);  // Ist der Start of List erreicht?
int isLast(T* this);   // Ist das End of List erreicht?
int isEmpty(T* this);  // Ist die Liste vielleicht leer?

T* insert(T* this, void *new_pointer);  // Fuegt neues Element vor der AktPos ein.
T* append(T* this, void *new_pointer);  // Haengt neues Element an di List an.

T* Tremove(T* this);  // Loescht den Akt. Knoten aus der Liste
                     // und liefert den Pointer auf das Element.

void removeWithElementDelete(T* this);  // Loescht den Akt. Knoten aus der Liste
                                        // und das Element des Knotens!

void *retrieve(T* this);  // Liefert einen Pointer auf das aktuelle Element.

void exchange(T* this, int pos1, int pos2);  // vertauscht die beiden Elemente der Positionen  pos1 und pos2

int setPos(T* this, int pos);  // 0..ende-1
int getPos(T* this);           // 0..ende-1

// Die Implementierung der Elementfunktionen von PointerListe !!

T *new_T(void) {
  Node *N;
  N = malloc(sizeof(Node));
  N->Pointer = NULL;
  N->Next = NULL;
  N->Prev = NULL;

  T *this = malloc(sizeof(T));
  this->endPtr = N;
  this->actualPtr = N;
  this->startPtr = N;

  this->startPos = 0;
  this->actualPos = 0;
  this->endPos = 0;
return this;
}

void free_T(T *this) {
  while (this->startPtr != NULL) {
    Node* N = this->startPtr;
    this->startPtr = N->Next;
    free(N);
  }
  free(this);
}

void empty(T *this) {
  while (this->startPtr != this->endPtr)  // Alle Knoten loeschen.
  {
    Node* N = this->startPtr;
    this->startPtr = N->Next;
    free(N);  // Knoten loeschen
  }

  this->startPtr->Prev = NULL;

  this->actualPtr = this->endPtr;

  this->actualPos = 0;
  this->startPos = 0;
  this->endPos = 0;
}

void emptyWithElementDelete(T *this) {
  while (this->startPtr != this->endPtr)  // Alle Knoten loeschen.
  {
    Node* N = this->startPtr;
    this->startPtr = N->Next;

    free(N->Pointer);  // Element loeschen

    free(N);  // Knoten loeschen
  }

  this->startPtr->Prev = NULL;

  this->actualPtr = this->endPtr;

  this->actualPos = 0;
  this->startPos = 0;
  this->endPos = 0;
}

T* first(T *this) {
  if (isEmpty(this))  // is list empty?
    return NULL;
  this->actualPos = 0;
  this->actualPtr = this->startPtr;
  return this->actualPtr->Pointer; /* falls Liste leer wird NULL zurueckgegeben */
}

T* last(T *this) {
  this->actualPtr = this->endPtr;
  this->actualPos = this->endPos;
  // das letzte Element ist immer leer!!!!
  //return endPtr->Pointer;
  return NULL;
}

T* next(T *this) {
  if (this->actualPtr != this->endPtr) {
    this->actualPos++;
    return (this->actualPtr = this->actualPtr->Next)->Pointer;
  }
  return NULL;  // falls Liste am Ende ist wird NULL zurueckgegeben
}

T* previous(T *this) {
  if (this->actualPtr != this->startPtr) {
    this->actualPos--;
    return (this->actualPtr = this->actualPtr->Prev)->Pointer;
  } else
    return NULL;  // falls Liste am Anfang ist wird NULL zurueckgegeben
}

int isLast(T *this) {
  return (this->actualPtr == this->endPtr);
}

int isFirst(T *this) {
  return (this->actualPtr == this->startPtr);
}

int isEmpty(T *this) {
  return (this->startPtr == this->endPtr);
}

T* insert(T *this, void *NewPointer) {
  char* __fn = "PointerListe::insert";

  T* R;
  Node *N;
  N = malloc(sizeof(Node));

  if (N != NULL) {
    N->Next = this->actualPtr;
    N->Prev = this->actualPtr->Prev;
    N->Pointer = NewPointer;

    if (!isEmpty(this)) this->actualPtr->Prev->Next = N;
    this->actualPtr->Prev = N;

    R = NewPointer;

    if (isEmpty(this) || (this->actualPtr == this->startPtr)) this->startPtr = N;
    this->actualPtr = N;

    this->endPos++;
  } else {
    ErrRecover(__fn, "", "mem allocation error");
    exit(1);
  }
  return R;
}

T* append(T *this, void *NewPointer) {
  last(this);
  return insert(this, NewPointer);
}

void *retrieve(T *this) {
  return this->actualPtr->Pointer;  // wenn Liste leer, dann wird NULL zurueckgeben!
}

T* Tremove(T *this) {
  Node *N = NULL;
  T* R = NULL;
  if (!isEmpty(this)) {
    N = this->actualPtr;
    R = this->actualPtr->Pointer;
    if (isFirst(this)) {  // Start of List
      N->Next->Prev = NULL;
      this->startPtr = N->Next;
      this->actualPtr = this->startPtr;
    } else if (isLast(this)) {  // End of List
      N->Prev->Next = NULL;
      this->endPtr = N->Prev;
      this->actualPtr = this->endPtr;
    } else {  // inbetween
      N->Next->Prev = N->Prev;
      N->Prev->Next = N->Next;
      this->actualPtr = this->actualPtr->Next;  // Loeschen wie ENTFERNEN nicht wie BACKSPACE!
    }
    free(N);
    this->endPos--;
  }
  return R;
}

void removeWithElementDelete(T *this) {
  Node *N;
  if (!isEmpty(this)) {
    N = this->actualPtr;
    if (isFirst(this)) {  // Start of List
      N->Next->Prev = NULL;
      this->startPtr = N->Next;
      this->actualPtr = this->startPtr;
    } else if (isLast(this)) {  // End of List
      N->Prev->Next = NULL;
      this->endPtr = N->Prev;
      this->actualPtr = this->endPtr;
    } else {  // inbetween
      N->Next->Prev = N->Prev;
      N->Prev->Next = N->Next;
      this->actualPtr = this->actualPtr->Next;  // Loeschen wie ENTFERNEN nicht wie BACKSPACE!
    }
    free(N->Pointer);  // Element loeschen
    free(N);          // Knoten loeschen
    this->endPos--;
  }
}

int setPos(T *this, int pos) {
  if ((pos < 0) || (pos > this->endPos)) {
    return -1;  // -1   if pos is invalid
  } else {
    this->actualPtr = this->startPtr;
    for (int i = 0; i < pos; i++) this->actualPtr = this->actualPtr->Next;
    return (this->actualPos = pos);
  }
}

int getPos(T *this) {
  return this->actualPos;
}

void exchange(T *this, int pos1, int pos2) {
  char* __fn = "PointerListe<T>::switch";
  T* posData;
  Node *posPtr;

  int oldPos = this->actualPos;

  if (pos1 == pos2) return;

  if (pos1 < 0) {
    pos1 = 0;
    ErrRecover(__fn, "", "pos1( %d ) < 0; setting pos1 = 0!", pos1);
  }
  if (pos1 > this->endPos) {
    pos1 = this->endPos;
    ErrRecover(__fn, "", "pos1( %d ) > endPos( %d ); setting pos1 = endPos!", pos1, this->endPos);
  }
  if (pos2 < 0) {
    pos2 = 0;
    ErrRecover(__fn, "", "pos2( %d ) < 0; setting pos2 = 0!", pos2);
  }
  if (pos2 > this->endPos) {
    pos2 = this->endPos;
    ErrRecover(__fn, "", "pos2( %d ) > endPos( %d ), setting pos2 = endPos!", pos2, this->endPos);
  }

  setPos(this, pos1);
  posPtr = this->actualPtr;
  posData = this->actualPtr->Pointer;

  setPos(this, pos2);
  posPtr->Pointer = this->actualPtr->Pointer;
  this->actualPtr->Pointer = posData;

  setPos(this, oldPos);
}

#endif  // _LIST_H
