// Created on: 2002-02-22
// Created by: Andrey BETENEV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef _Message_ProgressScope_HeaderFile
#define _Message_ProgressScope_HeaderFile

#include <Standard_Assert.hxx>
#include <Standard_TypeDef.hxx>
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <Precision.hxx>
#include <TCollection_AsciiString.hxx>

class Message_ProgressRange;
class Message_ProgressIndicator;

//! Message_ProgressScope class provides convenient way to advance progress
//! indicator in context of complex program organized in hierarchical way,
//! where usually it is difficult (or even not possible) to consider process
//! as linear with fixed step.
//!
//! On every level (sub-operation) in hierarchy of operations
//! the local instance of the Message_ProgressScope class is created.
//! It takes a part of the upper-level scope (via Message_ProgressRange) and provides
//! a way to consider this part as independent scale with locally defined range.
//!
//! The position on the local scale may be advanced using the method Next(),
//! which allows iteration-like advancement. This method can take argument to
//! advance by the specified value (with default step equal to 1).
//! This method returns Message_ProgressRange object that takes responsibility
//! of making the specified step, either directly at its destruction or by
//! delegating this task to another sub-scope created from that range object.
//!
//! It is important that sub-scope must have life time less than
//! the life time of its parent scope that provided the range.
//! The usage pattern is to create scope objects as local variables in the
//! functions that do the job, and pass range objects returned by Next() to
//! the functions of the lower level, to allow them creating their own scopes.
//!
//! The scope has a name that can be used in visualization of the progress.
//! It can be null. Note that when C string literal is used as a name, then its
//! value is not copied, just pointer is stored. In other variants (char pointer
//! or a string class) the string is copied, which is additional overhead.
//!
//! The same instance of the progress scope! must not be used concurrently from different threads.
//! For the algorithm running its tasks in parallel threads, a common scope is
//! created before the parallel execution, and the range objects produced by method
//! Next() are used to initialise the data pertinent to each task.
//! Then the progress is advanced within each task using its own range object.
//! See example below.
//!
//! Note that while a range of the scope is specified using double
//! (double) parameter, it is expected to be a positive integer value.
//! If the range is not an integer, method Next() shall be called with
//! explicit step argument, and the rounded value returned by method Value()
//! may be not coherent with the step and range.
//!
//! A scope can be created with option "infinite". This is useful when
//! the number of steps is not known by the time of the scope creation.
//! In this case the progress will be advanced logarithmically, approaching
//! the end of the scope at infinite number of steps. The parameter Max
//! for infinite scope indicates number of steps corresponding to mid-range.
//!
//! A progress scope created with empty constructor is not connected to any
//! progress indicator, and passing the range created on it to any algorithm
//! allows it executing safely without actual progress indication.
//!
//! Example of preparation of progress indicator:
//!
//! @code{.cpp}
//!   occ::handle<Message_ProgressIndicator> aProgress = ...; // assume it can be null
//!   func (Message_ProgressIndicator::Start (aProgress));
//! @endcode
//!
//! Example of usage in sequential process:
//!
//! @code{.cpp}
//!   Message_ProgressScope aWholePS(aRange, "Whole process", 100);
//!
//!   // do one step taking 20%
//!   func1 (aWholePS.Next (20)); // func1 will take 20% of the whole scope
//!   if (aWholePS.UserBreak()) // exit prematurely if the user requested break
//!     return;
//!
//!   // ... do next step taking 50%
//!   func2 (aWholePS.Next (50));
//!   if (aWholePS.UserBreak())
//!     return;
//! @endcode
//!
//! Example of usage in nested cycle:
//!
//! @code{.cpp}
//!   // Outer cycle
//!   Message_ProgressScope anOuter (theProgress, "Outer", nbOuter);
//!   for (int i = 0; i < nbOuter && anOuter.More(); i++)
//!   {
//!     // Inner cycle
//!     Message_ProgressScope anInner (anOuter.Next(), "Inner", nbInner);
//!     for (int j = 0; j < nbInner && anInner.More(); j++)
//!     {
//!       // Cycle body
//!       func (anInner.Next());
//!     }
//!   }
//! @endcode
//!
//! Example of use in function:
//!
//! @code{.cpp}
//! //! Implementation of iterative algorithm showing its progress
//! func (const Message_ProgressRange& theProgress)
//! {
//!   // Create local scope covering the given progress range.
//!   // Set this scope to count aNbSteps steps.
//!   Message_ProgressScope aScope (theProgress, "", aNbSteps);
//!   for (int i = 0; i < aNbSteps && aScope.More(); i++)
//!   {
//!     // Optional: pass range returned by method Next() to the nested algorithm
//!     // to allow it to show its progress too (by creating its own scope object).
//!     // In any case the progress will advance to the next step by the end of the func2 call.
//!     func2 (aScope.Next());
//!   }
//! }
//! @endcode
//!
//! Example of usage in parallel process:
//!
//! @code{.cpp}
//! struct Task
//! {
//!   Data& Data;
//!   Message_ProgressRange Range;
//!
//!   Task (const Data& theData, const Message_ProgressRange& theRange)
//!     : Data (theData), Range (theRange) {}
//! };
//! struct Functor
//! {
//!   void operator() (Task& theTask) const
//!   {
//!     // Note: it is essential that this method is executed only once for the same Task object
//!     Message_ProgressScope aPS (theTask.Range, NULL, theTask.Data.NbItems);
//!     for (int i = 0; i < theTask.Data.NbSteps && aPS.More(); i++)
//!     {
//!       do_job (theTask.Data.Item[i], aPS.Next());
//!     }
//!   }
//! };
//! ...
//! {
//!   std::vector<Data> aData = ...;
//!   std::vector<Task> aTasks;
//!
//!   Message_ProgressScope aPS (aRootRange, "Data processing", aData.size());
//!   for (int i = 0; i < aData.size(); ++i)
//!     aTasks.push_back (Task (aData[i], aPS.Next()));
//!
//!   OSD_Parallel::ForEach (aTasks.begin(), aTasks.end(), Functor());
//! }
//! @endcode
//!
//! For lightweight algorithms that do not need advancing the progress
//! within individual tasks the code can be simplified to avoid inner scopes:
//!
//! @code
//! struct Functor
//! {
//!   void operator() (Task& theTask) const
//!   {
//!     if (theTask.Range.More())
//!     {
//!       do_job (theTask.Data);
//!       // advance the progress
//!       theTask.Range.Close();
//!     }
//!   }
//! };
//! @endcode
class Message_ProgressScope
{
public:
  class NullString; //!< auxiliary type for passing NULL name to Message_ProgressScope constructor
public:             //! @name Preparation methods
  //! Creates dummy scope.
  //! It can be safely passed to algorithms; no progress indication will be done.
  Message_ProgressScope()
      : myProgress(nullptr),
        myParent(nullptr),
        myName(nullptr),
        myStart(0.),
        myPortion(1.),
        myMax(1.),
        myValue(0.),
        myIsActive(false),
        myIsOwnName(false),
        myIsInfinite(false)
  {
  }

  //! Creates a new scope taking responsibility of the part of the progress
  //! scale described by theRange. The new scope has own range from 0 to
  //! theMax, which is mapped to the given range.
  //!
  //! The topmost scope is created and owned by Message_ProgressIndicator
  //! and its pointer is contained in the Message_ProgressRange returned by the Start() method of
  //! progress indicator.
  //!
  //! @param[in][out] theRange  range to fill (will be disarmed)
  //! @param[in] theName        new scope name
  //! @param[in] theMax         number of steps in scope
  //! @param[in] isInfinite     infinite flag
  Message_ProgressScope(const Message_ProgressRange&   theRange,
                        const TCollection_AsciiString& theName,
                        double                         theMax,
                        bool                           isInfinite = false);

  //! Creates a new scope taking responsibility of the part of the progress
  //! scale described by theRange. The new scope has own range from 0 to
  //! theMax, which is mapped to the given range.
  //!
  //! The topmost scope is created and owned by Message_ProgressIndicator
  //! and its pointer is contained in the Message_ProgressRange returned by the Start() method of
  //! progress indicator.
  //!
  //! @param[in][out] theRange  range to fill (will be disarmed)
  //! @param[in] theName        new scope name constant (will be stored by pointer with no deep
  //! copy)
  //! @param[in] theMax         number of steps in scope
  //! @param[in] isInfinite     infinite flag
  template <size_t N>
  Message_ProgressScope(const Message_ProgressRange& theRange,
                        const char (&theName)[N],
                        double theMax,
                        bool   isInfinite = false);

  //! Creates a new scope taking responsibility of the part of the progress
  //! scale described by theRange. The new scope has own range from 0 to
  //! theMax, which is mapped to the given range.
  //!
  //! The topmost scope is created and owned by Message_ProgressIndicator
  //! and its pointer is contained in the Message_ProgressRange returned by the Start() method of
  //! progress indicator.
  //!
  //! @param[in][out] theRange  range to fill (will be disarmed)
  //! @param[in] theName        empty scope name (only NULL is accepted as argument)
  //! @param[in] theMax         number of steps in scope
  //! @param[in] isInfinite     infinite flag
  Message_ProgressScope(const Message_ProgressRange& theRange,
                        const NullString*            theName,
                        double                       theMax,
                        bool                         isInfinite = false);

  //! Sets the name of the scope.
  void SetName(const TCollection_AsciiString& theName)
  {
    if (myIsOwnName)
    {
      Standard::Free(myName);
      myIsOwnName = false;
    }
    myName = nullptr;
    if (!theName.IsEmpty())
    {
      myIsOwnName = true;
      myName      = (char*)Standard::AllocateOptimal(size_t(theName.Length()) + size_t(1));
      char* aName = (char*)myName;
      memcpy(aName, theName.ToCString(), theName.Length());
      aName[theName.Length()] = '\0';
    }
  }

  //! Sets the name of the scope; can be null.
  //! Note! Just pointer to the given string is copied,
  //! so do not pass string from a temporary variable whose
  //! lifetime is less than that of this object.
  template <size_t N>
  void SetName(const char (&theName)[N])
  {
    if (myIsOwnName)
    {
      Standard::Free(myName);
      myIsOwnName = false;
    }
    myName = theName;
  }

public: //! @name Advance by iterations
  //! Returns true if ProgressIndicator signals UserBreak
  bool UserBreak() const;

  //! Returns false if ProgressIndicator signals UserBreak
  bool More() const { return !UserBreak(); }

  //! Advances position by specified step and returns the range
  //! covering this step
  Message_ProgressRange Next(double theStep = 1.);

public: //! @name Auxiliary methods to use in ProgressIndicator
  //! Force update of presentation of the progress indicator.
  //! Should not be called concurrently.
  void Show();

  //! Returns true if this progress scope is attached to some indicator.
  bool IsActive() const { return myIsActive; }

  //! Returns the name of the scope (may be null).
  //! Scopes with null name (e.g. root scope) should
  //! be bypassed when reporting progress to the user.
  const char* Name() const { return myName; }

  //! Returns parent scope (null for top-level scope)
  const Message_ProgressScope* Parent() const { return myParent; }

  //! Returns the maximal value of progress in this scope
  double MaxValue() const { return myMax; }

  //! Returns the current value of progress in this scope.
  //!
  //! The value is computed by mapping current global progress into
  //! this scope range; the result is rounded up to integer.
  //! Note that if MaxValue() is not an integer, Value() can be
  //! greater than MaxValue() due to that rounding.
  //!
  //! This method should not be called concurrently while the progress
  //! is advancing, except from implementation of method Show() in
  //! descendant of Message_ProgressIndicator.
  double Value() const;

  //! Returns the infinite flag
  bool IsInfinite() const { return myIsInfinite; }

  //! Get the portion of the indicator covered by this scope (from 0 to 1)
  double GetPortion() const { return myPortion; }

public: //! @name Destruction, allocation
  //! Destructor - closes the scope and adds its scale to the total progress
  ~Message_ProgressScope()
  {
    Close();
    if (myIsOwnName)
    {
      Standard::Free(myName);
      myIsOwnName = false;
      myName      = nullptr;
    }
  }

  //! Closes the scope and advances the progress to its end.
  //! Closed scope should not be used.
  void Close();

  DEFINE_STANDARD_ALLOC

private: //! @name Internal methods
  //! Creates a top-level scope with default range [0,1] and step 1.
  //! Called only by Message_ProgressIndicator constructor.
  Message_ProgressScope(Message_ProgressIndicator* theProgress);

  //! Convert value from this scope to global scale, but disregarding
  //! start position of the scope, in the range [0, myPortion]
  double localToGlobal(const double theVal) const;

private:
  //! Copy constructor is prohibited
  Message_ProgressScope(const Message_ProgressScope& theOther) = delete;

  //! Copy assignment is prohibited
  Message_ProgressScope& operator=(const Message_ProgressScope& theOther) = delete;

private:
  Message_ProgressIndicator*   myProgress; //!< Pointer to progress indicator instance
  const Message_ProgressScope* myParent;   //!< Pointer to parent scope
  const char*                  myName; //!< Name of the operation being done in this scope, or null

  double myStart;  //!< Start position on the global scale [0, 1]
                   // clang-format off
  double      myPortion;     //!< The portion of the global scale covered by this scope [0, 1]
                   // clang-format on

  double myMax;   //!< Maximal value of progress in this scope
  double myValue; //!< Current position advanced within this scope [0, Max]

  bool myIsActive;   //!< flag indicating armed/disarmed state
  bool myIsOwnName;  //!< flag indicating if name was allocated or not
  bool myIsInfinite; //!< Option to advance by hyperbolic law

private:
  friend class Message_ProgressIndicator;
  friend class Message_ProgressRange;
};

#include <Message_ProgressRange.hxx>

//=================================================================================================

inline Message_ProgressScope::Message_ProgressScope(Message_ProgressIndicator* theProgress)
    : myProgress(theProgress),
      myParent(nullptr),
      myName(nullptr),
      myStart(0.),
      myPortion(1.),
      myMax(1.),
      myValue(0.),
      myIsActive(theProgress != nullptr),
      myIsOwnName(false),
      myIsInfinite(false)
{
}

//=================================================================================================

inline Message_ProgressScope::Message_ProgressScope(const Message_ProgressRange&   theRange,
                                                    const TCollection_AsciiString& theName,
                                                    double                         theMax,
                                                    bool                           isInfinite)
    : myProgress(theRange.myParentScope != nullptr ? theRange.myParentScope->myProgress : nullptr),
      myParent(theRange.myParentScope),
      myName(nullptr),
      myStart(theRange.myStart),
      myPortion(theRange.myDelta),
      myMax((std::max)(1.e-6, theMax)), // protection against zero range
      myValue(0.),
      myIsActive(myProgress != nullptr && !theRange.myWasUsed),
      myIsOwnName(false),
      myIsInfinite(isInfinite)
{
  SetName(theName);
  Standard_ASSERT_VOID(!theRange.myWasUsed,
                       "Message_ProgressRange is used to initialize more than one scope");
  theRange.myWasUsed = true; // Disarm the range
}

//=================================================================================================

template <size_t N>
Message_ProgressScope::Message_ProgressScope(const Message_ProgressRange& theRange,
                                             const char (&theName)[N],
                                             double theMax,
                                             bool   isInfinite)
    : myProgress(theRange.myParentScope != nullptr ? theRange.myParentScope->myProgress : nullptr),
      myParent(theRange.myParentScope),
      myName(theName),
      myStart(theRange.myStart),
      myPortion(theRange.myDelta),
      myMax((std::max)(1.e-6, theMax)), // protection against zero range
      myValue(0.),
      myIsActive(myProgress != nullptr && !theRange.myWasUsed),
      myIsOwnName(false),
      myIsInfinite(isInfinite)
{
  Standard_ASSERT_VOID(!theRange.myWasUsed,
                       "Message_ProgressRange is used to initialize more than one scope");
  theRange.myWasUsed = true; // Disarm the range
}

//=================================================================================================

inline Message_ProgressScope::Message_ProgressScope(const Message_ProgressRange& theRange,
                                                    const NullString*,
                                                    double theMax,
                                                    bool   isInfinite)
    : myProgress(theRange.myParentScope != nullptr ? theRange.myParentScope->myProgress : nullptr),
      myParent(theRange.myParentScope),
      myName(nullptr),
      myStart(theRange.myStart),
      myPortion(theRange.myDelta),
      myMax((std::max)(1.e-6, theMax)), // protection against zero range
      myValue(0.),
      myIsActive(myProgress != nullptr && !theRange.myWasUsed),
      myIsOwnName(false),
      myIsInfinite(isInfinite)
{
  Standard_ASSERT_VOID(!theRange.myWasUsed,
                       "Message_ProgressRange is used to initialize more than one scope");
  theRange.myWasUsed = true; // Disarm the range
}

//=================================================================================================

inline void Message_ProgressScope::Close()
{
  if (!myIsActive)
  {
    return;
  }

  // Advance indicator to the end of the scope
  double aCurr  = localToGlobal(myValue);
  myValue       = (myIsInfinite ? Precision::Infinite() : myMax);
  double aDelta = myPortion - aCurr;
  if (aDelta > 0.)
  {
    myProgress->Increment(aDelta, *this);
  }
  Standard_ASSERT_VOID(myParent == 0 || myParent->myIsActive,
                       "Parent progress scope has been closed before child");

  myIsActive = false;
}

//=================================================================================================

inline bool Message_ProgressScope::UserBreak() const
{
  return myProgress && myProgress->UserBreak();
}

//=================================================================================================

inline Message_ProgressRange Message_ProgressScope::Next(double theStep)
{
  if (myIsActive && theStep > 0.)
  {
    double aCurr  = localToGlobal(myValue);
    double aNext  = localToGlobal(myValue += theStep);
    double aDelta = aNext - aCurr;
    if (aDelta > 0.)
    {
      return Message_ProgressRange(*this, myStart + aCurr, aDelta);
    }
  }
  return Message_ProgressRange();
}

//=================================================================================================

inline void Message_ProgressScope::Show()
{
  if (myIsActive)
  {
    myProgress->Show(*this, true);
  }
}

//=================================================================================================

inline double Message_ProgressScope::localToGlobal(const double theVal) const
{
  if (theVal <= 0.)
    return 0.;

  if (!myIsInfinite)
  {
    if (myMax - theVal < RealSmall())
      return myPortion;
    return myPortion * theVal / myMax;
  }

  double x = theVal / myMax;
  // return myPortion * ( 1. - std::exp ( -x ) ); // exponent
  return myPortion * x / (1. + x); // hyperbola
}

//=================================================================================================

inline double Message_ProgressScope::Value() const
{
  if (!myIsActive)
  {
    return myIsInfinite ? Precision::Infinite() : myMax;
  }

  // get current progress on the global scale counted
  // from the start of this scope
  double aVal = myProgress->GetPosition() - myStart;

  // if progress has not reached yet the start of this scope, return 0
  if (aVal <= 0.)
    return 0.;

  // if at end of the scope (or behind), report the maximum
  double aDist = myPortion - aVal;
  if (aDist <= Precision::Confusion())
    return myIsInfinite ? Precision::Infinite() : myMax;

  // map the value to the range of this scope [0, Max],
  // rounding up to integer, with small correction applied
  // to avoid rounding errors
  return std::ceil(myMax * aVal / (myIsInfinite ? aDist : myPortion) - Precision::Confusion());
}

#endif // _Message_ProgressScope_HeaderFile
