/*
 *  node.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 "node.h"
#include "exceptions.h"
#include "event.h"
#include "network.h"
#include "namedatum.h"
#include "arraydatum.h"
#include "dictutils.h"

namespace nest {

  Network *Node::net_=NULL;

  Node::Node()
      :gid_(0),
       lid_(0),
       model_id_(-1),
       parent_(0),
       stat_(),
       thread_(0),
       vp_(invalid_thread_)
  {
    /**
     *
     * the scheduler starts with update_reference()==true,
     * thus we must reset the updated flag, so that
     * is_updated() will return false at the beginning of
     * each time-slice.
     */
    stat_.reset(updated); //!< Set to opposite of the scheduler's value
  }

  Node::Node(const Node &n)
      :gid_(0),
       lid_(0),
       model_id_(n.model_id_),
       parent_(n.parent_),
       stat_(n.stat_),  // copied from model prototype, frozen may be set
       thread_(n.thread_),
       vp_(n.vp_)
  {
  }

  Node::~Node()
  {}

  void Node::init_state()
  {
    Model const * const model= net_->get_model(model_id_);
    assert(model);
    init_state_(model->get_prototype());
  }
  
  void Node::init_buffers()
  {
    if ( stat_.test(buffers_initialized) )
      return;
      
    init_buffers_();
    stat_.set(buffers_initialized);
  }
  
  std::string Node::get_name() const
  {
    if(net_==0 || model_id_<0)
      return std::string("UnknownNode");

    return net_->get_model(model_id_)->get_name();
  }

  Model & Node::get_model_() const
  {
    if(net_==0 || model_id_<0)
      throw UnknownModelID(model_id_);
    
    return *net_->get_model(model_id_);
  }      

  bool Node::is_updated() const
  {
    return stat_.test(updated)==net_->update_reference();
  }

  bool Node::is_local() const
  {
    return !is_proxy();
  }

  DictionaryDatum Node::get_status_dict_()
  {
    return DictionaryDatum(new Dictionary);
  }  

  DictionaryDatum Node::get_status_base()
  {
    DictionaryDatum dict = get_status_dict_();

    assert(dict.valid());

    // add information available for all nodes
    (*dict)[names::local] = is_local();
    (*dict)[names::model] = LiteralDatum(get_name());

    // add information available only for local nodes
    if ( is_local() )
    {
      (*dict)[names::global_id] = get_gid();
      (*dict)[names::state] = get_status_flag();
      (*dict)[names::frozen] = is_frozen();
      (*dict)[names::thread] = get_thread();
      (*dict)[names::vp] = get_vp();
      if ( parent_ )
      {
        (*dict)[names::parent] = parent_->get_gid();

        // LIDs are only sensible for nodes with parents.
        // Add 1 as we count lids internally from 0, but from
        // 1 in the user interface.
        (*dict)[names::local_id] = get_lid() + 1;
      }
    }

    // This is overwritten with a corresponding value in the
    // base classes for stimulating and recording devices, and
    // in other special node classes
    (*dict)[names::type] = LiteralDatum(names::neuron);

    // now call the child class' hook
    get_status(dict);

    assert(dict.valid());
    return dict;
  }

  void Node::set_status_base(const DictionaryDatum &dict)
  {
    assert(dict.valid());

    // We call the child's set_status first, so that the Node remains
    // unchanged if the child should throw an exception.
    set_status(dict);

    if(dict->known(names::frozen))
    {
      bool frozen_val=(*dict)[names::frozen];

      if( frozen_val == true )
	set(frozen);
      else
	unset(frozen);
    }
  }

  /**
   * Default implementation of just throws UnexpectedEvent
   */
  port Node::check_connection(Connection&, port)
  {
    throw UnexpectedEvent();
    return invalid_port_;
  }

  /**
   * Default implementation of register_stdp_connection() just 
   * throws IllegalConnection
   */
  void Node::register_stdp_connection(double_t)
  {
    throw IllegalConnection();
  }

  /**
   * Default implementation of unregister_stdp_connection() just 
   * throws IllegalConnection
   */
  void Node::unregister_stdp_connection(double_t)
  {
    throw IllegalConnection();
  }

  /**
   * Default implementation of event handlers just throws
   * an UnexpectedEvent exception.
   * @see class UnexpectedEvent
   * @throws UnexpectedEvent  This is the default event to throw.
   */
  void Node::handle(SpikeEvent&)
  {
    throw UnexpectedEvent();
  }

  port Node::connect_sender(SpikeEvent&, port)
  {
    throw IllegalConnection();
    return invalid_port_;
  }

  void Node::handle(RateEvent&)
  {
    throw UnexpectedEvent();
  }

  port Node::connect_sender(RateEvent&, port)
  {
    throw IllegalConnection();
    return invalid_port_;
  }

  void Node::handle(CurrentEvent&)
  {
    throw UnexpectedEvent();
  }

  port Node::connect_sender(CurrentEvent&, port)
  {
    throw IllegalConnection();
    return invalid_port_;
  }

  void Node::handle(DataLoggingRequest&)
  {
    throw UnexpectedEvent();
  }

  port Node::connect_sender(DataLoggingRequest&, port)
  {
    throw IllegalConnection();
    return invalid_port_;
  }

  void Node::handle(DataLoggingReply&)
  {
    throw UnexpectedEvent();
  }

  void Node::handle(ConductanceEvent&)
  {
    throw UnexpectedEvent();
  }

  port Node::connect_sender(ConductanceEvent&, port)
  {
    throw IllegalConnection();
    return invalid_port_;
  }

  void Node::handle(DoubleDataEvent&)
  {
    throw UnexpectedEvent();
  }

  port Node::connect_sender(DoubleDataEvent&, port)
  {
    throw IllegalConnection();
    return invalid_port_;
  }

  double_t Node::get_K_value(double_t)
  {
    throw UnexpectedEvent();
    return 0;
  }


  void Node::get_K_values(double_t, double_t&, double_t&)
  {
    throw UnexpectedEvent();
  }

  void nest::Node::get_history(double_t, double_t,
			       std::deque<histentry>::iterator*,
			       std::deque<histentry>::iterator*)
  {
    throw UnexpectedEvent();
  }


  void Node::event_hook(DSSpikeEvent& e)
  {
    e.get_receiver().handle(e);
  }

  void Node::event_hook(DSCurrentEvent& e)
  {
    e.get_receiver().handle(e);
  }

  bool Node::allow_entry() const
  {
    return false;
  }

} // namespace