/**============================================================================
 *
 * \file   discretize_filter.hpp
 * \brief  Declarations for the DiscretizeFilter class.
 *
 *
 * \author Leblanc Christophe.
 * \date   24/03/2013
 ** \email cleblancad@gmail.com
 *
 * Code inspired from:
 * 'Getting Started with ITK + VTK'
 * L. Ibanez, W. Schroeder, Insight Software Consortium.
 *
 *===========================================================================*/

#ifndef TOMOPROCESS_FILTER_DISCRETIZE_FILTER_HPP
#define TOMOPROCESS_FILTER_DISCRETIZE_FILTER_HPP

#include "filter.hpp"
#include "detail/discretize_filter.hpp"
#include <cmath>

TOMOPROCESS_OPEN_GLOBAL_NAMESPACE
TOMOPROCESS_OPEN_FILTER_NAMESPACE

/** \class DiscretizeFilter
 * \brief Discretizes ellipsoids and sends the result to a Gmsh 'geo' file.
 */
template<class ImageT, class MaximaInformationsContainerT, class ImageInformationsContainerT>
class DiscretizeFilter
  : public Filter<
      itk::DiscretizeFilter<
        typename ImageT::InputImageType,
	typename ImageT::OutputImageType,
	MaximaInformationsContainerT,
	ImageInformationsContainerT>,
      ImageT>
{
public:
  /// \brief This type.
  typedef DiscretizeFilter 	Self;
  
  /// \brief Parent class.
  typedef Filter<
      itk::DiscretizeFilter<
        typename ImageT::InputImageType,
	typename ImageT::OutputImageType,
	MaximaInformationsContainerT,
	ImageInformationsContainerT>,
      ImageT> SuperClass;
  
  /// \brief Image type.
  typedef ImageT 		ImageType;
  
  /// \brief Maxima informations container type.
  typedef typename SuperClass::ITKFilterType::MaximaInformationsContainerType
    MaximaInformationsContainerType;
    
  /// \brief Image informations container type.
  typedef typename SuperClass::ITKFilterType::ImageInformationsContainerType
    ImageInformationsContainerType;
    
  /// Enum for the mode of edge decimation.
  enum Mode
  {
    unknown = 0,
    absolute = 1,
    relative = 2
  };
  
  /// \brief Sets the parameters.
  /// \param[in] ellipsoidToleranceSize size tolerance for ellipsoids. Ellipsoids smaller than this will be erased.
  /// \param[in] maxInfos informations on maxima.
  /// \param[in] mode decimation mode for small edges and faces (absolute or relative).
  /// \param[in] smallEdgeTolerance edge lengths below this tolerance will be decimated.
  /// \param[in] smallfaceTolerance face area below this tolerance will be decimated.
  /// \param[in] wantedFoamDensity wanted foam density (\$\in [0, 1]\$).
  /// \param[in] materialDensity density of wall's material of the foam.
  /// \param[in] fileName output file name.
  /// \param[in] meshScale characteristic length for the mesh.
  /// \param[in] optimTolerance tolerance for the optimization method.
  /// \param[in] intersectionTolerance tolerance for intersecting meshes.
  /// \param[in] moveTolerance amount by which a point of an intersecting mesh will be moved.
  /// \param[in] maxIter maximum number of allowed iterations for the optimization method.
  /// \param[in] imagesInfos informations on images.
  /// \param[in] verbose verbose mode.
  /// \param[in] outputStream output stream for verbosity.
  virtual void SetParameters(double ellipsoidToleranceSize,
                             MaximaInformationsContainerType* maxInfos,
			     int mode,
			     double smallEdgeTolerance,
			     double smallFaceTolerance,
			     double wantedFoamDensity,
			     double materialDensity,
			     const std::string &fileName,
			     double meshScale = 20.0,
			     double optimTolerance = 1.0e-3,
			     double intersectionTolerance = 1.0e-3,
			     double moveTolerance = 0.1,
			     unsigned int maxIter = 100,
			     const ImageInformationsContainerType* imageInfos = NULL,
			     const bool verbose = false,
			     std::ostream &outputStream = std::cerr)
  {
    // Check if the tolerances are correctly set.
    if(ellipsoidToleranceSize < 0.0 || smallEdgeTolerance < 0.0 || smallFaceTolerance < 0.0 
        || wantedFoamDensity < 0.0 || materialDensity < 0.0 || optimTolerance < 0.0
        || intersectionTolerance < 0.0 || moveTolerance < 0.0 || maxIter <= 0)
    {
      throw itk::ExceptionObject(__FILE__, __LINE__, "Wrong parameter value in DiscretizeFilter");
      return;
    }
    
    if(moveTolerance > ellipsoidToleranceSize)
    {
      throw itk::ExceptionObject(__FILE__, __LINE__, "moveTolerance should be smaller the ellipsoidTolerance");
      return;
    }
    else if(moveTolerance < intersectionTolerance)
    {
      throw itk::ExceptionObject(__FILE__, __LINE__, "moveTolerance should be bigger or equal to intersectionTolerance");
      return;
    }
    
    // Computes the print precision in the Gmsh's file based on
    // the intersection tolerance. The print precision must be at least as high
    // as the tolerance, as some mesh points can be moved by an amount of tolerance.
    double minTolerance = std::min(smallFaceTolerance, smallEdgeTolerance);
    minTolerance = std::min(minTolerance, intersectionTolerance);
    
    double exp = (minTolerance == 0.0) ? 0.0 : std::floor(std::log10(fabs(minTolerance)));
    exp = fabs(exp);
    unsigned int printPrecision = static_cast<unsigned int>(exp) + 1u;
    printPrecision = std::max(printPrecision, 16u);
    
    this->m_Filter->SetEllipsoidToleranceSize(ellipsoidToleranceSize);
    this->m_Filter->SetMaximaInformations(maxInfos);
    this->m_Filter->SetMode(mode);
    this->m_Filter->SetSmallEdgeTolerance(smallEdgeTolerance);
    this->m_Filter->SetSmallFaceTolerance(smallFaceTolerance);
    this->m_Filter->SetWantedFoamDensity(wantedFoamDensity);
    this->m_Filter->SetMaterialDensity(materialDensity);
    this->m_Filter->SetFileName(fileName);
    this->m_Filter->SetScale(meshScale);
    this->m_Filter->SetOptimTolerance(optimTolerance);
    this->m_Filter->SetIntersectionTolerance(intersectionTolerance);
    this->m_Filter->SetMoveTolerance(moveTolerance);
    this->m_Filter->SetMaxIter(maxIter);
    this->m_Filter->SetImageInformations(imageInfos);
    this->m_Filter->SetPrintPrecision(printPrecision);
    this->m_Filter->SetVerbose(verbose);
    this->m_Filter->SetErrorStream(outputStream);
  }
};

TOMOPROCESS_CLOSE_FILTER_NAMESPACE
TOMOPROCESS_CLOSE_GLOBAL_NAMESPACE

#endif // TOMOPROCESS_FILTER_DISCRETIZE_FILTER_HPP