/*
 *  dict.cc
 *
 *  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/>.
 *
 */

#include "dict.h"
#include "dictdatum.h"
#include "dictutils.h"
#include "sliexceptions.h"
#include <iomanip>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <string>

const Token Dictionary::VoidToken;

Dictionary::~Dictionary()
{
}

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

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


void Dictionary::clear()
{
// First, clear all contained dictionaries
    for(TokenMap::iterator i = TokenMap::begin(); i != end(); ++i)
    {
      DictionaryDatum *d=dynamic_cast<DictionaryDatum*>((*i).second.datum());
      if(d !=0)
      {
	if(d->get()!= this)
	  (*d)->clear();
	d->unlock();
      }
      (*i).second.clear();
    }

    // now clear dictionary itself; HEP 2004-09-08
    TokenMap::clear();
}
    
void Dictionary::info(std::ostream &out) const
{
  out.setf(std::ios::left);
  if(size()>0)
  {
    // copy to vector and sort
    typedef std::vector<std::pair<Name, Token> >DataVec;
    DataVec data;
    std::copy(begin(), end(), std::inserter(data, data.begin()));
    std::sort(data.begin(), data.end(), DictItemLexicalOrder());

    out << "--------------------------------------------------" << std::endl;
    out << std::setw(25) <<  "Name" << std::setw(20) << "Type" <<  "Value" << std::endl;
    out << "--------------------------------------------------" << std::endl;
      
    for(DataVec::const_iterator where = data.begin() ; 
	where != data.end() ; ++ where)
    {
      out  << std::setw(25) << where->first 
	   << std::setw(20) << where->second->gettypename()
	   << where->second
	   << std::endl;
    }
    out << "--------------------------------------------------" << std::endl;
  }
  out << "Total number of entries: "<< size() << std::endl;

  out.unsetf(std::ios::left);

}

void Dictionary::add_dict(const std::string& target, 
			  SLIInterpreter& i)
{
  DictionaryDatum targetdict;

    // retrieve targetdict from interpreter
    Token d = i.baselookup(Name(target));
    targetdict = getValue<DictionaryDatum>(d);

  for ( TokenMap::const_iterator it = TokenMap::begin() ; 
	it != TokenMap::end() ; ++it )
    if ( !targetdict->known(it->first) )
      targetdict->insert(it->first, it->second);
    else
      {
	throw UndefinedName((it->first).toString());
	//      throw DictError();
      }
}

void Dictionary::remove(const Name& n)
{
  TokenMap::iterator it = find(n);
  if ( it != end() )
    erase(it);
}

void Dictionary::remove_dict(const std::string& target, 
			     SLIInterpreter& i)
{
  DictionaryDatum targetdict;

  // retrieve targetdict from interpreter
  Token d = i.baselookup(Name(target));
  targetdict = getValue<DictionaryDatum>(d);

  for ( TokenMap::const_iterator it = TokenMap::begin() ; 
	it != TokenMap::end() ; ++it )
  {
    TokenMap::iterator tgt_it = targetdict->find(it->first); 
    if ( tgt_it != targetdict->end() )
      targetdict->erase(tgt_it);
  }
}

void Dictionary::clear_access_flags()
{
  for ( TokenMap::iterator it = TokenMap::begin() ; 
	it != TokenMap::end() ; ++it )
  {
    /* 
       Clear flags in nested dictionaries recursively.
       We first test whether the token is a DictionaryDatum
       and then call getValue(). This entails two dynamic casts,
       but is likely more efficient than a try-catch construction.
    */
    if ( it->second.is_a<DictionaryDatum>() )
    {
      DictionaryDatum subdict = getValue<DictionaryDatum>(it->second);
      subdict->clear_access_flags();
    }

    // in recursion, getValue sets the access flag for it->second, so
    // we must clear it after recursion is done
    it->second.clear_access_flag();
  }
}

bool Dictionary::all_accessed_(std::string& missed, std::string prefix) const
{
  missed = "";
  
  // build list of all non-accessed Token names
  for ( TokenMap::const_iterator it = TokenMap::begin() ; 
	it != TokenMap::end() ; ++it )
  {
    if ( !it->second.accessed() )	
	missed = missed + " " + prefix + it->first.toString();
    else if ( it->second.is_a<DictionaryDatum>() )
    {
      // recursively check if nested dictionary content was accessed
      // see also comments in clear_access_flags()

      // this sets access flag on it->second, but that does not matter,
      // since it is anyways set, otherwise we would not be recursing
      DictionaryDatum subdict = getValue<DictionaryDatum>(it->second);

      subdict->all_accessed_(missed, prefix + it->first.toString() + "::"); 
    }
  }

  return missed.empty();
}


std::ostream & operator<<(std::ostream &out, const Dictionary &d)
{
    out << "<<";
        
    for(TokenMap::const_iterator where = d.begin(); where != d.end(); where ++)
    {
        out << (*where).first << ' ' 
	    << (*where).second << ',';
    }
    out << ">>";
    
    return out;
    
}

bool Dictionary::DictItemLexicalOrder::nocase_compare(char c1, char c2)
{
  return std::toupper(c1) < std::toupper(c2);
}