//===- UniqueGCFactory.h --------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_UNIQUE_GCFACTORY_H
#define MCLD_UNIQUE_GCFACTORY_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif

#include "mcld/Support/GCFactory.h"
#include <map>
#include <utility>

namespace mcld
{

/** \class UniqueGCFactoryBase
 *  \brief UniqueGCFactories are unique associative factories, meaning that
 *  no two elements have the same key.
 */
template<typename KeyType, typename DataType, size_t ChunkSize>
class UniqueGCFactoryBase : public GCFactoryBase<LinearAllocator<DataType, ChunkSize> >
{
protected:
  typedef GCFactoryBase<LinearAllocator<DataType, ChunkSize> > Alloc;
  typedef std::map<KeyType, DataType*> KeyMap;

protected:
  UniqueGCFactoryBase()
  : GCFactoryBase<LinearAllocator<DataType, ChunkSize> >()
  { }

  UniqueGCFactoryBase(size_t pNum)
  : GCFactoryBase<LinearAllocator<DataType, ChunkSize> >(pNum)
  { }

public:
  virtual ~UniqueGCFactoryBase()
  { f_KeyMap.clear(); }

  DataType* find(const KeyType& pKey) {
    typename KeyMap::iterator dataIter = f_KeyMap.find(pKey);
    if (dataIter != f_KeyMap.end())
      return dataIter->second;
    return 0;
  }

  const DataType* find(const KeyType& pKey) const {
    typename KeyMap::const_iterator dataIter = f_KeyMap.find(pKey);
    if (dataIter != f_KeyMap.end())
      return dataIter->second;
    return 0;
  }

  DataType* produce(const KeyType& pKey, bool& pExist) {
    typename KeyMap::iterator dataIter = f_KeyMap.find(pKey);
    if (dataIter != f_KeyMap.end()) {
      pExist = true;
      return dataIter->second;
    }
    DataType* data = Alloc::allocate();
    construct(data);
    f_KeyMap.insert(std::make_pair(pKey, data));
    pExist = false;
    return data;
  }

  DataType* produce(const KeyType& pKey, const DataType& pValue, bool& pExist) {
    typename KeyMap::iterator dataIter = f_KeyMap.find(pKey);
    if (dataIter != f_KeyMap.end()) {
      pExist = true;
      return dataIter->second;
    }
    DataType* data = Alloc::allocate();
    construct(data, pValue);
    f_KeyMap.insert(std::make_pair(pKey, data));
    pExist = false;
    return data;
  }

protected:
  KeyMap f_KeyMap;

};

} // namespace of mcld

#endif