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

#include <vector>
#include <list>

namespace mcld {

/** \class KeyEntryMap
 *  \brief KeyEntryMap is a <const KeyType*, ENTRY*> map.
 */
template<typename KEY, typename ENTRY>
class KeyEntryMap
{
public:
  typedef KEY   KeyType;
  typedef ENTRY EntryType;

private:
  struct EntryPair {
    EntryPair(EntryType* pEntry1, EntryType* pEntry2)
     : entry1(pEntry1), entry2(pEntry2)
    {}

    EntryType* entry1;
    EntryType* entry2;
  };

  /// EntryOrPair - A key may mapping to a signal entry or a pair of entries,
  /// user is responsible for the type of Mapping.entry
  union EntryOrPair {
    EntryType* entry_ptr;
    EntryPair* pair_ptr;
  };

  struct Mapping {
    const KeyType* key;
    EntryOrPair entry;
  };

  typedef std::vector<Mapping> KeyEntryPool;
  typedef std::list<EntryPair> PairListType;

public:
  typedef typename KeyEntryPool::iterator iterator;
  typedef typename KeyEntryPool::const_iterator const_iterator;

public:
  /// lookUp - look up the entry mapping to pKey
  const EntryType* lookUp(const KeyType& pKey) const;
  EntryType*       lookUp(const KeyType& pKey);

  /// lookUpFirstEntry - look up the first entry mapping to pKey
  const EntryType* lookUpFirstEntry(const KeyType& pKey) const;
  EntryType*       lookUpFirstEntry(const KeyType& pKey);

  /// lookUpSecondEntry - look up the second entry mapping to pKey
  const EntryType* lookUpSecondEntry(const KeyType& pKey) const;
  EntryType*       lookUpSecondEntry(const KeyType& pKey);

  void record(const KeyType& pKey, EntryType& pEntry);
  void record(const KeyType& pKey,
              EntryType& pEntry1,
              EntryType& pEntry2);

  bool   empty() const { return m_Pool.empty(); }
  size_t size () const { return m_Pool.size(); }

  const_iterator begin() const { return m_Pool.begin(); }
  iterator       begin()       { return m_Pool.begin(); }
  const_iterator end  () const { return m_Pool.end();   }
  iterator       end  ()       { return m_Pool.end();   }

  void reserve(size_t pSize) { m_Pool.reserve(pSize); }

private:
  KeyEntryPool m_Pool;

  /// m_Pairs - the EntryPairs
  PairListType m_Pairs;
};

template<typename KeyType, typename EntryType>
const EntryType*
KeyEntryMap<KeyType, EntryType>::lookUp(const KeyType& pKey) const
{
  const_iterator mapping, mEnd = m_Pool.end();
  for (mapping = m_Pool.begin(); mapping != mEnd; ++mapping) {
    if (mapping->key == &pKey) {
      return mapping->entry.entry_ptr;
    }
  }

  return NULL;
}

template<typename KeyType, typename EntryType>
EntryType*
KeyEntryMap<KeyType, EntryType>::lookUp(const KeyType& pKey)
{
  iterator mapping, mEnd = m_Pool.end();
  for (mapping = m_Pool.begin(); mapping != mEnd; ++mapping) {
    if (mapping->key == &pKey) {
      return mapping->entry.entry_ptr;
    }
  }

  return NULL;
}

template<typename KeyType, typename EntryType>
const EntryType*
KeyEntryMap<KeyType, EntryType>::lookUpFirstEntry(const KeyType& pKey) const
{
  const_iterator mapping, mEnd = m_Pool.end();
  for (mapping = m_Pool.begin(); mapping != mEnd; ++mapping) {
    if (mapping->key == &pKey) {
      return mapping->entry.pair_ptr->entry1;
    }
  }

  return NULL;
}

template<typename KeyType, typename EntryType>
EntryType*
KeyEntryMap<KeyType, EntryType>::lookUpFirstEntry(const KeyType& pKey)
{
  const_iterator mapping, mEnd = m_Pool.end();
  for (mapping = m_Pool.begin(); mapping != mEnd; ++mapping) {
    if (mapping->key == &pKey) {
      return mapping->entry.pair_ptr->entry1;
    }
  }

  return NULL;
}

template<typename KeyType, typename EntryType>
const EntryType*
KeyEntryMap<KeyType, EntryType>::lookUpSecondEntry(const KeyType& pKey) const
{
  const_iterator mapping, mEnd = m_Pool.end();
  for (mapping = m_Pool.begin(); mapping != mEnd; ++mapping) {
    if (mapping->key == &pKey) {
      return mapping->entry.pair_ptr->entry2;
    }
  }

  return NULL;
}

template<typename KeyType, typename EntryType>
EntryType*
KeyEntryMap<KeyType, EntryType>::lookUpSecondEntry(const KeyType& pKey)
{
  const_iterator mapping, mEnd = m_Pool.end();
  for (mapping = m_Pool.begin(); mapping != mEnd; ++mapping) {
    if (mapping->key == &pKey) {
      return mapping->entry.pair_ptr->entry2;
    }
  }

  return NULL;
}

template<typename KeyType, typename EntryType>
void
KeyEntryMap<KeyType, EntryType>::record(const KeyType& pKey, EntryType& pEntry)
{
  Mapping mapping;
  mapping.key = &pKey;
  mapping.entry.entry_ptr = &pEntry;
  m_Pool.push_back(mapping);
}

template<typename KeyType, typename EntryType>
void
KeyEntryMap<KeyType, EntryType>::record(const KeyType& pKey,
                                  EntryType& pEntry1,
                                  EntryType& pEntry2)
{
  Mapping mapping;
  mapping.key = &pKey;
  m_Pairs.push_back(EntryPair(&pEntry1, &pEntry2));
  mapping.entry.pair_ptr = &m_Pairs.back();
  m_Pool.push_back(mapping);
}

} // namespace of mcld

#endif