/*
 * class WaveletTransform
 *
 * $Date$
 * $Revision$
 *
 */

/**
 * @addtogroup Wave
 * @{
 */

#ifndef WAVELET_TRANSFORM_HH__
#define WAVELET_TRANSFORM_HH__

#include "Wave/Wavelet.hh"
#include "WImage/CoeffInformation.hh"
#include "Wave/Filter.hh"

/**
 * An abstract Wavelet Transform. The Transform transform is two-dimensional,
 * it thus works on images.
 */
/*abstract*/
class
WaveletTransform
{
public:
  /** Constructor. Initializes references to Image and Wavelet.
   * @param img
   * the image
   * @param fil
   * the filter set for the wavelet */
  WaveletTransform (Image &img, FilterSet &fil);
  /** Destructor. Releases allocated objects. */
  virtual ~WaveletTransform (void);

  /** Perform a Wavelet transform on the image.
   * @exception invalid_argument
   * image is not square
   * @param steps
   * the number of transform steps */
  virtual void analysis (int steps);
  /** Perform an inverse Wavelet transform on the image.
   * @exception invalid_argument
   * image is not square
   * @param steps
   * the number of inverse transform steps
   * @param prevSteps
   * if greater than 0, the number of previous decomposition steps
   * to assume. This is necessary to reconstruct images that are
   * not square and have side lengths which are not powers of two. */
  virtual void synthesis (int steps, int prevSteps = 0);
  /** Temporarily expand the image (square, side length a power of two)
   * to make a transform possible. */
  void expandImage (void);
  /** Restore the temporarily expanded image back to original size. */
  void restoreImage (void);

  /** Identify position and size of a given channel in a given subband.
   * This default method of calculation refers to the Pyramid transform
   * and due to the lack of alternatives also to the Standard transform.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that
   * method.
   * @param what
   * the channel we want
   * @param subband
   * the subband we are looking at
   * @param yoffs
   * the returned row
   * @param xoffs
   * the returned col
   * @param ysize
   * the returned number of rows
   * @param xsize
   * the returned number of cols */
  virtual void where (area what, int subband, int &yoffs,
                      int &xoffs, int &ysize, int &xsize) const;

  /** Identify channel and subband of a given position in the transformed
   * image. This method depends on the `where' method and thus uses the
   * geometry associated with the `Pyramid' decomposition.
   * @param ypos
   * the location's row.
   * @param xpos
   * the location's column.
   * @param subband
   * where the calculated subband gets written to.
   * @param channel
   * where the calculated channel gets written to.
   * @param steps
   * the current number of transform steps to be assumed (-1 for automatic
   * determination) */
  virtual void where (int ypos, int xpos, int &subband, area &channel,
                      int steps = -1) const;

  /** Identify channel and subband of a given position in the transformed
   * image and map it to another subband/channel.
   * This default method of calculation refers to the Pyramid transform
   * and due to the lack of alternatives also to the Standard transform.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that
   * method.
   * @param yOld
   * the source location's row.
   * @param xOld
   * the source location's column.
   * @param toSubband
   * the target subband
   * @param toChannel
   * the target channel
   * @param yNew
   * where the target location's row gets written to.
   * @param xNew
   * where the target location's column gets written to.
   * @param sizeFactor
   * where the target sizeFactor gets written to.
   * @param steps
   * the current number of transform steps to be assumed (-1 for automatic
   * determination) */
  virtual void mapPosition (int yOld, int xOld, int toSubband,
                            area toChannel, int &yNew, int &xNew,
                            int &sizeFactor, int steps = -1) const;

  /** Return the average color for a particular area in a subband
   * inside the image. The coefficients' signedness will be considered.
   * This is a shortcut to the corresponding method in the Image class.
   * @param subband the subband
   * @param channel the area in that subband
   * @return the average color */
  virtual coeff saverage (int subband, area channel) const;

  /** Return the average color for a particular area in a subband
   * inside the image. The coefficients' signedness will be discarded.
   * This is a shortcut to the corresponding method in the Image class.
   * @param subband the subband
   * @param channel the area in that subband
   * @return the average color */
  virtual coeff aaverage (int subband, area channel) const;

  /** Return the square variance for a particular area in a subband
   * inside the image.
   * This is a shortcut to the corresponding method in the Image class.
   * @param subband the subband
   * @param channel the area in that subband
   * @return the square variance */
  virtual coeff sqvariance (int subband, area channel) const;

  /** Return the variance for a particular area in a subband
   * inside the image.
   * This is a shortcut to the corresponding method in the Image class.
   * @param subband the subband
   * @param channel the area in that subband
   * @param abs true if signs are discarded
   * @return the square variance */
  virtual coeff variance (int subband, area channel, bool abs = false) const;

  /** Returns the standard deviation for a particular area in a subband
   * inside the image.
   * This is a shortcut to the corresponding method in the Image class.
   * @param subband the subband
   * @param channel the area in that subband
   * @param abs true if signs are discarded
   * @return the standard deviation. */
  virtual coeff sdeviation (int subband, area channel, bool abs = false) const;

  /** Return the ratio resulting from the zerotree relationship between
   * two subbands (e.g. 1:4 from subband 1 to 2). This method depends on the
   * geometry associated with the `Pyramid' decomposition.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that
   * method.
   * @param subband1 the first subband
   * @param subband2 the second subband
   * @return the ratio, e.g. 4 for 1:4 */
  virtual coeff ratio (int subband1, int subband2);

  /** Get a subpicture containing the LL subband.
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  inline Image *ll (int steps = -1) { return subband (LL, steps); }
  /** Get a subpicture containing the HL subband.
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  inline Image *hl (int steps = -1) { return subband (HL, steps); }
  /** Get a subpicture containing the LH subband.
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  inline Image *lh (int steps = -1) { return subband (LH, steps); }
  /** Get a subpicture containing the HH subband.
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  inline Image *hh (int steps = -1) { return subband (HH, steps); }
  /**
   * Get an image consisting of the given subband's highpass components
   * maxima.
   * @param steps
   * assume number of transform steps instead of those computed from
   * @return a new image object containing the maxima as coefficients
   */
  Image *highMax(int steps = -1);
  /** Get a reference to the image
   * @return the reference to the image */
  inline Image &image (void) const { return *m_image; }

  /** Get a subpicture containing one of the subbands.
   * @param what
   * the subband, out of LL, HL, LH, HH
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  virtual Image *subband (area what, int steps = -1);

  /** Get the subband of a given position in the image. This
   * default method of calculation refers to the Pyramid transform and due
   * to the lack of alternatives also to the Standard transform.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that method.
   * @param ypos
   * the position's row
   * @param xpos
   * the position's col
   * @param steps
   * the number of steps we assume the image to have been transformed
   * @return
   * the position's subband */
  virtual int getSubband (int ypos, int xpos, int steps = -1);
  /** Get the subband of a given position in the image denoted by a
   * {\em CoeffInformation} object.
   * This default method of calculation refers to the Pyramid transform and
   * due * to the lack of alternatives also to the Standard transform.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that method.
   * @param c
   * a reference the {\em CoeffInformation} object.
   * @param steps
   * the number of steps we assume the image to have been transformed
   * @return
   * the position's subband */
  inline int getSubband (CoeffInformation &c, int steps = -1) {
    return getSubband (c.ypos (), c.xpos (), steps);
  }

  /** Get the area of a given position in the image. This
   * default method of calculation refers to the Pyramid transform and due
   * to the lack of alternatives also to the Standard transform.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that method.
   * @param ypos
   * the position's row
   * @param xpos
   * the position's col
   * @param steps
   * the number of steps we assume the image to have been transformed
   * @return
   * the position's subband */
  virtual area getArea (int ypos, int xpos, int steps = -1);
  /** Get the area of a given position in the image denoted by a
   * {\em CoeffInformation} object.
   * This default method of calculation refers to the Pyramid transform and
   * due * to the lack of alternatives also to the Standard transform.
   * Decomposition schemes using a different geometries, like e.g. the
   * Packet transform may have to implement their own version of that method.
   * @param c
   * a reference the {\em CoeffInformation} object.
   * @param steps
   * the number of steps we assume the image to have been transformed
   * @return
   * the position's subband */
  inline area getArea (CoeffInformation &c, int steps = -1) {
    return getArea (c.ypos (), c.xpos (), steps);
  }

  /** Import a subpicture containing one of the subbands.
   * @exception invalid_argument
   * the imported image does not match the calculated subband size
   * @param img
   * the image containing the subband
   * @param what
   * the subband, out of LL, HL, LH, HH
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  virtual void import (Image &img, area what, int steps = -1);

  /** Fill one of the subbands with one particular value.
   * @exception invalid_argument
   * the imported image does not match the calculated subband size
   * @param value
   * the value to be inserted
   * @param what
   * the subband, out of LL, HL, LH, HH
   * @param steps
   * assume number of transform steps instead of those computed from
   * the previous transforms */
  virtual void fill (coeff value, area what, int steps = -1);

  /** Return the current number of decomposition steps
   * @return the number of decomposition steps */
  inline int steps (void) const {
    return m_image->anasteps () - m_image->synsteps (); }

#ifdef USE_DEPRECATED_API
  inline void import (Image &img, area what, int steps = -1) {
    import &img, what, steps); }
#endif
private:
  /** Resize m_buffer for transforms if image dimensions have changed */
  void sanity (void);
protected:
  /** A reference to the wavelet to be used. */
  Wavelet *m_wavelet;
  /** A reference to the image to be used. */
  Image *m_image;
  /** A second image used as temporary m_buffer. */
  Image *m_buffer;
  /** A reference to the filter set used. */
  FilterSet *m_filter;
  /** The image's number of rows before resizing */
  int m_rows;
  /** The image's number of cols before resizing */
  int m_cols;

  /** Perform a Wavelet transform on the image.
   * @param steps
   * the number of transform steps */
  virtual void doanalysis (int steps) = 0;
  /** Perform an inverse Wavelet transform on the image.
   * @param steps
   * the number of inverse transform steps
   * @param prevSteps
   * if greater than 0, the number of previous decomposition steps
   * to assume. This is necessary to reconstruct images that are
   * not square and have side lengths which are not powers of two. */
  virtual void dosynthesis (int steps, int prevSteps = 0) = 0;

}; /* class WaveletTransform */

/** @} */

#endif /* WAVELET_TRANSFORM_HH__ */


