/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_LIBARTBASE_BASE_INTRUSIVE_FORWARD_LIST_H_
#define ART_LIBARTBASE_BASE_INTRUSIVE_FORWARD_LIST_H_

#include <stdint.h>
#include <functional>
#include <iterator>
#include <memory>
#include <type_traits>

#include <android-base/logging.h>

#include "base/casts.h"
#include "base/macros.h"

namespace art {

struct IntrusiveForwardListHook {
  IntrusiveForwardListHook() : next_hook(nullptr) { }
  explicit IntrusiveForwardListHook(const IntrusiveForwardListHook* hook) : next_hook(hook) { }

  // Allow copyable values but do not copy the hook, it is not part of the value.
  IntrusiveForwardListHook(const IntrusiveForwardListHook& other ATTRIBUTE_UNUSED)
      : next_hook(nullptr) { }
  IntrusiveForwardListHook& operator=(const IntrusiveForwardListHook& src ATTRIBUTE_UNUSED) {
    return *this;
  }

  mutable const IntrusiveForwardListHook* next_hook;
};

template <typename Derived, typename Tag = void>
struct IntrusiveForwardListNode : public IntrusiveForwardListHook {
};

template <typename T, IntrusiveForwardListHook T::* NextPtr = &T::hook>
class IntrusiveForwardListMemberHookTraits;

template <typename T, typename Tag = void>
class IntrusiveForwardListBaseHookTraits;

template <typename T,
          typename HookTraits =
              IntrusiveForwardListBaseHookTraits<typename std::remove_const<T>::type>>
class IntrusiveForwardList;

template <typename T, typename HookTraits>
class IntrusiveForwardListIterator : public std::iterator<std::forward_iterator_tag, T> {
 public:
  // Construct/copy/destroy (except the private constructor used by IntrusiveForwardList<>).
  IntrusiveForwardListIterator() : hook_(nullptr) { }
  IntrusiveForwardListIterator(const IntrusiveForwardListIterator& src) = default;
  IntrusiveForwardListIterator& operator=(const IntrusiveForwardListIterator& src) = default;

  // Conversion from iterator to const_iterator.
  template <typename OtherT,
            typename = typename std::enable_if<std::is_same<T, const OtherT>::value>::type>
  IntrusiveForwardListIterator(const IntrusiveForwardListIterator<OtherT, HookTraits>& src)  // NOLINT, implicit
      : hook_(src.hook_) { }

  // Iteration.
  IntrusiveForwardListIterator& operator++() {
    DCHECK(hook_ != nullptr);
    hook_ = hook_->next_hook;
    return *this;
  }
  IntrusiveForwardListIterator operator++(int) {
    IntrusiveForwardListIterator tmp(*this);
    ++*this;
    return tmp;
  }

  // Dereference
  T& operator*() const {
    DCHECK(hook_ != nullptr);
    return *HookTraits::GetValue(hook_);
  }
  T* operator->() const {
    return &**this;
  }

 private:
  explicit IntrusiveForwardListIterator(const IntrusiveForwardListHook* hook) : hook_(hook) { }

  const IntrusiveForwardListHook* hook_;

  template <typename OtherT, typename OtherTraits>
  friend class IntrusiveForwardListIterator;

  template <typename OtherT, typename OtherTraits>
  friend class IntrusiveForwardList;

  template <typename OtherT1, typename OtherT2, typename OtherTraits>
  friend typename std::enable_if<std::is_same<const OtherT1, const OtherT2>::value, bool>::type
  operator==(const IntrusiveForwardListIterator<OtherT1, OtherTraits>& lhs,
             const IntrusiveForwardListIterator<OtherT2, OtherTraits>& rhs);
};

template <typename T, typename OtherT, typename HookTraits>
typename std::enable_if<std::is_same<const T, const OtherT>::value, bool>::type operator==(
    const IntrusiveForwardListIterator<T, HookTraits>& lhs,
    const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
  return lhs.hook_ == rhs.hook_;
}

template <typename T, typename OtherT, typename HookTraits>
typename std::enable_if<std::is_same<const T, const OtherT>::value, bool>::type operator!=(
    const IntrusiveForwardListIterator<T, HookTraits>& lhs,
    const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
  return !(lhs == rhs);
}

// Intrusive version of std::forward_list<>. See also slist<> in Boost.Intrusive.
//
// This class template provides the same interface as std::forward_list<> as long
// as the functions are meaningful for an intrusive container; this excludes emplace
// functions and functions taking an std::initializer_list<> as the container does
// not construct elements.
template <typename T, typename HookTraits>
class IntrusiveForwardList {
 public:
  typedef HookTraits hook_traits;
  typedef       T  value_type;
  typedef       T& reference;
  typedef const T& const_reference;
  typedef       T* pointer;
  typedef const T* const_pointer;
  typedef IntrusiveForwardListIterator<      T, hook_traits> iterator;
  typedef IntrusiveForwardListIterator<const T, hook_traits> const_iterator;

  // Construct/copy/destroy.
  IntrusiveForwardList() = default;
  template <typename InputIterator>
  IntrusiveForwardList(InputIterator first, InputIterator last) : IntrusiveForwardList() {
    insert_after(before_begin(), first, last);
  }
  IntrusiveForwardList(IntrusiveForwardList&& src) : first_(src.first_.next_hook) {
    src.first_.next_hook = nullptr;
  }
  IntrusiveForwardList& operator=(const IntrusiveForwardList& src) = delete;
  IntrusiveForwardList& operator=(IntrusiveForwardList&& src) {
    IntrusiveForwardList tmp(std::move(src));
    tmp.swap(*this);
    return *this;
  }
  ~IntrusiveForwardList() = default;

  // Iterators.
  iterator before_begin() { return iterator(&first_); }
  const_iterator before_begin() const { return const_iterator(&first_); }
  iterator begin() { return iterator(first_.next_hook); }
  const_iterator begin() const { return const_iterator(first_.next_hook); }
  iterator end() { return iterator(nullptr); }
  const_iterator end() const { return const_iterator(nullptr); }
  const_iterator cbefore_begin() const { return const_iterator(&first_); }
  const_iterator cbegin() const { return const_iterator(first_.next_hook); }
  const_iterator cend() const { return const_iterator(nullptr); }

  // Capacity.
  bool empty() const { return begin() == end(); }
  size_t max_size() { return static_cast<size_t>(-1); }

  // Element access.
  reference front() { return *begin(); }
  const_reference front() const { return *begin(); }

  // Modifiers.
  template <typename InputIterator>
  void assign(InputIterator first, InputIterator last) {
    IntrusiveForwardList tmp(first, last);
    tmp.swap(*this);
  }
  void push_front(value_type& value) {
    insert_after(before_begin(), value);
  }
  void pop_front() {
    DCHECK(!empty());
    erase_after(before_begin());
  }
  iterator insert_after(const_iterator position, value_type& value) {
    const IntrusiveForwardListHook* new_hook = hook_traits::GetHook(&value);
    new_hook->next_hook = position.hook_->next_hook;
    position.hook_->next_hook = new_hook;
    return iterator(new_hook);
  }
  template <typename InputIterator>
  iterator insert_after(const_iterator position, InputIterator first, InputIterator last) {
    while (first != last) {
      position = insert_after(position, *first++);
    }
    return iterator(position.hook_);
  }
  iterator erase_after(const_iterator position) {
    const_iterator last = position;
    std::advance(last, 2);
    return erase_after(position, last);
  }
  iterator erase_after(const_iterator position, const_iterator last) {
    DCHECK(position != last);
    position.hook_->next_hook = last.hook_;
    return iterator(last.hook_);
  }
  void swap(IntrusiveForwardList& other) {
    std::swap(first_.next_hook, other.first_.next_hook);
  }
  void clear() {
    first_.next_hook = nullptr;
  }

  // Operations.
  void splice_after(const_iterator position, IntrusiveForwardList& src) {
    DCHECK(position != end());
    splice_after(position, src, src.before_begin(), src.end());
  }
  void splice_after(const_iterator position, IntrusiveForwardList&& src) {
    splice_after(position, src);  // Use l-value overload.
  }
  // Splice the element after `i`.
  void splice_after(const_iterator position, IntrusiveForwardList& src, const_iterator i) {
    // The standard specifies that this version does nothing if `position == i`
    // or `position == ++i`. We must handle the latter here because the overload
    // `splice_after(position, src, first, last)` does not allow `position` inside
    // the range `(first, last)`.
    if (++const_iterator(i) == position) {
      return;
    }
    const_iterator last = i;
    std::advance(last, 2);
    splice_after(position, src, i, last);
  }
  // Splice the element after `i`.
  void splice_after(const_iterator position, IntrusiveForwardList&& src, const_iterator i) {
    splice_after(position, src, i);  // Use l-value overload.
  }
  // Splice elements between `first` and `last`, i.e. open range `(first, last)`.
  void splice_after(const_iterator position,
                    IntrusiveForwardList& src,
                    const_iterator first,
                    const_iterator last) {
    DCHECK(position != end());
    DCHECK(first != last);
    if (++const_iterator(first) == last) {
      // Nothing to do.
      return;
    }
    // If position is just before end() and last is src.end(), we can finish this quickly.
    if (++const_iterator(position) == end() && last == src.end()) {
      position.hook_->next_hook = first.hook_->next_hook;
      first.hook_->next_hook = nullptr;
      return;
    }
    // Otherwise we need to find the position before last to fix up the hook.
    const_iterator before_last = first;
    while (++const_iterator(before_last) != last) {
      ++before_last;
    }
    // Detach (first, last).
    const IntrusiveForwardListHook* first_taken = first.hook_->next_hook;
    first.hook_->next_hook = last.hook_;
    // Attach the sequence to the new position.
    before_last.hook_->next_hook = position.hook_->next_hook;
    position.hook_->next_hook = first_taken;
  }
  // Splice elements between `first` and `last`, i.e. open range `(first, last)`.
  void splice_after(const_iterator position,
                    IntrusiveForwardList&& src,
                    const_iterator first,
                    const_iterator last) {
    splice_after(position, src, first, last);  // Use l-value overload.
  }
  void remove(const value_type& value) {
    remove_if([value](const value_type& v) { return value == v; });
  }
  template <typename Predicate>
  void remove_if(Predicate pred) {
    iterator prev = before_begin();
    for (iterator current = begin(); current != end(); ++current) {
      if (pred(*current)) {
        erase_after(prev);
        current = prev;
      } else {
        prev = current;
      }
    }
  }
  void unique() {
    unique(std::equal_to<value_type>());
  }
  template <typename BinaryPredicate>
  void unique(BinaryPredicate pred) {
    if (!empty()) {
      iterator prev = begin();
      iterator current = prev;
      ++current;
      for (; current != end(); ++current) {
        if (pred(*prev, *current)) {
          erase_after(prev);
          current = prev;
        } else {
          prev = current;
        }
      }
    }
  }
  void merge(IntrusiveForwardList& other) {
    merge(other, std::less<value_type>());
  }
  void merge(IntrusiveForwardList&& other) {
    merge(other);  // Use l-value overload.
  }
  template <typename Compare>
  void merge(IntrusiveForwardList& other, Compare cmp) {
    iterator prev = before_begin();
    iterator current = begin();
    iterator other_prev = other.before_begin();
    iterator other_current = other.begin();
    while (current != end() && other_current != other.end()) {
      if (cmp(*other_current, *current)) {
        ++other_current;
        splice_after(prev, other, other_prev);
        ++prev;
      } else {
        prev = current;
        ++current;
      }
      DCHECK(++const_iterator(prev) == current);
      DCHECK(++const_iterator(other_prev) == other_current);
    }
    splice_after(prev, other);
  }
  template <typename Compare>
  void merge(IntrusiveForwardList&& other, Compare cmp) {
    merge(other, cmp);  // Use l-value overload.
  }
  void sort() {
    sort(std::less<value_type>());
  }
  template <typename Compare>
  void sort(Compare cmp) {
    size_t n = std::distance(begin(), end());
    if (n >= 2u) {
      const_iterator middle = before_begin();
      std::advance(middle, n / 2u);
      IntrusiveForwardList second_half;
      second_half.splice_after(second_half.before_begin(), *this, middle, end());
      sort(cmp);
      second_half.sort(cmp);
      merge(second_half, cmp);
    }
  }
  void reverse() {
    IntrusiveForwardList reversed;
    while (!empty()) {
      value_type& value = front();
      erase_after(before_begin());
      reversed.insert_after(reversed.before_begin(), value);
    }
    reversed.swap(*this);
  }

  // Extensions.
  bool HasExactlyOneElement() const {
    return !empty() && ++begin() == end();
  }
  size_t SizeSlow() const {
    return std::distance(begin(), end());
  }
  bool ContainsNode(const_reference node) const {
    for (auto&& n : *this) {
      if (std::addressof(n) == std::addressof(node)) {
        return true;
      }
    }
    return false;
  }

 private:
  static IntrusiveForwardListHook* ModifiableHook(const IntrusiveForwardListHook* hook) {
    return const_cast<IntrusiveForwardListHook*>(hook);
  }

  IntrusiveForwardListHook first_;
};

template <typename T, typename HookTraits>
void swap(IntrusiveForwardList<T, HookTraits>& lhs, IntrusiveForwardList<T, HookTraits>& rhs) {
  lhs.swap(rhs);
}

template <typename T, typename HookTraits>
bool operator==(const IntrusiveForwardList<T, HookTraits>& lhs,
                const IntrusiveForwardList<T, HookTraits>& rhs) {
  auto lit = lhs.begin();
  auto rit = rhs.begin();
  for (; lit != lhs.end() && rit != rhs.end(); ++lit, ++rit) {
    if (*lit != *rit) {
      return false;
    }
  }
  return lit == lhs.end() && rit == rhs.end();
}

template <typename T, typename HookTraits>
bool operator!=(const IntrusiveForwardList<T, HookTraits>& lhs,
                const IntrusiveForwardList<T, HookTraits>& rhs) {
  return !(lhs == rhs);
}

template <typename T, typename HookTraits>
bool operator<(const IntrusiveForwardList<T, HookTraits>& lhs,
               const IntrusiveForwardList<T, HookTraits>& rhs) {
  return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}

template <typename T, typename HookTraits>
bool operator>(const IntrusiveForwardList<T, HookTraits>& lhs,
               const IntrusiveForwardList<T, HookTraits>& rhs) {
  return rhs < lhs;
}

template <typename T, typename HookTraits>
bool operator<=(const IntrusiveForwardList<T, HookTraits>& lhs,
                const IntrusiveForwardList<T, HookTraits>& rhs) {
  return !(rhs < lhs);
}

template <typename T, typename HookTraits>
bool operator>=(const IntrusiveForwardList<T, HookTraits>& lhs,
                const IntrusiveForwardList<T, HookTraits>& rhs) {
  return !(lhs < rhs);
}

template <typename T, IntrusiveForwardListHook T::* NextPtr>
class IntrusiveForwardListMemberHookTraits {
 public:
  static const IntrusiveForwardListHook* GetHook(const T* value) {
    return &(value->*NextPtr);
  }

  static T* GetValue(const IntrusiveForwardListHook* hook) {
    return reinterpret_cast<T*>(
        reinterpret_cast<uintptr_t>(hook) - OFFSETOF_MEMBERPTR(T, NextPtr));
  }
};

template <typename T, typename Tag>
class IntrusiveForwardListBaseHookTraits {
 public:
  static const IntrusiveForwardListHook* GetHook(const T* value) {
    // Explicit conversion to the "node" followed by implicit conversion to the "hook".
    return static_cast<const IntrusiveForwardListNode<T, Tag>*>(value);
  }

  static T* GetValue(const IntrusiveForwardListHook* hook) {
    return down_cast<T*>(down_cast<IntrusiveForwardListNode<T, Tag>*>(
        const_cast<IntrusiveForwardListHook*>(hook)));
  }
};

}  // namespace art

#endif  // ART_LIBARTBASE_BASE_INTRUSIVE_FORWARD_LIST_H_
