#ifndef _RSTL_LIST
#define _RSTL_LIST

#include "types.h"

#include "rstl/construct.hpp"
#include "rstl/rmemory_allocator.hpp"

namespace rstl {
template < typename T, typename Alloc = rmemory_allocator >
class list {
public:
  class iterator;
  class const_iterator;
  iterator erase(const iterator& item) { return do_erase(item.get_node()); }

// private:
  struct node;
  node* do_erase(node* item);

public:
  list()
  : x4_start(reinterpret_cast< node* >(&xc_empty_prev))
  , x8_end(reinterpret_cast< node* >(&xc_empty_prev))
  , xc_empty_prev(reinterpret_cast< node* >(&xc_empty_prev))
  , x10_empty_next(reinterpret_cast< node* >(&xc_empty_prev))
  , x14_count(0) {}
  ~list();

  void push_back(const T& val) { do_insert_before(x8_end, val); }
  void clear() { erase(begin(), end()); }

  size_t size() const { return x14_count; }
  bool empty() const { return x14_count == 0; }

  void pop_front() { erase(x4_start); }

  iterator begin() { return iterator(x4_start); }
  const_iterator begin() const { return const_iterator(x4_start); }
  iterator end() { return iterator(x8_end); }
  const_iterator end() const { return const_iterator(x8_end); }

  iterator erase(const iterator& start, const iterator& end) {
    node* last = end.get_node();
    node* it = start.get_node();
    for (node* t = it; t != last; t = t->get_next()) {
    }

    while (it != last) {
      it = do_erase(it);
    }
    return iterator(it);
  }

  struct node {
    node* x0_prev;
    node* x4_next;
    uchar x8_item[sizeof(T)];

    node(node* prev, node* next) : x0_prev(prev), x4_next(next) {}

    node* get_prev() const { return x0_prev; }
    node* get_next() const { return x4_next; }
    void set_prev(node* prev) { x0_prev = prev; }
    void set_next(node* next) { x4_next = next; }
    T* get_value() { return reinterpret_cast< T* >(&x8_item); }
    const T* get_value() const { return reinterpret_cast< const T* >(&x8_item); }
  };

  node* create_node(node* prev, node* next, const T& val) {
    node* n;
    x0_allocator.allocate(n, 1);
    new (n) node(prev, next);
    construct(n->get_value(), val);
    return n;
  }

  node* do_insert_before(node* n, const T& val) {
    node* nn = create_node(n->get_prev(), n, val);
    if (n == x4_start) {
      x4_start = nn;
    }
    nn->get_prev()->set_next(nn);
    nn->get_next()->set_prev(nn);
    ++x14_count;

    return nn;
  }

public:
  class const_iterator {
  public:
    typedef T* value_type;

    const_iterator() : current(nullptr) {}
    const_iterator(node* begin) : current(begin) {}
    const_iterator& operator++() {
      this->current = this->current->x4_next;
      return *this;
    }
    const_iterator operator++(int) const { return const_iterator(this->current->x4_next); }
    const_iterator& operator--() {
      this->current = this->current->x0_prev;
      return *this;
    }
    const_iterator operator--(int) const { return const_iterator(this->current->x0_prev); }
    const T* get_pointer() const { return current->get_value(); }
    const T& operator*() const { return *current->get_value(); }
    const T* operator->() const { return current->get_value(); }
    bool operator==(const const_iterator& other) const { return current == other.current; }
    bool operator!=(const const_iterator& other) const { return current != other.current; }

    node* get_node() const { return current; }

  protected:
    node* current;
  };

  class iterator : public const_iterator {
  public:
    typedef T* value_type;

    iterator() : const_iterator(nullptr) {}
    iterator(node* begin) : const_iterator(begin) {}
    iterator& operator++() {
      this->current = this->current->x4_next;
      return *this;
    }
    iterator operator++(int) {
      node* cur = this->current;
      this->current = this->current->x4_next;
      return cur;
    }
    iterator& operator--() {
      this->current = this->current->x0_prev;
      return *this;
    }
    iterator operator--(int) { return iterator(this->curent->x0_prev); }
    T* get_pointer() const { return this->current->get_value(); }
    T& operator*() const { return *this->current->get_value(); }
    T* operator->() const { return this->current->get_value(); }
    bool operator==(const iterator& other) const { return this->current == other.current; }
    bool operator!=(const iterator& other) const { return this->current != other.current; }
  };

private:
  Alloc x0_allocator;
  node* x4_start;
  node* x8_end;
  node* xc_empty_prev;
  node* x10_empty_next;
  int x14_count;
};

template < typename T, typename Alloc >
list< T, Alloc >::~list() {
  node* cur = x4_start;
  while (cur != x8_end) {
    node* it = cur;
    node* next = cur->get_next();
    cur = next;
    destroy(it->get_value());
    x0_allocator.deallocate(it);
  }
}

template < typename T, typename Alloc >
typename list< T, Alloc >::node* list< T, Alloc >::do_erase(node* node) {
  typename list< T, Alloc >::node* result = node->get_next();
  if (node == x4_start) {
    x4_start = result;
  }
  node->get_prev()->set_next(node->get_next());
  node->get_next()->set_prev(node->get_prev());
  destroy(node->get_value());
  x0_allocator.deallocate(node);
  x14_count--;
  return result;
}

} // namespace rstl

#endif // _RSTL_LIST
