/*
 *  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/AdfT.hpp
 *  \brief  Definition of the templated class GP::AdfT.
 *  \author Christian Gagne
 *  \author Marc Parizeau
 *  $Revision: 1.3 $
 *  $Date: 2004/04/25 23:12:20 $
 */

#ifndef Beagle_GP_AdfT_hpp
#define Beagle_GP_AdfT_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/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 AdfT beagle/GP/AdfT.hpp "beagle/GP/AdfT.hpp"
 *  \brief GP generic atomatically defined function (ADF) primitive
 *  \param ReturnType Type of data returned by the ADF.
 *  \param ArgsType Type of data taken as the ADF's arguments.
 *  \ingroup GPF
 *  \ingroup Primit
 *  \ingroup FunctPrimit
 */
template <class ReturnType, class ArgsType>
class AdfT : public Primitive {

public:

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

  explicit AdfT(unsigned int lAdfIndex=1,
                unsigned int lNumberArgs=2,
                std::string inName="ADF",
                std::string inArgsPrefix="ARG");
  virtual ~AdfT() { }

  virtual std::string getArgType(unsigned int inN) const;
  virtual std::string getReturnType() const;
  virtual void        execute(GP::Datum& outResult, GP::Context& ioContext);

protected:
  std::string  mArgsPrefix;  //!< Arguments' name prefixto which is appended an index.
  unsigned int mAdfIndex;    //!< Index of the ADF associated tree in the individual.

};

}
}


/*!
 *  \brief Construct a new ADF primitive.
 *  \param lAdfIndex Index of the ADF associated tree in the individual.
 *  \param lNumberArgs Number of arguments of the ADF.
 *  \param inName Name of the ADF primitive.
 *  \param inArgsPrefix Name prefix of the associated arguments.
 */
template <class ReturnType, class ArgsType>
Beagle::GP::AdfT<ReturnType,ArgsType>::AdfT(unsigned int lAdfIndex,
                                            unsigned int lNumberArgs,
                                            std::string inName,
                                            std::string inArgsPrefix) :
  Beagle::GP::Primitive(lNumberArgs, inName),
  mArgsPrefix(inArgsPrefix),
  mAdfIndex(lAdfIndex)
{ }


/*!
 *  \brief Return the tag of the type of data needed as input for the primitive AdfT.
 *  \param inN Index of the argument to get the type tag.
 *  \return String tagging the data type needed.
 *  \throw AssertException If the index inN given is > to 1.
 *  \throw InternalException If RTTI is disabled and the method is not properly overdefined.
 */
template <class ReturnType, class ArgsType>
std::string Beagle::GP::AdfT<ReturnType,ArgsType>::getArgType(unsigned int inN) const
{
#ifdef BEAGLE_HAVE_RTTI
  Beagle_AssertM(inN < getNumberArguments());
  return typeid(ArgsType).name();
#else  // BEAGLE_HAVE_RTTI
  throw Beagle_UndefinedMethodInternalExceptionM("getArgType","GP::AdfT");
  return "";
#endif // BEAGLE_HAVE_RTTI
}


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


/*!
 *  \brief Execute the call to the ADF associated tree with the given parameters.
 *  \param outResult Result of the ADF.
 *  \param ioContext Evolutionary context.
 */
template <class ReturnType, class ArgsType>
void Beagle::GP::AdfT<ReturnType,ArgsType>::execute(Beagle::GP::Datum& outResult,
                                                    GP::Context& ioContext)
{
#ifndef BEAGLE_NDEBUG
  if(mAdfIndex >= ioContext.getSystem().getPrimitiveSuperSet().size()) {
   std::string lMessage = "The ADF \"";
   lMessage += getName();
   lMessage += "\" has an index (";
   lMessage += uint2str(mAdfIndex);
   lMessage += ") that is superior or equal to the number of sets (";
   lMessage += uint2str(ioContext.getSystem().getPrimitiveSuperSet().size());
   lMessage += ") in the primitive super set.";
   throw Beagle_RunTimeExceptionM(lMessage);
  }
  if(mAdfIndex >= ioContext.getIndividual().size()) {
   std::string lMessage = "The ADF \"";
   lMessage += getName();
   lMessage += "\" has an index (";
   lMessage += uint2str(mAdfIndex);
   lMessage += ") that is superior or equal to the number of trees (";
   lMessage += uint2str(ioContext.getIndividual().size());
   lMessage += ") in the actual individual.";
   throw Beagle_RunTimeExceptionM(lMessage);
  }
#endif // BEAGLE_NDEBUG

  // Keep actual arguments value in a buffer and set the arguments to the ADF values.
  GP::PrimitiveSet::Handle lAssocPrimitSet =
    ioContext.getSystem().getPrimitiveSuperSet()[mAdfIndex];
  std::vector<ArgsType> lOldArgsValue(getNumberArguments());
  for(unsigned int i=0; i<lOldArgsValue.size(); i++) {
    std::string lArgName = mArgsPrefix + uint2str(i);
    Primitive::Handle lActualArg = lAssocPrimitSet->getPrimitiveByName(lArgName);
#ifndef BEAGLE_NDEBUG
    if(!lActualArg) {
      std::string lMessage = "The argument named \"";
      lMessage += lArgName;
      lMessage += "composed using the prefix given to the ADF named \"";
      lMessage += getName();
      lMessage += "\" does not refer to a valid primitive in the set";
      lMessage += " of the tree associated to the ADF.";
      throw Beagle_RunTimeExceptionM(lMessage);
    }
#endif // BEAGLE_NDEBUG
    lActualArg->getValue(lOldArgsValue[i]);
    ArgsType lActualArgValue;
    getArgument(i, lActualArgValue, ioContext);
    lActualArg->setValue(lActualArgValue);
  }

  // Call the associated tree.
  GP::Tree::Handle lAssocTree = ioContext.getIndividual()[mAdfIndex];
  GP::Tree::Handle lOldGenotypeHandle = ioContext.getGenotypeHandle();
  unsigned int lOldGenotypeIndex = ioContext.getGenotypeIndex();
  ioContext.setGenotypeHandle(lAssocTree);
  ioContext.setGenotypeIndex(mAdfIndex);
  ioContext.pushCallStack(0);
  (*lAssocTree)[0].mPrimitive->execute(outResult, ioContext);
  ioContext.popCallStack();

  // Reset everything as it was initially.
  ioContext.setGenotypeHandle(lOldGenotypeHandle);
  ioContext.setGenotypeIndex(lOldGenotypeIndex);
  for(unsigned int j=0; j<lOldArgsValue.size(); j++) {
    std::string lArgName = mArgsPrefix + uint2str(j);
    Primitive::Handle lActualArg = lAssocPrimitSet->getPrimitiveByName(lArgName);
    lActualArg->setValue(lOldArgsValue[j]);
  }
}


#endif // Beagle_GP_AdfT_hpp
