/*
 *  dict.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST 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 of the License, or
 *  (at your option) any later version.
 *
 *  NEST 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef DICT_H
#define DICT_H
/* 
    SLI's dictionary class
*/
#include "name.h"
#include "token.h"

#include <map>
#include "sliexceptions.h"


typedef  std::map<Name,Token, std::less<Name> > TokenMap;

inline bool operator==(const TokenMap & x, const TokenMap &y)
{
  return (x.size() == y.size()) && equal(x.begin(), x.end(), y.begin());
}

/** A class that associates names and tokens.
 *  @ingroup TokenHandling
 */
class Dictionary :private TokenMap
{
  /**
   * Helper class for lexicographical sorting of dictionary entries.
   * Provides comparison operator for ascending, case-insensitive 
   * lexicographical ordering.
   * @see This is a simplified version of the class presented in 
   * N.M.Josuttis, The C++ Standard Library, Addison Wesley, 1999,
   * ch. 6.6.6.
   */    
  class DictItemLexicalOrder {
  private:
    static bool nocase_compare(char c1, char c2);

  public:
    bool operator() (const std::pair<Name, Token>& lhs, 
		     const std::pair<Name, Token>& rhs) const
      {
	const std::string& ls = lhs.first.toString();
	const std::string& rs = rhs.first.toString();

	return std::lexicographical_compare(ls.begin(), ls.end(),
					    rs.begin(), rs.end(),
					    nocase_compare);
      }
  };
  
public:
  Dictionary() {}
  Dictionary(const Dictionary &d) : TokenMap(d), refs_on_dictstack_(0) {}
  ~Dictionary();
  
  using TokenMap::erase;
  using TokenMap::size;
  using TokenMap::begin;
  using TokenMap::end;
  using TokenMap::iterator;
  using TokenMap::find;

  void clear();

  /**
   * Lookup and return Token with given name in dictionary.
   * If the name is not found, an empty token is returned. 
   * This version of lookup is deprecated and will disappear in future versions.
   * Please use lookup2() instead.
   * @note The token returned should @b always  be stored as a 
   *       <tt>const \&</tt>, so that the control flag for 
   *       dictionary read-out is set on the Token in the dictionary,
   *       not its copy.  
   */

  const Token & lookup(const Name &n) const;

  /**
   * lookup a name in the dictionary. If the name is not found an UndefinedName exception is thrown.
   * lookup2 is the preferred way to retrieve entries from the dictionary. 
   * @note The token returned should @b always  be stored as a 
   *       <tt>const \&</tt>, so that the control flag for 
   *       dictionary read-out is set on the Token in the dictionary,
   *       not its copy.  
   */
  const Token & lookup2(const Name &n) const; //throws UndefinedName
  bool known(const Name &) const;
  
  Token & insert(const Name &n, const Token &t);
  Token & insert_move(const Name &, Token &);

  //! Remove entry from dictionary
  void remove(const Name& n);  

  const Token& operator[](const Name&) const;
  Token& operator[](const Name &);
  const Token& operator[](const char*) const;
  Token& operator[](const char *);
  
  bool empty(void) const { return TokenMap::empty(); }
      
  void info(std::ostream &) const;
  
  bool operator==(const Dictionary &d) const { return ::operator==(*this, d); }
  
  /**
   * Add the contents of this dictionary to another.
   * The target dictionary is given by names and must be retrieved via
   * the interpreter.
   * @todo Allow for free formatting of target dictionary entries 
   * via functor, and add traits to allow duplicates.
   * @see remove_dict
   */
  void add_dict(const std::string&, SLIInterpreter&);

  /**
   * Remove entries found in another dictionary from this.
   * @see add_dict
   */
  void remove_dict(const std::string&, SLIInterpreter&);

  /**
   * Clear access flags on all dictionary elements.
   * Flags for nested dictionaries are cleared recursively.
   * @see all_accessed()
   */
  void clear_access_flags();
  
  /**
   * Check whether all elements have been accessed.
   * Checks nested dictionaries recursively.
   * @param std::string& contains string with names of non-accessed entries
   * @returns true if all dictionary elements have been accessed
   * @note this is just a wrapper, all_accessed_() does the work, hides recursion
   * @see clear_access_flags(), all_accessed_()
   */
  bool all_accessed(std::string& missed) const { return all_accessed_(missed); }
	 
  friend std::ostream & operator<<(std::ostream &, const Dictionary &);
  
  /** 
   * Constant iterator for dictionary.
   * Dictionary inherits privately from std::map to hide implementation
   * details. To allow for inspection of all elements in a dictionary,
   * we export the constant iterator type and begin() and end() methods.
   */  
  typedef TokenMap::const_iterator const_iterator;
  
  /** 
   * First element in dictionary.
   * Dictionary inherits privately from std::map to hide implementation
   * details. To allow for inspection of all elements in a dictionary,
   * we export the constant iterator type and begin() and end() methods.
   */  
  //  const_iterator begin() const;

  /** 
   * One-past-last element in dictionary.
   * Dictionary inherits privately from std::map to hide implementation
   * details. To allow for inspection of all elements in a dictionary,
   * we export the constant iterator type and begin() and end() methods.
   */  
  //const_iterator end() const;

  /**
   *
   */
  void initialize_property_array(Name propname);
  
  /**
   * This function is called when a dictionary is pushed to the dictionary stack.
   * The dictioray stack must keep track about which dictioraries are on the dictionary stack. 
   * If a dictionary is modified and it is on the dictionary stack, the cache of the dictionary stack must
   * be adjusted. This is e.g. the case for the systemdict or the errordict.
   */ 
  void add_dictstack_reference()
  {
    ++refs_on_dictstack_;
  }

  /**
   * This function is called when the dictionary is popped from the dictionary stack.
   */
  void remove_dictstack_reference()
  {
    --refs_on_dictstack_;
  }

  /**
   * Returns true, if the dictionary has references on the dictionary stack.
   */
  bool is_on_dictstack() const
  {
    return refs_on_dictstack_ >0;
  }

  
 private:
  /**
   * Worker function checking whether all elements have been accessed.
   * Checks nested dictionaries recursively.
   * @param std::string& contains string with names of non-accessed entries
   * @param std::string prefix for nested dictionary entries, built during recursion
   * @returns true if all dictionary elements have been accessed
   * @note this is just the worker for all_accessed()
   * @see clear_access_flags(), all_accessed()
   */
 
  int refs_on_dictstack_; 
  bool all_accessed_(std::string&, std::string prefix = std::string()) const;
  static const Token VoidToken;
};

inline
const Token& Dictionary::lookup(const Name &n) const
{
  TokenMap::const_iterator where = find(n);
  if(where != end())
    return (*where).second;
  else
    return Dictionary::VoidToken;
}

inline
const Token& Dictionary::lookup2(const Name &n) const
{      
  TokenMap::const_iterator where = find(n);
  if(where != end())
    return (*where).second;
  else
    throw UndefinedName(n.toString());
}

inline
bool Dictionary::known(const Name &n) const
{
  TokenMap::const_iterator where = find(n);
  if(where != end())
    return true;
  else
    return false;
}

inline
Token& Dictionary::insert(const Name &n, const Token &t)  
{ 
  return TokenMap::operator[](n) = t; 
}


inline
const Token& Dictionary::operator[](const Name &n) const
{      
  TokenMap::const_iterator where = find(n);
  if(where != end())
    return (*where).second;
  else
    throw UndefinedName(n.toString());
}


inline
Token& Dictionary::operator[](const Name &n)
{  
  return TokenMap::operator[](n);
}

inline
Token& Dictionary::insert_move(const Name &n, Token &t)
{
  Token &result=TokenMap::operator[](n);
  result.move(t);
  return result;
}


#endif