/*
 *  model.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 MODEL_H
#define MODEL_H
#include <string>

#include <new>
#include <vector>
#include "node.h"
#include "allocator.h"
#include "dictutils.h"

namespace nest
{

  /**
   * Base class for all Models.
   * Each Node class is associated with a corresponding Model
   * class. The Model class is responsible for the creation and class
   * wide parametrisation of its associated Node objects.
   *
   * class Model manages the thread-sorted memory pool of the model.
   * The default constructor uses one thread as default. Use set_threads() to
   * use more than one thread.
   * @ingroup user_interface
   * @see Node
   */
  class Model
  {
  public:

      Model(const std::string& name);
      Model(const Model& m):
      name_(m.name_),
      type_id_(m.type_id_),
      memory_(m.memory_)
	  {}
    
      virtual ~Model(){}

    /**
     * Create clone with new name.
     */
    virtual Model* clone(const std::string&) const =0;

    /**
     * Set number of threads based on number set in network.
     * As long as no nodes of the model have been allocated, the number
     * of threads may be changed.
     * @note Requires that network pointer in NestModule is initialized.
     */
    void set_threads();

    /**
     * Allocate new Node and return its pointer.
     * allocate() is not const, because it
     * is allowed to modify the Model object for
     * 'administrative' purposes.
     */
    Node* allocate(thread t);

    void  free(thread t, Node *);

    /**
     * Deletes all nodes which belong to this model.
     */

    void clear();

    /**
     * Reserve memory for at least n new Nodes.
     * A number of memory managers work more efficently, if they have
     * an idea about the number of Nodes to be allocated.
     * This function prepares the memory manager for the subsequent
     * allocation of n Nodes.
     * @param t Thread for which the Nodes are reserved.
     * @param n Number of Nodes to be allocated.
     * @note Due to the semantics of sli::pool::reserve(), this reserve()
     * ensures that there is space for at least @b additional @b nodes. 
     * This is different from the semantics of reserve() for STL containers.
     * @todo Adapt the semantics of reserve() to STL semantics.
     */
    void  reserve(thread t, size_t n);

    /**
     * Return name of the Model.
     * This function returns the name of the Model as C++ string. The
     * name is defined by the constructor. The result is identical to the value
     * of Node::get_name();
     * @see Model::Model()
     * @see Node::get_name()
     */
    std::string get_name() const;

    /**
     * Return the available memory. The result is given in number of elements,
     * not in bytes.
     * Note that this function reports a sum over all threads.
     */
    size_t mem_available();

    /**
     * Return the memory capacity. The result is given in number of elements,
     * not in bytes.
     * Note that this function reports a sum over all threads.
     */
    size_t mem_capacity();

    virtual bool has_proxies()=0;
    virtual bool one_node_per_process()=0;
    virtual bool is_off_grid()=0;
 
    /**
     * Change properties of the prototype node according to the
     * entries in the dictionary.
     * @param d Dictionary with named parameter settings.
     * @ingroup status_interface
     */
    void set_status(DictionaryDatum);

    /**
     * Export properties of the prototype node by setting
     * entries in the status dictionary.
     * @param d Dictionary.
     * @ingroup status_interface
     */
    DictionaryDatum get_status(void) ;

    virtual port check_connection(Connection&, port)=0;

    /**
     * Return the size of the prototype.
     */
    virtual 
    size_t get_element_size() const=0;

    /**
     * Return const reference to the prototype.
     */
    virtual 
    Node const& get_prototype(void) const =0;

    /**
     * Set the model id on the prototype.
     */
    virtual
    void set_model_id(int) =0;
    
    /**
     * Set the model id on the prototype.
     */
    void set_type_id(index id)
    {
      type_id_=id;
    }
      
    index get_type_id() const
    {
      return type_id_;
    }
	      
  private:
  
    virtual 
    void set_status_(DictionaryDatum)=0;

    virtual 
    DictionaryDatum get_status_()=0;


    /**
     * Set the number of threads.
     * @see set_threads()
     */
    void set_threads_(thread t);
  
    /**
     * Initialize the pool allocator with the Node specific values.
     */
    virtual
    void init_memory_(sli::pool &)=0;

    /**
     * Allocate a new object at the specified memory position.
     */
    virtual
    Node * allocate_(void *)=0;

    /**
     * Name of the Model.
     * This name will be used to identify all Nodes which are
     * created by this model object.
     */
    std::string name_;

    /**
     * Identifier of the model C++ type.
     * For pristine models, the type_id equals the model_id.
     * For copied models, the type_id equals the type_id of the base model.
     * This number is needed to automatically save and restore copied models.
     */
    index type_id_; 

    /**
     * Memory for all nodes sorted by threads.
     */
    std::vector<sli::pool> memory_;

  };


  inline
  Node * Model::allocate(thread t)
  {
    assert((size_t)t < memory_.size());
    return allocate_(memory_[t].alloc());
  }

  inline
  void Model::free(thread t, Node *n)
  {
    assert((size_t)t < memory_.size());
    memory_[t].free(n);
  }

  inline
  std::string Model::get_name() const
  {
    return name_;
  }

}
#endif