/*
 * class VideoArray
 *
 * $Date$
 * $Revision$
 *
 */

#ifndef VIDEO_ARRAY_HH__
#define VIDEO_ARRAY_HH__

#include "WImage/ImageArray.hh"

/** 
 * @addtogroup WImage 
 * @{ */

/**
 * A two-dimensional array wrapper. This allows us to access the video's
 * pixels or coefficients and provides some utility methods. It is the
 * main working horse for the Video framework.
 */ 
template<class T> class 
VideoArray : public ImageArray <T>
{
public:
  /** Constructor. Sets the dimension and allocates space. A size of 
   * zero is allowed, negative values not.
   * @param rows
   * the vertical size
   * @param cols
   * the horizontal size 
   * @param frames
   * the number of frames */
  VideoArray (int rows, int cols, int frames);
  VideoArray (int rows, int cols, int frames, const VideoArray<T>*v);
  /** Destructor. Releases allocated memory. */
  virtual ~VideoArray (void);

  /** Sets the current frame. Valid values are 0..n-1.
   * @param frame
   * the current frame 
   * @exception invalid_argument
   * the new frame is out of bounds */
  void current (int frame);

  /** Returns the number of the current frame. Counting starts from zero.
   * @return the number of the current frame. */
  inline int current (void) { return m_current; }
  
  /** Returns the number of frames
   * @return
   * the number of frames */
  inline int frames (void) const { return m_frames; }

  /** Returns the value at (x,y).
   * @param y
   * the row
   * @param x
   * the col
   * @return
   * the value */
  inline T at (int y, int x) const { return this->m_ar[abs (y, x)]; }
  /** Returns the current frame's {\em n}th value.
   * @param abs
   * the offset from the array start
   * @return
   * the value */
  virtual T at (int abs) const { 
    return this->m_ar[m_offset + abs]; 
  }
  /** Sets the value at (x,y).
   * @param y
   * the row
   * @param x
   * the col
   * @param val
   * the value */
  inline void to (int y, int x, T val) { this->m_ar[abs (y, x)] = val; }
  /** Sets the array's {\em n}th value.
   * @param abs
   * the offset from the array start
   * @param val
   * the value */
  virtual void to (int abs, T val) { this->m_ar[m_offset + abs] = val; }
  /** Return the absolute offset of a position.
   * @param y
   * the position's row
   * @param x
   * the position's col
   * @return
   * the absolute offset */
  virtual int abs (int y, int x) const { 
    return m_offset + this->m_rows[y] + x; 
  }

  /** Rough comparison. See if this and another frame are similar according 
   * to a given {\em epsilon} (important for floating-point comparisons). 
   * @exception invalid_argument 
   * one of the frames is out of bounds
   * @param f1
   * the first frame
   * @param f2
   * the second frame
   * @param epsilon
   * the epsilon
   * @return
   * if both are identical: {\em true}, else {\em false} */
  bool epsilonsFrames (int f1, int f2, T epsilon) const;

  /** Exact comparison. See if two frames are similar.
   * @exception invalid_argument 
   * one of the frames is out of bounds
   * @param f1
   * the first frame
   * @param f2
   * the second frame
   * if both are identical: {\em true}, else {\em false} */
  bool equalsFrames (int f1, int f2) const;

  /** Rough comparison. See if this and another video or the current and 
   * another frame are similar according to a given {\em epsilon} 
   * (important for floating-point comparisons). Whether the argument 
   * will be treated as a video or a frame depends on its type.
   * @param ia
   * the other {\em ImageArray} object
   * @param epsilon
   * the epsilon
   * @return
   * if both are identical: {\em true}, else {\em false} */
  virtual bool epsilons (ImageArray<T> &ia, T epsilon) const;

  /** Resize the array. The old values will be copied to the new
   * dimension as far as they fit in. The new dimensions must all be
   * greater than zero.
   * @exception invalid_argument
   * one or both dimensions are either negative or zero
   * @param rows
   * the new number of rows
   * @param cols
   * the new number of cols */
  virtual void resize (int rows, int cols);
  /** Sets new number of frames. The old frames will be copied to the new
   * ones as far as they fit in. The new number must be greater than zero.
   * @exception invalid_argument
   * the new number of frames is either negative or zero
   * @param frames
   * the new number of frames. 
   * @param keephead
   * if shrinking the video, frames will be deleted at the end.*/
  virtual void reframe (int frames, bool keephead = true);
  /** Import a raw array. The new dimensions and the new array will
   * be stored discarding the old ones.
   * @param rows
   * the new number of rows
   * @param cols
   * the new number of cols 
   * @param frames
   * the new number of frames
   * @param array
   * the new array */
  virtual void import (int rows, int cols, int frames, T *array);

  /** This method cannot be used on videos and will throw an invalid_argument.
   * @param rows
   * the new number of rows
   * @param cols
   * the new number of cols 
   * @param array
   * the new array 
   * @exception invalid_argument always */
  virtual void import (int rows, int cols, T *array);
  /** Create a copy of the whole video. All frames will be duplicated 
   * rather than the references.
   * @return
   * the new object */
  virtual VideoArray<T> *cloneVideo (void) const;
  /** Create a copy of the current frame. All frames will be duplicated 
   * rather than the references.
   * @return
   * the new object */
  virtual ImageArray<T> *clone (void) const;
  /** Copy from a different array. This includes resizing and reframing if
   * necessary.
   * @exception invalid_argument if the other object is not a VideoArray.
   * @param ia
   * the other array */
  virtual void copy (ImageArray<T> &ia);

protected:
  /** The video's number of frames. */
  int m_frames;
  /** The video's total size. */
  int m_xyzsize;
  /** The video's current frame number. */
  int m_current;
  /** The offset in the array to the current frame. */
  int m_offset;
  /** Rough comparison. See if this and another video are similar according 
   * to a given {\em epsilon} (important for floating-point comparisons). 
   * Called by `epsilons()'.
   * @param va
   * the other {\em ImageArray} object
   * @param epsilon
   * the epsilon
   * @return
   * if both are identical: {\em true}, else {\em false} */
  bool epsilonsVideo (VideoArray<T> &va, T epsilon) const;
}; /* class VideoArray */

#include "../VideoArray.cc"

/** @} */

#endif /* VIDEO_ARRAY_HH__ */

