/*
 *  Open BEAGLE
 *  Copyright (C) 2001-2004 by Christian Gagne and Marc Parizeau
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *  Laboratoire de Vision et Systemes Numeriques
 *  Departement de genie electrique et de genie informatique
 *  Universite Laval, Quebec, Canada, G1K 7P4
 *  http://vision.gel.ulaval.ca
 *
 */

/*!
 *  \file   beagle/GP/EphemeralT.hpp
 *  \brief  Definition of the templated class GP::EphemeralT.
 *  \author Christian Gagne
 *  \author Marc Parizeau
 *  $Revision: 1.3 $
 *  $Date: 2004/04/25 23:12:20 $
 */

#ifndef Beagle_GP_EphemeralT_hpp
#define Beagle_GP_EphemeralT_hpp

#include <string>

#include "beagle/config.hpp"
#include "beagle/macros.hpp"
#include "beagle/Object.hpp"
#include "beagle/AllocatorT.hpp"
#include "beagle/PointerT.hpp"
#include "beagle/ContainerT.hpp"
#include "beagle/WrapperT.hpp"
#include "beagle/XMLParser.hpp"
#include "beagle/XMLStreamer.hpp"
#include "beagle/GP/Primitive.hpp"
#include "beagle/GP/PrimitiveInline.hpp"
#include "beagle/GP/Context.hpp"
#include "beagle/GP/Datum.hpp"

#ifdef BEAGLE_HAVE_RTTI
#include <typeinfo>
#endif // BEAGLE_HAVE_RTTI


namespace Beagle {
namespace GP {

/*!
 *  \class EphemeralT beagle/GP/EphemeralT.hpp "beagle/GP/EphemeralT.hpp"
 *  \brief Generic Randomly generated ephemeral constant primitive.
 *  \param T Type of the constants.
 *  \ingroup GPF
 *  \ingroup Primit
 *  \ingroup FunctPrimit
 */
template <class T>
class EphemeralT : public Primitive {

public:

  //! GP::EphemeralT allocator type.
  typedef AllocatorT<EphemeralT<T>,Primitive::Alloc>
          Alloc;
  //! GP::EphemeralT handle type.
  typedef PointerT<EphemeralT<T>,Primitive::Handle>
          Handle;
  //! GP::EphemeralT bag type.
  typedef ContainerT<EphemeralT<T>,Primitive::Bag>
          Bag;

  explicit EphemeralT(typename T::Handle inValue=NULL, std::string inName="E");
  virtual ~EphemeralT() { }

  /*!
   *  \brief  Generate a new ephemeral constant.
   *  \param  inName Name of the constant.
   *  \param  ioContext Context to use to generate the value.
   *  \return Handle to the ephemeral constant generated.
   */
  virtual GP::Primitive::Handle generate(std::string inName, GP::Context& ioContext) =0;

  virtual std::string           getReturnType() const;
  virtual void                  getValue(Object& outValue);
  virtual GP::Primitive::Handle giveReference(GP::Context& ioContext);
  virtual void                  execute(GP::Datum& outResult, GP::Context& ioContext);
  virtual void                  read(XMLNode::Handle& inNode);
  virtual void                  setValue(const Object& inValue);
  virtual void                  writeContent(XMLStreamer& ioStreamer) const;

protected:
  typename T::Handle mValue;     //!< Value of the ephemeral.

};

}
}


/*!
 *  \brief Construct a new ephemeral constant. If the value is NULL, act a an ephemeral generator.
 *  \param inValue Value of the ephemeral constant. When NULL, act a an ephemeral generator.
 *  \param inName Name of the ephemeral primitive.
 */
template <class T>
Beagle::GP::EphemeralT<T>::EphemeralT(typename T::Handle inValue, std::string inName) :
  Beagle::GP::Primitive(0, inName),
  mValue(inValue)
{ }


/*!
 *  \brief Return the string tagging the type of data used as ephemeral constant.
 *  \return String tagging the type of data returned.
 *  \throw InternalException If RTTI is disabled and the method is not properly overdefined.
 */
template <class T>
std::string Beagle::GP::EphemeralT<T>::getReturnType() const
{
#ifdef BEAGLE_HAVE_RTTI
  return typeid(T).name();
#else  // BEAGLE_HAVE_RTTI
  throw Beagle_UndefinedMethodInternalExceptionM("getReturnType","GP::TokenT");
  return "";
#endif // BEAGLE_HAVE_RTTI
}


/*!
 *  \brief Get the value of the ephemeral constant.
 *  \param outValue Object containing the value of the ephemeral constant.
 *  \throw InternalException If the constant value is missing.
 */
template <class T>
void Beagle::GP::EphemeralT<T>::getValue(Beagle::Object& outValue)
{
  if(!mValue) throw Beagle_InternalExceptionM("Could not execute as the constant value is missing!");
  T& lValueT = castObjectT<T&>(outValue);
  lValueT = *mValue;
}


/*!
 *  \brief Give a referene to the ephemeral. If the value is NULL, generate a new ephemeral.
 *  \return Handle to the actual ephemeral, or a new ephemeral if the value is missing (NULL).
 */
template <class T>
Beagle::GP::Primitive::Handle
Beagle::GP::EphemeralT<T>::giveReference(Beagle::GP::Context& ioContext)
{
  if(!mValue) return generate(getName(), ioContext);
  return this;
}


/*!
 *  \brief Execute the ephemeral constant operation by copying the value.
 *  \param outResult Result of the execution, the value of the constant.
 *  \param ioContext Evolutionary context.
 *  \throw InternalException If the constant value is missing.
 */
template <class T>
void Beagle::GP::EphemeralT<T>::execute(Beagle::GP::Datum& outResult, Beagle::GP::Context& ioContext)
{
  if(!mValue) throw Beagle_InternalExceptionM("Could not execute as the constant value is missing!");
  T& lResult = castObjectT<T&>(outResult);
  lResult = *mValue;
}


/*!
 *  \brief Read ephemeral constant from XML node.
 *  \param inNode XML node to read the ephemeral from.
 *  \throw Beagle::IOException If the constant value is missing.
 */
template <class T>
void Beagle::GP::EphemeralT<T>::read(Beagle::XMLNode::Handle& inNode)
{
  if(inNode->getType() != XMLNode::eTag)
    throw Beagle_IOExceptionNodeM(*inNode, "tag expected!");

  if(inNode->getTagName() != getName()) {
    std::ostringstream lOSS;
    lOSS << "tag <" << getName() << "> expected, but ";
    lOSS << "got tag <" << inNode->getTagName() << "> instead!";
    throw Beagle_IOExceptionNodeM(*inNode, lOSS.str());
  }

  if(mValue != NULL) {
    std::string lValue = inNode->getTagAttribute("value");
    if(lValue.empty())
      throw Beagle_IOExceptionNodeM(*inNode, "value of ephemeral constant expected!");
    std::istringstream lISS(lValue);
    XMLParser lParser;
    lParser.parse(lISS);
    XMLNode::Handle lRootNode = lParser.getRootNode();
    mValue->read(lRootNode);
  }
}


/*!
 *  \brief Set the value of the ephemeral constant.
 *  \param inValue Object containing the new value of the ephemeral constant.
 *  \throw InternalException If the constant value is missing.
 */
template <class T>
void Beagle::GP::EphemeralT<T>::setValue(const Beagle::Object& inValue)
{
  if(!mValue) throw Beagle_InternalExceptionM("Could not execute as the constant value is missing!");
  const T& lValueT = castObjectT<const T&>(inValue);
  *mValue = lValueT;
}


/*!
 *  \brief Write the value of the ephemeral constant.
 *  \param ioStreamer XML streamer to write content of ephemeral constant.
 */
template <class T>
void Beagle::GP::EphemeralT<T>::writeContent(Beagle::XMLStreamer& ioStreamer) const
{
  if(mValue != NULL) {
    std::ostringstream lOSS;
    XMLStreamer lStreamer2(lOSS);
    mValue->write(lStreamer2);
    ioStreamer.insertAttribute("value", lOSS.str());
  }
}


#endif // Beagle_GP_EphemeralT_hpp

