/*
 *  subnet.cpp
 *
 *  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 "event.h"
#include "subnet.h"
#include "dictdatum.h"
#include "arraydatum.h"
#include "dictutils.h"
#include "network.h"
#include <string>

#ifdef N_DEBUG
#undef N_DEBUG
#endif

nest::Subnet::Subnet()
  :Node(),
   nodes_(),
   gids_(),
   children_on_same_vp_(false),
   children_vp_(0),
   label_(),
   customdict_(new Dictionary),
   homogeneous_(true),
   last_mid_(0)
{
  set(frozen);  // freeze subnet by default
}

nest::Subnet::Subnet(const Subnet &c)
  :Node(c),
   nodes_(c.nodes_),
   gids_(c.gids_),
   children_on_same_vp_(c.children_on_same_vp_),
   children_vp_(c.children_vp_),
   label_(c.label_),
   customdict_(new Dictionary(*(c.customdict_))),
   homogeneous_(c.homogeneous_),
   last_mid_(c.last_mid_)
{}

void nest::Subnet::set_status(const DictionaryDatum& dict)
{
  updateValue<std::string>(dict,"label",label_);
  updateValue<DictionaryDatum> (dict,"customdict",customdict_);

  bool children_on_same_vp;
  if (updateValue<bool>(dict,"children_on_same_vp", children_on_same_vp))
  {
    bool parent_children_on_same_vp = (get_gid() == 0) ? false : get_parent()->get_children_on_same_vp();
    if (parent_children_on_same_vp && !children_on_same_vp)
    {
      network()->message(SLIInterpreter::M_ERROR, "SetStatus", "Setting /children_on_same_vp to false is not possible,");
      network()->message(SLIInterpreter::M_ERROR, "SetStatus", "because it set to true in the parent subnet.");
    }
    else if (nodes_.size() > 0)
    {
      network()->message(SLIInterpreter::M_ERROR, "SetStatus", "Modifying /children_on_same_vp is not possible,");
      network()->message(SLIInterpreter::M_ERROR, "SetStatus", "because the subnet already contains nodes."); 
    }
    else
      children_on_same_vp_ = children_on_same_vp;
  }
}

void nest::Subnet::get_status(DictionaryDatum& dict) const
{
  (*dict)["number_of_children"]= global_size();
  (*dict)["label"]=label_;
  (*dict)["customdict"]=customdict_;
  (*dict)["children_on_same_vp"]=children_on_same_vp_; 

  (*dict)[names::type] = LiteralDatum(names::structure);
}

void nest::Subnet::get_dimensions_(std::vector<int> & dim) const
{
  dim.push_back(gids_.size());
  if(nodes_.empty())
    return;
  if(homogeneous_ && (dynamic_cast<Subnet *>(nodes_.at(0)) !=NULL))
    {
      bool homog=true;
      for(size_t i=0; i< nodes_.size()-1; ++i)
      {
    	Subnet *c1=dynamic_cast<Subnet *>(nodes_.at(i));
    	Subnet *c2=dynamic_cast<Subnet *>(nodes_.at(i+1));

	    if(c1->global_size() != c2->global_size())
	    {
	      homog=false;
	      continue;
	    }
	  }

      // If homog is true, all child-subnets have the same size
      // and we go one level deeper.
      if(homog)
	  {
	    Subnet *c=dynamic_cast<Subnet *>(nodes_.at(0));
	    c->get_dimensions_(dim);
	  }
    }
}


std::string nest::Subnet::print_network(int max_depth, int level, std::string prefix)
{
  // When the function is first called, we have to have a single
  // space as prefix, otherwise everything will by slightly out of
  // format.
  if(prefix == "")
    prefix=" ";

  std::ostringstream out;
  if(get_parent())
  {
    out << "+-[" << get_lid()+1 << "] ";
 
    if (get_label() != "")
      out << get_label();
    else
      out << get_name();
  }
  else
  {
    out << "+-" << "[0] ";
    if (get_label() != "")
      out << get_label();
    else
      out << "root";
  }

  std::vector<int> dim;
  get_dimensions_(dim);

  out << " dim=[";
  for(size_t k = 0; k < dim.size() - 1; ++k)
    out << dim[k] << " ";
  out << dim[dim.size() - 1] << "]" << std::endl;

  if(max_depth <= level)
    return out.str();

  if(nodes_.size()==0)
    return out.str();

  prefix += "  ";
  out << prefix << "|" << std::endl;

  size_t first=0;
  for(size_t i=0; i< nodes_.size(); ++i)
  {

    size_t next=i+1;
    if(nodes_[i]==NULL)
    {
      out << prefix << "+-NULL" << std::endl;
      // Print extra line, if we are at the end of a subnet.
      if(next==nodes_.size())
	out << prefix << std::endl;
      first=i+1;
      continue;
    }
    
    Subnet *c=dynamic_cast<Subnet *>(nodes_[i]);
    if(c !=NULL)
    {
      // this node is a subnet,
      // the sequence is printed, so
      // we print the children and move on
      // print subnet
      //
      // If the subnet is the last node of the parent subnet,
      // we must not print the continuation line '|', so we distinguish 
      // this case.
      if(next==nodes_.size())
	out << prefix << nodes_[i]->print_network(max_depth, level + 1, prefix+" ");
      else
	out << prefix << nodes_[i]->print_network(max_depth, level + 1, prefix+"|");

      first=next;
      continue;
    }
      
    // now we look one into the future
    // to determine whether this is a sequence
    // or not.
      
    if(next < nodes_.size())
    {
      // we have a successor
      if(nodes_[next]!=NULL)
      {
	// it is not NULL
	
	c=dynamic_cast<Subnet *>(nodes_[next]);
	if(c == NULL)
	{
	  // and not a subnet, so we skipp 
	  // the printout, until the end
	  // of the sequence is found.
	  if((nodes_[first]->get_name() == nodes_[next]->get_name()))
	  {
	    continue;
	  }
	} // if the next node is a compount we flush the sequence 
      } // if the next node is NULL, we flush the sequence
    } // if there is no next node, we flush the sequence
    
    if(first<i)
    {
      // Here we print the sequence of consecutive nodes.
      // We can be sure that neither first, nor i point to NULL.
      out << prefix << "+-[" << first+1 << "]...[" << i+1 << "] " 
	  << nodes_[first]->get_name() << std::endl;
    // Print extra line, if we are at the end of a subnet.
      if(next==nodes_.size())
	out << prefix << std::endl;
      first=next;
      continue;
    }
    
    // Here, we deal the case of an individual Node with no identical neighbours.

      out << prefix << "+-[" << i+1 << "] " 
	  << nodes_[first]->get_name() << std::endl;

    // Print extra line, if we are at the end of a subnet.
    if(next==nodes_.size())
       out << prefix << std::endl;
    first=next;
    
  }
  return out.str();
}

void nest::Subnet::set_label(std::string const l)
{
  // set the new label on all sibling threads
  for (thread t = 0; t < network()->get_num_threads(); ++t)
  {
    Node* n = network()->get_node(get_gid(), t);
    Subnet* c = dynamic_cast<Subnet*>(n);
    assert(c);
    c->label_ = l;
  }
}

bool nest::Subnet::allow_entry() const
{
  return true;
}