/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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 itkDomainThreader_h
#define itkDomainThreader_h

#include "itkObject.h"
#include "itkMultiThreaderBase.h"

namespace itk
{

/** \class DomainThreader
 *  \brief Multi-threaded processing on a domain by processing sub-domains per
 *  thread.
 *
 *  This class uses a ThreadedDomainPartitioner as a helper to split the
 *  domain into subdomains.  Each subdomain is then processed in the
 *  \c ThreadedExecution method.
 *
 *  The data on which to perform the processing is assumed to be members
 *  of an associating class.  Therefore, to perform a threaded operation in a
 *  class, the associating class usually will declare derived versions of this
 *  class as a friend class.
 *
 *  To use this class, at a minimum,
 *  \li Inherit from it.
 *  \li Implement ThreadedExecution.
 *  \li Create a member instance.
 *  \li Run with m_DomainThreader->Execute( this, domain );
 *
 *  If a 'threaded method' is desired to perform some data processing in a
 *  class, a derived version of this class can be defined to perform the threaded operation.
 *  Since a threaded operation is relatively complex compared to a simple serial
 *  operation, a class instead of a simple method is required.  Inside this
 *  class, the method to partition the data is handled, the logic for
 *  deciding the number of work units is determined, and operations surrounding
 *  the threading are encapsulated into the class with the
 *  \c DetermineNumberOfWorkUnitsToUse, \c BeforeThreadedExecution, \c ThreadedExecution,
 *  and \c AfterThreadedExecution virtual methods.
 *
 *  \tparam TDomainPartitioner A class that inherits from
 *  ThreadedDomainPartitioner.
 *  \tparam TAssociate  The associated class that uses a derived version of
 *  this class as a "threaded method".  The associated class usually declares
 *  derived version of this class as nested classes so there is easy access to
 *  its protected and private members in ThreadedExecution.
 *
 *  \ingroup ITKCommon
 */
template< typename TDomainPartitioner, typename TAssociate >
class ITK_TEMPLATE_EXPORT DomainThreader: public Object
{
public:
  ITK_DISALLOW_COPY_AND_ASSIGN(DomainThreader);

  /** Standard class type aliases. */
  using Self = DomainThreader;
  using Superclass = Object;
  using Pointer = SmartPointer< Self >;
  using ConstPointer = SmartPointer< const Self >;

  using DomainPartitionerType = TDomainPartitioner;
  using DomainType = typename DomainPartitionerType::DomainType;

  using AssociateType = TAssociate;

  /** Run-time type information (and related methods). */
  itkTypeMacro( DomainThreader, Object );

  /** Run the multi-threaded operation on the given domain.
   *
   * The domain is first partitioned by the ThreadedDomainPartitioner, then
   * the virtual methods \c BeforeThreadedExecution, \c ThreadedExecution, and
   * \c AfterThreadedExecution. are run, in order. */
  void Execute( AssociateType * enclosingClass, const DomainType & domain );

  /** Set/Get the DomainPartitioner. */
  itkSetObjectMacro( DomainPartitioner, DomainPartitionerType );
  itkGetModifiableObjectMacro( DomainPartitioner, DomainPartitionerType );

  /** Accessor for number of work units that were actually used in the last
   * ThreadedExecution. */
  itkGetConstMacro( NumberOfWorkUnitsUsed, ThreadIdType );

  /** Return the multithreader used by this class. */
  MultiThreaderBase * GetMultiThreader() const;

  /** Convenience methods to set/get the desired number of work units to use.
   * \warning When setting the desired number of work units, it might be clamped by
   * itk::MultiThreaderBase::GetGlobalMaximumNumberOfThreads().
   * */
  itkSetClampMacro(NumberOfWorkUnits, ThreadIdType, 1, ITK_MAX_THREADS);
  itkGetConstMacro(NumberOfWorkUnits, ThreadIdType);

  /** Convenience methods to set/get the maximum number of threads to use.
   * \warning When setting the maximum number of threads, it will be clamped by
   * itk::MultiThreaderBase::GetGlobalMaximumNumberOfThreads().
   * */
  ThreadIdType GetMaximumNumberOfThreads() const
  {
    return this->m_MultiThreader->GetMaximumNumberOfThreads();
  }
  void SetMaximumNumberOfThreads(const ThreadIdType threads);

protected:
  DomainThreader();
  ~DomainThreader() override = default;

  /** This is evauated at the beginning of Execute() so that it can be used in
   * BeforeThreadedExecution(). */
  virtual void DetermineNumberOfWorkUnitsUsed();

  /** When \c Execute is run, this method is run singled-threaded before \c
   * ThreadedExecution.  Inside this method optional operations such as
   * creating instance variables needed per thread may be performed. */
  virtual void BeforeThreadedExecution(){}

  /** Do the threaded operation, somewhat like \c ThreadedGenerateData in an
   * ImageSource.
   * \param subdomain The subdomain to operate on.
   * \param threadId  The identifier for the current thread.
   * Data to perform the operation on can be accessed by dereferencing
   * this->m_Associate, which has direct access to private and protected
   * members the enclosing class.
   */
  virtual void ThreadedExecution( const DomainType& subdomain,
                                  const ThreadIdType threadId ) = 0;

  /** When \c Execute in run, this method is run single-threaded after \c
   * ThreadedExecution.  Optionally collect results, etc. E.g. calculate the
   * global minimum from the minimums calculated per thread. */
  virtual void AfterThreadedExecution(){}

  itkSetObjectMacro( MultiThreader, MultiThreaderBase );

  /** Static function used as a "callback" by the MultiThreaderBase.  The threading
   * library will call this routine for each thread, which will delegate the
   * control to the ThreadFunctor. */
  static ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION ThreaderCallback( void *arg );

  AssociateType * m_Associate;

private:
  void StartThreadingSequence();

  /** This contains the object passed to the threading library. */
  struct ThreadStruct
    {
    DomainThreader     * domainThreader;
    };

  /** Store the actual number of work units used, which may be less than
   * the number allocated by the threader if the object does not split
   * well into that number.
   * This value is determined at the beginning of \c Execute(). */
  ThreadIdType                             m_NumberOfWorkUnitsUsed{0};
  ThreadIdType                             m_NumberOfWorkUnits;
  typename DomainPartitionerType::Pointer  m_DomainPartitioner;
  DomainType                               m_CompleteDomain;
  MultiThreaderBase::Pointer               m_MultiThreader;
};

}

#ifndef ITK_MANUAL_INSTANTIATION
#include "itkDomainThreader.hxx"
#endif

#endif
