//===- HashIterator.h -----------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_ADT_HASHITERATOR_H
#define MCLD_ADT_HASHITERATOR_H

#include <cstddef>

namespace mcld {

/** \class ChainIteratorBase
 *  \brief ChaintIteratorBase follows the HashEntryTy with the same hash value.
 */
template<typename HashTableImplTy>
class ChainIteratorBase
{
public:
  typedef HashTableImplTy hash_table;
  typedef typename HashTableImplTy::key_type key_type;
  typedef typename HashTableImplTy::entry_type entry_type;
  typedef typename HashTableImplTy::bucket_type bucket_type;

public:
  ChainIteratorBase()
  : m_pHashTable(0), m_Index(0), m_HashValue(0), m_EndIndex(0)
  { }

  ChainIteratorBase(HashTableImplTy* pTable, const key_type& pKey)
  : m_pHashTable(pTable)
  {
    m_HashValue = pTable->hash()(pKey);
    m_EndIndex = m_Index = m_HashValue % m_pHashTable->m_NumOfBuckets;
    const unsigned int probe = 1;
    while(true) {
      bucket_type &bucket = m_pHashTable->m_Buckets[m_Index];
      if (bucket_type::getTombstone() == bucket.Entry) {
        // Ignore tombstones.
      }
      else if (m_HashValue == bucket.FullHashValue) {
        if (bucket.Entry->compare(pKey)) {
          m_EndIndex = m_Index;
          break;
        }
      }
      m_Index += probe;
      if (m_Index == m_pHashTable->m_NumOfBuckets)
        m_Index = 0;
      // doesn't exist
      if (m_EndIndex == m_Index) {
        reset();
        break;
      }
    }
  }

  ChainIteratorBase(const ChainIteratorBase& pCopy)
  : m_pHashTable(pCopy.m_pHashTable),
    m_Index(pCopy.m_Index),
    m_HashValue(pCopy.m_HashValue),
    m_EndIndex(pCopy.m_EndIndex)
  { }

  ChainIteratorBase& assign(const ChainIteratorBase& pCopy) {
    m_pHashTable = pCopy.m_pHashTable;
    m_Index = pCopy.m_Index;
    m_HashValue = pCopy.m_HashValue;
    m_EndIndex = pCopy.m_EndIndex;
    return *this;
  }

  inline bucket_type* getBucket() {
    if (0 == m_pHashTable)
      return 0;
    return &(m_pHashTable->m_Buckets[m_Index]);
  }

  inline const bucket_type* getBucket() const {
    if (0 == m_pHashTable)
      return 0;
    return &(m_pHashTable->m_Buckets[m_Index]);
  }

  inline entry_type* getEntry() {
    if (0 == m_pHashTable)
      return 0;
    return m_pHashTable->m_Buckets[m_Index].Entry;
  }

  inline const entry_type* getEntry() const {
    if (0 == m_pHashTable)
      return 0;
    return m_pHashTable->m_Buckets[m_Index].Entry;
  }

  inline void reset() {
    m_pHashTable = 0;
    m_Index = 0;
    m_EndIndex = 0;
    m_HashValue = 0;
  }

  inline void advance() {
    if (0 == m_pHashTable)
      return;
    const unsigned int probe = 1;
    while(true) {
      m_Index += probe;
      if (m_Index == m_pHashTable->m_NumOfBuckets)
        m_Index = 0;
      // reach end()
      if (m_Index == m_EndIndex) {
        reset();
        return;
      }

      bucket_type &bucket = m_pHashTable->m_Buckets[m_Index];

      if (bucket_type::getTombstone() == bucket.Entry ||
          bucket_type::getEmptyBucket() == bucket.Entry) {
        // Ignore tombstones.
      }
      else if (m_HashValue == bucket.FullHashValue) {
        return;
      }
    }
  }

  bool operator==(const ChainIteratorBase& pCopy) const {
    if (m_pHashTable == pCopy.m_pHashTable) {
      if (0 == m_pHashTable)
        return true;
      return ((m_HashValue == pCopy.m_HashValue) &&
              (m_EndIndex == pCopy.m_EndIndex) &&
              (m_Index == pCopy.m_Index));
    }
    return false;
  }

  bool operator!=(const ChainIteratorBase& pCopy) const
  { return !(*this == pCopy); }

private:
  HashTableImplTy* m_pHashTable;
  unsigned int m_Index;
  unsigned int m_HashValue;
  unsigned int m_EndIndex;
};

/** \class EntryIteratorBase
 *  \brief EntryIteratorBase walks over hash table by the natural layout of the
 *  buckets
 */
template<typename HashTableImplTy>
class EntryIteratorBase
{
public:
  typedef HashTableImplTy hash_table;
  typedef typename HashTableImplTy::key_type key_type;
  typedef typename HashTableImplTy::entry_type entry_type;
  typedef typename HashTableImplTy::bucket_type bucket_type;

public:
  EntryIteratorBase()
  : m_pHashTable(0), m_Index(0)
  { }

  EntryIteratorBase(HashTableImplTy* pTable,
                   unsigned int pIndex)
  : m_pHashTable(pTable), m_Index(pIndex)
  { }

  EntryIteratorBase(const EntryIteratorBase& pCopy)
  : m_pHashTable(pCopy.m_pHashTable), m_Index(pCopy.m_Index)
  { }

  EntryIteratorBase& assign(const EntryIteratorBase& pCopy) {
    m_pHashTable = pCopy.m_pHashTable;
    m_Index = pCopy.m_Index;
    return *this;
  }

  inline bucket_type* getBucket() {
    if (0 == m_pHashTable)
      return 0;
    return &(m_pHashTable->m_Buckets[m_Index]);
  }

  inline const bucket_type* getBucket() const {
    if (0 == m_pHashTable)
      return 0;
    return &(m_pHashTable->m_Buckets[m_Index]);
  }

  inline entry_type* getEntry() {
    if (0 == m_pHashTable)
      return 0;
    return m_pHashTable->m_Buckets[m_Index].Entry;
  }

  inline const entry_type* getEntry() const {
    if (0 == m_pHashTable)
      return 0;
    return m_pHashTable->m_Buckets[m_Index].Entry;
  }

  inline void reset() {
    m_pHashTable = 0;
    m_Index = 0;
  }

  inline void advance() {
    if (0 == m_pHashTable)
      return;
    do {
      ++m_Index;
      if (m_pHashTable->m_NumOfBuckets == m_Index) { // to the end
        reset();
        return;
      }
    } while(bucket_type::getEmptyBucket() == m_pHashTable->m_Buckets[m_Index].Entry ||
            bucket_type::getTombstone() == m_pHashTable->m_Buckets[m_Index].Entry);
  }

  bool operator==(const EntryIteratorBase& pCopy) const
  { return ((m_pHashTable == pCopy.m_pHashTable) &&
            (m_Index == pCopy.m_Index)); }

  bool operator!=(const EntryIteratorBase& pCopy) const
  { return !(*this == pCopy); }

private:
  HashTableImplTy* m_pHashTable;
  unsigned int m_Index;

};

/** \class HashIterator
 *  \brief HashIterator provides a policy-based iterator.
 *
 *  HashTable has two kinds of iterators. One is used to traverse buckets
 *  with the same hash value; the other is used to traverse all entries of the
 *  hash table.
 *
 *  HashIterator is a template policy-based iterator, which can change its
 *  behavior by change the template argument IteratorBase. HashTable defines
 *  above two iterators by defining HashIterators with different IteratorBase.
 */
template<typename IteratorBase,
         typename Traits>
class HashIterator : public IteratorBase
{
public:
  typedef Traits                     traits;
  typedef typename traits::pointer   pointer;
  typedef typename traits::reference reference;
  typedef size_t                     size_type;
  typedef ptrdiff_t                  difference_type;
  typedef IteratorBase               Base;

  typedef HashIterator<IteratorBase,
                       Traits>             Self;

  typedef typename traits::nonconst_traits nonconst_traits;
  typedef HashIterator<IteratorBase,
                       nonconst_traits>    iterator;

  typedef typename traits::const_traits    const_traits;
  typedef HashIterator<IteratorBase,
                       const_traits>       const_iterator;
  typedef std::forward_iterator_tag        iterator_category;

public:
  HashIterator()
  : IteratorBase()
  { }

  /// HashIterator - constructor for EntryIterator
  HashIterator(typename IteratorBase::hash_table* pTable, unsigned int pIndex)
  : IteratorBase(pTable, pIndex)
  { }

  /// HashIterator - constructor for ChainIterator
  explicit HashIterator(typename IteratorBase::hash_table* pTable,
                        const typename IteratorBase::key_type& pKey,
                        int)
  : IteratorBase(pTable, pKey)
  { }

  HashIterator(const HashIterator& pCopy)
  : IteratorBase(pCopy)
  { }

  ~HashIterator()
  { }

  HashIterator& operator=(const HashIterator& pCopy) {
    IteratorBase::assign(pCopy);
    return *this;
  }

  // -----  operators  ----- //
  Self& operator++() {
    this->Base::advance();
    return *this;
  }

  Self operator++(int) {
    Self tmp = *this;
    this->Base::advance();
    return tmp;
  }
};

} // namespace of mcld

#endif