/*
 *  datum.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 DATUM_H
#define DATUM_H

#include "slitype.h"
class DatumConverter;


/***********************************************************/
/* Datum                                                   */
/* -----                                                   */
/*  base class for all Data Objects                        */
/***********************************************************/
class Datum
{
  
  friend class Token;
  
  virtual Datum * clone(void) const = 0;


  /**
   * Returns a reference counted pointer to the datum, or a new pointer, if the
   * type does not support reference counting.
   * The prefix const indicates that the pointer should be trated as const
   * because changes affect all other references as well.
   */ 

  virtual Datum * get_ptr() 
  {
    return clone();
  }

 protected:
  // Putting the following variables here, avoids a number of virtual
  // functions.

  const SLIType     *type;   //!< Pointer to type object.
  const SLIFunction *action; //!< Shortcut to the SLIType default action.
  mutable
    unsigned int reference_count_;
  bool   executable_;

 Datum() :
  type(NULL), 
    action(NULL),
    reference_count_(1),
    executable_(true)
      {}

  
 Datum(const SLIType *t) : 
  type(t), 
    action(t->getaction()),
    reference_count_(1),
    executable_(true)
      { }

 Datum(const Datum &d) : type(d.type), action(d.action),reference_count_(1),executable_(d.executable_){}


 public:

  virtual ~Datum() {};

  void addReference() const
  {
    ++reference_count_;
  }

  void removeReference()
  {
    --reference_count_;
    if(reference_count_==0)
      delete this;
  }

  size_t numReferences() const
  {
    return reference_count_;
  }

  bool is_executable() const
  {
    return executable_;
  }

  void set_executable()
  {
    executable_=true;
  }

  void unset_executable()
  {
    executable_=false;
  }

  virtual void  print(std::ostream & ) const =0;
  virtual void  pprint(std::ostream &) const =0;

  virtual void  list(std::ostream &o, std::string prefix, int l) const
  {
    if(l==0)
      prefix="-->"+prefix;
    else
      prefix="   "+prefix;
    o << prefix;
    print(o);
  }

  virtual void  input_form(std::ostream &o) const
  {
    pprint(o);
  }
  
  virtual bool  equals(const Datum *d) const
  {
    return this == d;
  }
  
  virtual void  info(std::ostream &) const;
  
  const Name & gettypename(void) const
    {
      return type->gettypename();
    }

  bool isoftype(SLIType const &t) const
    {
      return (type==&t);  // or: *type==t, there is only one t with same contents !
    }
    
  void execute(SLIInterpreter *i)
    {
      action->execute(i);
    }

  /**
   * Accept a DatumConverter as a visitor to this datum for conversion.
   * (visitor pattern).
   */
  virtual void use_converter(DatumConverter &v);
  
};

template<SLIType * slt >
class TypedDatum: public Datum
{
 public:
 TypedDatum(void)
   :Datum(slt)
  { }
  
 TypedDatum(const TypedDatum<slt> &d) :Datum(d){}
  const TypedDatum<slt>& operator=(const TypedDatum<slt>&);
  
};

template<SLIType * slt >
inline 
const TypedDatum<slt>& TypedDatum<slt>::operator=(const TypedDatum<slt>&)
{
  //  assert( type == d.type );
  return *this;
}
  


#endif