/* Copyright (c) 1997-2015
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.polymake.org

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
--------------------------------------------------------------------------------
*/

#ifndef POLYMAKE_HASH_MAP_
#define POLYMAKE_HASH_MAP_

#if defined(__cplusplus) && __cplusplus >= 201103L && !defined(PM_FORCE_TR1)
#include <unordered_map>
#define PmTr1NS std
#else
#include <tr1/unordered_map>
#define PmTr1NS std::tr1
#endif

#include "polymake/internal/assoc.h"
#include "polymake/internal/hash_iterators.h"

namespace pm {

template <typename Key, typename Value, typename Params=void>
class hash_map
   : public PmTr1NS::unordered_map<Key, Value, hash_func<Key>, typename hash_table_cmp_adapter<Key,Params>::type> {
   typedef PmTr1NS::unordered_map<Key, Value, hash_func<Key>, typename hash_table_cmp_adapter<Key,Params>::type> _super;

   typedef typename extract_type_param<Params, DefaultValue, operations::clear<Value> >::type default_value_supplier;
   default_value_supplier dflt;

public:
   hash_map() {}
   explicit hash_map(size_t start_cap) : _super(start_cap) {}

   explicit hash_map(const default_value_supplier& dflt_arg) : dflt(dflt_arg) {}

   hash_map(const default_value_supplier& dflt_arg, size_t start_cap) : _super(start_cap), dflt(dflt_arg) {}

   template <typename Iterator>
   hash_map(Iterator first, Iterator last) : _super(first,last) {}

   template <typename Iterator>
   hash_map(Iterator first, Iterator last, const default_value_supplier& dflt_arg) : _super(first,last), dflt(dflt_arg) {}

   bool exists(typename function_argument<Key>::type k) const
   {
      return _super::find(k) != _super::end();
   }

   typename _super::iterator
   insert(typename function_argument<Key>::type k)
   {
      return _super::insert(typename _super::value_type(k, dflt())).first;
   }
#if 0
   template <typename Operation>
   typename _super::iterator
   insert(typename function_argument<Key>::type k, const Operation& op, typename disable_if<void**, identical<Operation,Value>::value>::type=0)
   {
      std::pair<typename _super::iterator,bool> ret=_super::insert(typename _super::value_type(k, dflt()));
      if (!ret.second) op(ret.first->second);
      return ret.first;
   }
#endif
   typename _super::iterator
   insert(typename function_argument<Key>::type k, typename function_argument<Value>::type v)
   {
      std::pair<typename _super::iterator,bool> ret=_super::insert(typename _super::value_type(k,v));
      if (!ret.second) ret.first->second=v;
      return ret.first;
   }
#if 0
   template <typename Operation>
   typename _super::iterator
   insert(typename function_argument<Key>::type k, typename function_argument<Value>::type v, const Operation& op)
   {
      std::pair<typename _super::iterator,bool> ret=_super::insert(typename _super::value_type(k,v));
      if (!ret.second) op(ret.first->second,v);
      return ret.first;
   }
#endif

   std::pair<typename _super::iterator,bool>
   find_or_insert(typename function_argument<Key>::type k)
   {
      return _super::insert(typename _super::value_type(k, dflt()));
   }

   std::pair<typename _super::iterator,bool>
   insert(const typename _super::value_type& p) { return _super::insert(p); }

   template <typename Iterator>
   void insert(Iterator first, Iterator last, typename disable_if<void**, identical<Iterator,Key>::value && identical<Iterator,Value>::value>::type=0)
   {
      _super::insert(first,last);
   }

   bool operator== (const hash_map& other) const
   {
      if (this->size() != other.size()) return false;
      typename _super::const_iterator not_found=this->end();
      for (typename _super::const_iterator elem=other.begin(), other_end=other.end(); elem != other_end; ++elem) {
         typename _super::const_iterator my=this->find(elem->first);
         if (my==not_found || my->second != elem->second)
            return false;
      }
      return true;
   }

   bool operator!= (const hash_map& other) const { return !operator==(other); }

   class filler {
   public:
      filler(hash_map& me_arg) : me(me_arg) {};

      void operator() (const typename _super::value_type& p) const { me.insert(p); }
      void operator() (typename function_argument<Key>::type k, typename function_argument<Value>::type v) const { me.insert(k,v); }
   private:
      hash_map& me;
   };

   filler make_filler() { return filler(*this); }
};

template <typename Key, typename Value, typename Params>
struct spec_object_traits< hash_map<Key,Value,Params> >
   : spec_object_traits<is_container> {
   static const IO_separator_kind IO_separator=IO_sep_inherit;
};

// FIXME: soon deprecated
template <typename Key, typename Value, typename HashFcn=hash_func<Key>, typename EqualKey=std::equal_to<Key> >
class hash_map_as_property_map
   : public PmTr1NS::unordered_map<Key, Value, HashFcn, EqualKey> {
   typedef PmTr1NS::unordered_map<Key, Value, HashFcn, EqualKey> _super;
public:
   hash_map_as_property_map() {}

   explicit hash_map_as_property_map(size_t start_cap)
      : _super(start_cap) {}

   Value& operator() (const Key& k)
   {
      return (_super::insert(typename _super::value_type(k, def_val))).first->second;
   }

   const Value& operator() (const Key& k) const
   {
      typename _super::const_iterator ii=this->find(k);
      return ii==this->end() ? def_val : ii->second;
   }

   std::pair<typename _super::iterator, bool>
   insert(const Key& k, typename function_argument<Value>::type v)
   {
      return _super::insert(typename _super::value_type(k, v));
   }

   void set_default_value(typename function_argument<Value>::type default_value)
   {
      def_val=default_value;
   }
protected:
   Value def_val;
};

} // end namespace pm

namespace polymake {
   using pm::hash_map;
}

#endif // POLYMAKE_HASH_MAP_

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
