// Copyright (C) 2004, 2006 International Business Machines and others.
// All Rights Reserved.
// This code is published under the Eclipse Public License.
//
// Authors:  Carl Laird, Andreas Waechter     IBM    2004-08-13

#ifndef __IPALGSTRATEGY_HPP__
#define __IPALGSTRATEGY_HPP__

#include "IpOptionsList.hpp"
#include "IpJournalist.hpp"
#include "IpIpoptCalculatedQuantities.hpp"
#include "IpIpoptNLP.hpp"
#include "IpIpoptData.hpp"

namespace Ipopt
{

/** This is the base class for all algorithm strategy objects.
 *
 *  The AlgorithmStrategyObject base class implements a common interface
 *  for all algorithm strategy objects.  A strategy object is a
 *  component of the algorithm for which different alternatives or
 *  implementations exists.  It allows to compose the algorithm
 *  before execution for a particular configuration, without the
 *  need to call alternatives based on enums. For example, the
 *  LineSearch object is a strategy object, since different line
 *  search options might be used for different runs.
 *
 *  This interface is used for
 *  things that are done to all strategy objects, like
 *  initialization and setting options.
 */
class IPOPTLIB_EXPORT AlgorithmStrategyObject: public ReferencedObject
{
public:
   /**@name Constructors/Destructors */
   ///@{
   /** Default Constructor */
   AlgorithmStrategyObject()
      : initialize_called_(false)
   { }

   /** Destructor */
   virtual ~AlgorithmStrategyObject()
   { }
   ///@}

   /** This method is called every time the algorithm starts again -
    *  it is used to reset any internal state.
    *
    *  The pointers to the
    *  Journalist, as well as to the IpoptNLP, IpoptData, and
    *  IpoptCalculatedQuantities objects should be stored in the
    *  instantiation of this base class.  This method is also used to
    *  get all required user options from the OptionsList.  Here, if
    *  prefix is given, each tag (identifying the options) is first
    *  looked for with the prefix in front, and if not found, without
    *  the prefix.  Note: you should not cue off of the iteration
    *  count to indicate the "start" of an algorithm!
    *
    *  Do not overload this method, since it does some general
    *  initialization that is common for all strategy objects.
    *  Overload the protected InitializeImpl method instead.
    */
   bool Initialize(
      const Journalist&          jnlst,
      IpoptNLP&                  ip_nlp,
      IpoptData&                 ip_data,
      IpoptCalculatedQuantities& ip_cq,
      const OptionsList&         options,
      const std::string&         prefix
   )
   {
      initialize_called_ = true;
      // Copy the pointers for the problem defining objects
      jnlst_ = &jnlst;
      ip_nlp_ = &ip_nlp;
      ip_data_ = &ip_data;
      ip_cq_ = &ip_cq;

      bool retval = InitializeImpl(options, prefix);
      if( !retval )
      {
         initialize_called_ = false;
      }

      return retval;
   }

   /** Reduced version of the Initialize method, which does not
    *  require special Ipopt information.
    *
    *  This is useful for algorithm objects that could be used
    *  outside Ipopt, such as linear solvers.
    */
   bool ReducedInitialize(
      const Journalist&  jnlst,
      const OptionsList& options,
      const std::string& prefix
   )
   {
      initialize_called_ = true;
      // Copy the pointers for the problem defining objects
      jnlst_ = &jnlst;
      ip_nlp_ = NULL;
      ip_data_ = NULL;
      ip_cq_ = NULL;

      bool retval = InitializeImpl(options, prefix);
      if( !retval )
      {
         initialize_called_ = false;
      }

      return retval;
   }

protected:
   /** Implementation of the initialization method that has to be
    *  overloaded by for each derived class.
    */
   virtual bool InitializeImpl(
      const OptionsList& options,
      const std::string& prefix
   ) = 0;

   /** @name Accessor methods for the problem defining objects.
    *
    *  Those should be used by the derived classes.
    */
   ///@{
   const Journalist& Jnlst() const
   {
      DBG_ASSERT(initialize_called_);
      return *jnlst_;
   }
   IpoptNLP& IpNLP() const
   {
      DBG_ASSERT(initialize_called_);
      DBG_ASSERT(IsValid(ip_nlp_));
      return *ip_nlp_;
   }
   IpoptData& IpData() const
   {
      DBG_ASSERT(initialize_called_);
      DBG_ASSERT(IsValid(ip_data_));
      return *ip_data_;
   }
   IpoptCalculatedQuantities& IpCq() const
   {
      DBG_ASSERT(initialize_called_);
      DBG_ASSERT(IsValid(ip_cq_));
      return *ip_cq_;
   }
   bool HaveIpData() const
   {
      return IsValid(ip_data_);
   }
   ///@}

private:
   /**@name Default Compiler Generated Methods
    * (Hidden to avoid implicit creation/calling).
    *
    * These methods are not implemented and
    * we do not want the compiler to implement
    * them for us, so we declare them private
    * and do not define them. This ensures that
    * they will not be implicitly created/called.
    */
   ///@{
   /** Default Constructor */
   //AlgorithmStrategyObject();

   /** Copy Constructor */
   AlgorithmStrategyObject(
      const AlgorithmStrategyObject&
   );

   /** Default Assignment Operator */
   void operator=(
      const AlgorithmStrategyObject&
   );
   ///@}

   /** @name Pointers to objects defining a particular optimization problem */
   ///@{
   SmartPtr<const Journalist> jnlst_;
   SmartPtr<IpoptNLP> ip_nlp_;
   SmartPtr<IpoptData> ip_data_;
   SmartPtr<IpoptCalculatedQuantities> ip_cq_;
   ///@}

   /** flag indicating if Initialize method has been called (for debugging) */
   bool initialize_called_;
};

} // namespace Ipopt

#endif
