/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#ifndef elxProgressCommand_h
#define elxProgressCommand_h

#include "itkProcessObject.h"
#include "itkCommand.h"

namespace elastix
{

/**
 * \class ProgressCommand
 * \brief A specialized Command object for updating the progress of a
 *  filter.
 *
 * There are 3 ways to use this class.
 *
 * \li Whenever a filter, such as the itk::ResampleImageFilter, supports
 * a ProgressReporter, this class can be employed. This class makes
 * sure that the progress of a filter is printed to screen. It works
 * as follows:
 *
 * \code
 *   auto command = ProgressCommand::New();
 *   command->ConnectObserver( filterpointer );
 *   command->SetStartString( "  Progress: " );
 *   command->SetEndString( "%" );
 *   filterpointer->Update(); // run the filter, progress messages are printed now
 *   command->DisconnectObserver( filterPointer );
 * \endcode
 *
 * So, first an instantiation of this class is created, then it is
 * connected to a filter, and some options are set. Whenever the filter
 * throws a ProgressEvent(), this class asks for the progress and prints
 * the percentage of progress.
 *
 * \li In manually written loops, a call to UpdateAndPrintProgress() can be included.
 * Before the loop, the user should set the total number of loops, and the frequency
 * that the progress message should be printed with. For example
 *
 * \code
 *   auto command = ProgressCommand::New();
 *   command->SetUpdateFrequency( maxnrofvoxels, 100 );
 *   command->SetStartString( "  Progress: " );
 *   command->SetEndString( "%" );
 *   log::info(std::ostringstream{}  << "Looping over voxels... ");
 *   for ( unsigned int i =0; i < maxnrofvoxels; ++i )
 *   {
 *     command->UpdateAndPrintProgress( i );
 *   }
 *   command->PrintProgress(1.0); // make sure the 100% is reached
 * \endcode
 *
 * \li The last possibility is to directly use the PrintProgress function:
 *
 * \code
 *   auto command = ProgressCommand::New();
 *   command->SetStartString( "  Progress: " );
 *   command->SetEndString( "%" );
 *   // Reading, casting, writing...
 *   command->PrintProgress( 0.0 );
 *   reader->Update();
 *   command->PrintProgress( 0.33 );
 *   caster->Update();
 *   command->PrintProgress( 0.67 );
 *   writer->Update();
 *   command->PrintProgress( 1.0 );
 *   // example assumes reader, caster and writer have been configured before
 * \endcode
 *
 */

class ProgressCommand : public itk::Command
{
public:
  /** Smart pointer declaration methods. */
  using Self = ProgressCommand;
  using Superclass = itk::Command;
  using Pointer = itk::SmartPointer<Self>;
  using ConstPointer = itk::SmartPointer<const Self>;

  /** Standard ITK stuff. */
  itkOverrideGetNameOfClassMacro(ProgressCommand);
  itkNewMacro(Self);

  /** Typedef's. */
  using ProcessObjectType = itk::ProcessObject;
  using ProcessObjectPointer = ProcessObjectType::Pointer;

  /** Define when to print the progress. */
  virtual void
  SetUpdateFrequency(const unsigned long numberOfVoxels, const unsigned long numberOfUpdates);

  /** Connect an observer to a process object. */
  virtual void
  ConnectObserver(itk::ProcessObject * filter);

  /** Disconnect an observer to a process object. */
  void
  DisconnectObserver(itk::ProcessObject * filter);

  /** Standard Command virtual methods. */
  void
  Execute(itk::Object * caller, const itk::EventObject & event) override;

  void
  Execute(const itk::Object * caller, const itk::EventObject & event) override;

  /** Print the progress to screen. A float value between 0.0 and 1.0
   * is expected as input.
   */
  void
  PrintProgress(const float progress) const;

  /** Update and possibly print the progress to screen.
   * The progress information on screen is refreshed according to the
   * UpdateFrequency, which is assumed being specified beforehand using the
   * SetUpdateFrequency function.
   */
  void
  UpdateAndPrintProgress(const unsigned long currentVoxelNumber) const;

  /** Set and get the string starting each progress report. */
  itkSetStringMacro(StartString);
  itkGetStringMacro(StartString);

  /** Set and get the string ending each progress report. */
  itkSetStringMacro(EndString);
  itkGetStringMacro(EndString);

  static Pointer
  CreateAndSetUpdateFrequency(unsigned long numberOfVoxels);

  static Pointer
  CreateAndConnect(itk::ProcessObject &);

protected:
  /** The constructor. */
  ProgressCommand();

  /** The destructor. */
  ~ProgressCommand() override;

private:
  /** Member variables to define a start and end string for printing. */
  std::string m_StartString;
  std::string m_EndString;

  /** Member variables to keep track of what is set. */
  unsigned long        m_Tag;
  bool                 m_TagIsSet;
  ProcessObjectPointer m_ObservedProcessObject;

  /** Member variables that define the update frequency. */
  unsigned long m_NumberOfVoxels;
  unsigned long m_NumberOfUpdates;
};

} // end namespace elastix

#endif // end #ifndef elxProgressCommand_h
