#pragma once

#include <stddef.h>
#include <stdint.h>

void *operator new(size_t, void *ptr);
void operator delete(void *, void *);

namespace hilbert::kernel::utility {

  template <class t>
  struct no_ref;
  template <class t>
  struct no_ref<t &&> {
    typedef t _t;
  };
  template <class t>
  struct no_ref<t &> {
    typedef t _t;
  };
  template <class t>
  struct no_ref {
    typedef t _t;
  };

  template <class t>
  constexpr typename no_ref<t>::_t &&move(t &&v) {
    return static_cast<typename no_ref<t>::_t &&>(v);
  }

  template <class t>
  static inline t min(t a, t b) {
    return a < b ? a : b;
  }

  //includes start_i, does not include end_i
  void mark_bitmap_region_zero(
    uint64_t *bitmap, uint64_t start_i, uint64_t end_i);
  //includes start_i, does not include end_i
  void mark_bitmap_region_one(
    uint64_t *bitmap, uint64_t start_i, uint64_t end_i);

  struct uuid {
    uint8_t bytes[16];
  };

  //if c appears in str, this returns the index of the first time it appears.
  //otherwise, this returns len.
  static inline unsigned find(const char *str, unsigned len, char c) {
    for (unsigned i = 0; i < len; ++i)
      if (str[i] == c)
        return i;
    return len;
  }

  //if c appears in str, this returns the index of the last time it appears.
  //otherwise, this returns len.
  static inline unsigned find_last(const char *str, unsigned len, char c) {
    for (unsigned i = len; i > 0; --i)
      if (str[i - 1] == c)
        return i - 1;
    return len;
  }

  //str1 starts with str2
  static inline bool starts_with(
    const char *str1, unsigned str1_len, const char *str2, unsigned str2_len
  ) {
    if (str1_len < str2_len)
      return false;
    for (unsigned i = 0; i < str2_len; ++i)
      if (str1[i] != str2[i])
        return false;
    return true;
  }

  template <class value_t>
  struct list {

    struct node {
      value_t value;
      node *next;
      node *prev;
    };

    node *first;
    node *last;

    list() : first(0), last(0) {}

    list(const list &other) = delete;
    list(list &&other) = delete;

    ~list() {
      if (first) {
        for (node *n = first->next; n; n = n->next)
          delete n->prev;
        delete last;
      }
    }

    list &operator =(const list &other) = delete;
    list &operator =(list &&other) = delete;

    void insert_end(const value_t &value) {
      node *n = new node {};
      n->value = value;
      n->next = 0;
      n->prev = last;
      last = n;
    }

    void insert_end(value_t &&value) {
      node *n = new node {};
      n->value = value;
      n->next = 0;
      n->prev = last;
      last = n;
    }

    void clear() {
      for (node *n = first; n; n = n->next)
        delete n;
      first = 0;
      last = 0;
    }

    //assumes there is a last.
    void remove_last() {
      node *new_last = last->prev;
      if (new_last)
        new_last->next = 0;
      else
        first = 0;
      delete last;
      last = new_last;
    }

    void remove(node *n) {
      if (n->prev)
        n->prev->next = n->next;
      else
        first = n->next;
      if (n->next)
        n->next->prev = n->prev;
      else
        last = n->prev;
      delete n;
    }

  };

  template <class value_t>
  struct vector {

    value_t *buffer;
    unsigned buffer_len;
    unsigned count;

    vector(unsigned buffer_len = 16)
      : buffer(new value_t[buffer_len]), buffer_len(buffer_len), count(0) {}

    vector(const value_t *copy_from, unsigned len)
      : buffer(new value_t[len]), buffer_len(len), count(len) {
      for (unsigned i = 0; i < len; ++i)
        buffer[i] = copy_from[i];
    }

    vector(const vector &other)
      : buffer(new value_t[other.buffer_len]),
        buffer_len(other.buffer_len), count(other.count)
    {
      for (unsigned i = 0; i < count; ++i)
        buffer[i] = other.buffer[i];
    }

    vector(vector &&other) = delete;

    ~vector() {
      if (buffer)
        delete[] buffer;
    }

    vector &operator =(const vector &other) {
      if (buffer)
        delete[] buffer;
      buffer = new value_t[other.buffer_len];
      buffer_len = other.buffer_len;
      count = other.count;
      for (unsigned i = 0; i < other.count; ++i)
        buffer[i] = other.buffer[i];
      return *this;
    }

    vector &operator =(vector &&other) {
      if (buffer)
        delete[] buffer;
      buffer = other.buffer;
      buffer_len = other.buffer_len;
      count = other.count;
      other.buffer = 0;
      return *this;
    }

    bool operator ==(const vector &other) {
      if (other.count != count)
        return false;
      for (unsigned i = 0; i < count; ++i)
        if (other.buffer[i] != buffer[i])
          return false;
      return true;
    }

    void verify_buffer_len(unsigned target_len) {
      if (buffer_len >= target_len)
        return;
      unsigned new_buffer_len = buffer_len;
      do
        new_buffer_len *= 2;
      while (new_buffer_len < target_len);
      value_t *new_buffer = new value_t[new_buffer_len];
      for (unsigned i = 0; i < count; ++i)
        new_buffer[i] = move(buffer[i]);
      delete[] buffer;
      buffer = new_buffer;
      buffer_len = new_buffer_len;
    }

    void add_end(value_t &&v) {
      verify_buffer_len(count + 1);
      buffer[count] = move(v);
      ++count;
    }

    void add_end(const value_t &v) {
      verify_buffer_len(count + 1);
      buffer[count] = v;
      ++count;
    }

    bool starts_with(const vector &other) {
      if (count < other.count)
        return false;
      for (unsigned i = 0; i < other.count; ++i)
        if (buffer[i] != other.buffer[i])
          return false;
      return true;
    }

  };

  typedef vector<char> string;

  template <class value_t>
  struct id_allocator {

    vector<value_t *> the_vector;

  public:
    id_allocator() : the_vector() {}
    id_allocator(const id_allocator &other) = delete;
    id_allocator(id_allocator &&other) = delete;
    id_allocator &operator =(const id_allocator &other) = delete;
    id_allocator &operator =(id_allocator &&other) = delete;

    //returns id; pointer becomes owned by id_allocator
    unsigned add(value_t *v) {
      for (unsigned i = 0; i < the_vector.count; ++i)
        if (the_vector.buffer[i] == 0) {
          the_vector.buffer[i] = v;
          return i;
        }
      the_vector.add_end(v);
      return the_vector.count - 1;
    }

    //returns id
    unsigned add_new(value_t &&v) {
      return add(new value_t(move(v)));
    }

    bool has_id(unsigned i) {
      return i < the_vector.count && the_vector.buffer[i] != 0;
    }

    value_t &get(unsigned i) {
      return *the_vector.buffer[i];
    }

    void remove_id(unsigned i) {
      delete the_vector.buffer[i];
      the_vector.buffer[i] = 0;
    }

  };

  template <class value_t>
  struct queue {

    value_t *buffer;
    unsigned buffer_len;
    unsigned count;
    unsigned next_read_index;

    queue(unsigned initial_len = 1024) : buffer(new value_t[initial_len]),
      buffer_len(initial_len), count(0), next_read_index(0) { }

    ~queue() {
      delete[] buffer;
    }

    void insert(const value_t &v) {

      if (count == buffer_len) {
        value_t *new_buffer = new value_t[buffer_len * 2];
        for (unsigned i = 0; i < buffer_len - next_read_index; ++i)
          new_buffer[i] = move(buffer[i + next_read_index]);
        for (unsigned i = buffer_len - next_read_index; i < buffer_len; ++i)
          new_buffer[i] = move(buffer[i + next_read_index - buffer_len]);
        delete[] buffer;
        buffer = new_buffer;
        buffer_len *= 2;
        next_read_index = 0;
      }

      buffer[(count + next_read_index) % buffer_len] = v;
      ++count;

    }

    //assumes not empty
    const value_t &peek() const {
      return buffer[next_read_index];
    }

    //assumes not empty
    value_t &&take() {
      value_t &&ret = move(buffer[next_read_index]);
      if (++next_read_index == buffer_len)
        next_read_index = 0;
      --count;
      return move(ret);
    }

    //assumes not empty
    value_t &last_inserted() const {
      return buffer[(count - 1 + next_read_index) % buffer_len];
    }

  };

}
