// -*- c++ -*-
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTableFFT.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

/*-------------------------------------------------------------------------
  Copyright 2009 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

/**
 * @class   vtkTableFFT
 * @brief   FFT for table columns
 *
 * vtkTableFFT performs the Fast Fourier Transform on the columns of a table.
 * It can perform the FFT per block : this performs something close to the
 * Welch method but it uses raw FFTs instead of periodograms. This allows to
 * reduce the impact of noise as well as speeding up the filter when the input
 * signal is too big.
 *
 * It is also possible to apply a window on the input signal. If performing
 * the FFT per block then the window will be applied to each block instead.
 *
 * The filter will look for a "Time" array (case insensitive) to determine the
 * sampling frequency. "Time" array is considered to have the same frequency
 * all along. If no "Time" array is found then the filter use the default frequency
 * value.
 *
 * This filter will not apply the FFT on any arrays which names begin with 'vtk'.
 */

#ifndef vtkTableFFT_h
#define vtkTableFFT_h

#include "vtkDeprecation.h"          // For VTK_DEPRECATED_IN_9_2_0
#include "vtkFiltersGeneralModule.h" // For export macro
#include "vtkSmartPointer.h"         // For internal method.
#include "vtkTableAlgorithm.h"

#include <memory> // For unique_ptr

class VTKFILTERSGENERAL_EXPORT vtkTableFFT : public vtkTableAlgorithm
{
public:
  vtkTypeMacro(vtkTableFFT, vtkTableAlgorithm);
  static vtkTableFFT* New();
  void PrintSelf(ostream& os, vtkIndent indent) override;

  enum // Windowing functions
  {
    HANNING = 0,
    BARTLETT,
    SINE,
    BLACKMAN,
    RECTANGULAR,

    MAX_WINDOWING_FUNCTION
  };

  ///@{
  /**
   * Specify if the output should be normalized. This has 2 consequences :
   * first is that for each block the mean signal value is removed from the input
   * signal before doing the actual FFT. Second is that it will take the norm of
   * the resulting imaginary values and normalize it using Parseval's theorem.
   *
   * Default is false
   */
  vtkGetMacro(Normalize, bool);
  vtkSetMacro(Normalize, bool);
  vtkBooleanMacro(Normalize, bool);
  ///@}

  ///@{
  /**
   * Specify if the input should be split in multiple blocks to compute
   * an average fft across all blocks. It uses the Welch method except
   * that it averages raw FFTs instead of periodograms.
   *
   * @see vtkTableFFT::SetNumberOfBlock(int)
   * @see vtkTableFFT::SetBlockSize(int)
   *
   * Default is false
   */
  vtkGetMacro(AverageFft, bool);
  virtual void SetAverageFft(bool);
  vtkBooleanMacro(AverageFft, bool);
  ///@}

  ///@{
  /**
   * Specify if the filter should use the optimized discrete fourier transform for
   * real values. This will cause output columns to have from n to ((n / 2) + 1) rows.
   *
   * Default is false
   */
  vtkGetMacro(OptimizeForRealInput, bool);
  vtkSetMacro(OptimizeForRealInput, bool);
  vtkBooleanMacro(OptimizeForRealInput, bool);
  ///@}

  ///@{
  /**
   * Specify if the filter should create a frequency column based on a column
   * named "time" (not case sensitive). An evenly-spaced time array is expected.
   *
   * @see vtkTableFFT::SetDefaultSampleRate(double)
   *
   * Default is false
   */
  vtkGetMacro(CreateFrequencyColumn, bool);
  vtkSetMacro(CreateFrequencyColumn, bool);
  vtkBooleanMacro(CreateFrequencyColumn, bool);
  ///@}

  ///@{
  /**
   * If the "Time" column is not found then this value will be used.
   * Expressed in Hz.
   *
   * Default is 10'000 (Hz)
   */
  vtkGetMacro(DefaultSampleRate, double);
  vtkSetMacro(DefaultSampleRate, double);
  ///@}

  ///@{
  /**
   * Only used if @c AverageFft is true
   *
   * Specify the number of blocks to use when computing the average fft over
   * the whole input sample array. Blocks can overlap if @c NumberOfBlock times
   * @c BlockSize is superior to the input signal size.
   *
   * This parameter is ignored if @c BlockSize is superior
   * to the number of samples of the input array.
   *
   * @see vtkTableFFT::SetAverageFft(bool)
   *
   * Default is 2
   */
  vtkGetMacro(NumberOfBlock, int);
  vtkSetMacro(NumberOfBlock, int);
  ///@}

  ///@{
  /**
   * Only used if @c AverageFft is true
   *
   * Specify the number of samples to use for each block. This should be a power of 2.
   * If not, the closest power of two will be used anyway.
   *
   * @see vtkTableFFT::SetAverageFft(bool)
   *
   * Default is 1024
   */
  vtkGetMacro(BlockSize, int);
  virtual void SetBlockSize(int);
  ///@}

  ///@{
  /**
   * Specify the windowing function to apply on the input.
   * If @c AverageFft is true the windowing function will be
   * applied per block and not on the whole input.
   *
   * Default is RECTANGULAR (does nothing)
   */
  vtkGetMacro(WindowingFunction, int);
  virtual void SetWindowingFunction(int);
  ///@}

  ///@{
  /**
   * Specify if output array should be prefixed by "FFT_" or not.
   * This behavior was introduced in v9.1. Default is false.
   *
   * Deprecated in v9.2.
   */
  VTK_DEPRECATED_IN_9_2_0(
    "Deprecated in favor of always keeping the output array names the same as the input.")
  vtkGetMacro(PrefixOutputArrays, bool);
  VTK_DEPRECATED_IN_9_2_0(
    "Deprecated in favor of always keeping the output array names the same as the input.")
  vtkSetMacro(PrefixOutputArrays, bool);
  ///@}

protected:
  vtkTableFFT();
  ~vtkTableFFT() override;

  int RequestData(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector) override;

  /**
   * Initialize the internal state before performing the actual fft.
   * This checks that the given parameters are coherent with the input and
   * tries to extract time information from a column.
   */
  void Initialize(vtkTable* input);

  /**
   * Perform the FFT on the given data array.
   */
  vtkSmartPointer<vtkDataArray> DoFFT(vtkDataArray* input);

private:
  vtkTableFFT(const vtkTableFFT&) = delete;
  void operator=(const vtkTableFFT&) = delete;

  bool Normalize = false;
  bool AverageFft = false;
  bool OptimizeForRealInput = false;
  bool CreateFrequencyColumn = false;
  int NumberOfBlock = 2;
  vtkIdType BlockSize = 1024;
  int WindowingFunction = RECTANGULAR;
  double DefaultSampleRate = 1e4;

  bool PrefixOutputArrays = false;

  struct vtkInternal;
  std::unique_ptr<vtkInternal> Internals;
};

#endif // vtkTableFFT_h
