/*
 * class ColorVideo
 *
 * $Date$
 * $Revision$
 *
 */

#ifndef COLOR_VIDEO_HH__
#define COLOR_VIDEO_HH__

#include "WImage/ColorImage.hh"
#include "WImage/VideoArray.hh"

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

/**
 * An wrapper for videos containing several color components.
 * Objects may import and export different video formats,
 * such as RAW. The color models can be changed (RGB or YUV).
 */
class 
ColorVideo : public ColorBuffer
{
public:
  /** Default Constructor. Creates objects and sets actual start values. */
  ColorVideo (void);
  /** Copy Constructor. 
   * @param cv the other color video */
  ColorVideo (const ColorVideo &cv);
  /** Constructor setting a particular size etc. Initialization not done.
   * @param colors
   * the number of colors 
   * @param colormodel
   * the initial color model
   * @param rows
   * the number of rows
   * @param cols
   * the number of cols
   * @param frames
   * the number of frames */
  ColorVideo (int colors, clrmodel colormodel, int rows, int cols, int frames);
  /** Destructor. Frees allocated objects. */
  virtual ~ColorVideo (void);

  /** Swap two color channels.
   * @exception invalid_argument if the channel specs are out of bounds
   * @param ch1 the first channel
   * @param ch2 the second channel */
  virtual void swapColors (int ch1, int ch2);

  /** Convert the video to a new colormodel. This applies the 
   * `ColorBuffer::colormodel()' operation on the whole video instead
   * of only the current frame.
   * @param model
   * the new colormodel ({\em cm_rgb} or {\em cm_yuv}) */
  void colormodelVideo (clrmodel model);

  /** Returns a reference to one of the color channels of the current frame.
   * {\em Don't change the dimensions of single channels unless you know
   * what you're doing!}
   * @param num the number of the color channel
   * @return the frame as a reference to an VideoFrame object */
  inline Image &currentFrameChannel (int num = 0) { return *m_images[num]; }

  /** Returns a reference to a color video containing the color channels 
   * of the current frame. 
   * @return the frame as a reference to an ColorImage object */
  inline ColorImage &currentFrame (void) { return *m_colorImage; }

  /** Returns the frame rate
   * @return the frameRate */
  inline double frameRate (void) const { return m_frameRate; }

  /** Sets the frame rate
   * @param fr the frameRate */
  void frameRate (double fr) { m_frameRate = fr; }

  /** Returns the number of the current frame.
   * @return the current frame (counted from 0) */
  inline int current (void) const { return m_current; }

  /** Returns the number of the current frame.
   * @return the current frame (counted from 0) */
  void current (int current);

  /** Returns the number of frames
   * @return the number of frames */
  inline int frames (void) const { return m_frames; }

  /** 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.*/
  void reframe (int frames, bool keephead);

  /** Resize the video. 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 */
  void resize (int rows, int cols);
  
#ifndef _WIN32_WCE
  /** Read the video. All steps independent of the file format will be 
   * performed, like testing for file readability etc. 
   * @exception invalid_argument
   * invalid file format
   * @exception ios_base::failure
   * a read error has occured [not supported by all libraries, so eventually 
   * {\em invalid_argument} instead] 
   * @param fname the file name to read from
   * @param rawy
   * the number of rows (only needed for RAW format)
   * @param rawx
   * the number of cols (only needed for RAW format) 
   * @param colors
   * the number of colors (if known, necessary for raw videos)
   * @param from
   * the frame to start with (from 0)
   * @param to
   * one greater than the last frame, if equal to `from' then the whole video
   * will be read, if 0, then the video will be read from `from' to the end
   * @param cmodel
   * the color model 
   * @param skip
   * the amount of bytes to skip as a header before contents */
  void read (char const *fname, int from = 0, int to = 0, 
             int rawy = 0, int rawx = 0, int colors = 3, 
             clrmodel cmodel = cm_rgb, int skip = 512);
  /** Read an video. Reads an video from a file using the specified 
   * file type. Currently PGM, RAW and PFI are supported.
   * @exception invalid_argument
   * the file type is either not supported or could not be determined
   * from the given file name
   * @param fname
   * the file name, if {\em NULL}, then {\em stdin}
   * @param ftype
   * the file type 
   * @param rawy
   * the number of rows (only needed for RAW format)
   * @param rawx
   * the number of cols (only needed for RAW format) 
   * @param colors
   * the number of colors (if known, necessary for raw videos)
   * @param from 
   * the frame to start with (from 0)
   * @param to
   * one greater than the last frame, if equal to `from' then the whole video
   * will be read, if 0, then the video will be read from `from' to the end
   * @param cmodel
   * the color model 
   * @param skip
   * the amount of bytes to skip as a header before contents */
  void read (char const *fname, filetype ftype, int from = 0, int to = 0, 
             int rawy = 0, int rawx = 0, int colors = 3, 
             clrmodel cmodel = cm_rgb, int skip = 512);

  /** Write the video. All steps independent of the file format will be 
   * performed, like testing for file writeability etc. 
   * @exception ios_base::failure
   * a read error has occured [not supported by all libraries, so eventually 
   * {\em invalid_argument} instead] 
   * @exception invalid_argument
   * the source video has a different number of colors than the target 
   * format allows
   * @param fname
   * the target file name
   * @param rawskip the amount of header area before contents (will be filled
   * with zeroes) 
   * @param beautify
   * beautify video that have not had more analysis than synthesis steps? */
  void write (char const *fname, int rawskip = 512, bool beautify = false);
  /** Write an video. Writes an video to a file using the specified file type.
   * @param ftype
   * the file type
   * @param fname
   * the file name, if {\em NULL}, then {\em stdout} 
   * @param rawskip the amount of header area before contents (will be filled
   * with zeroes) 
   * @param beautify
   * beautify videos that have not had more analysis than synthesis steps? */
  void write (char const *fname, filetype ftype, 
              int rawskip = 512, bool beautify = false);
#endif

  /** Returns the maximum value. Signs will be considered. 
   * @exception invalid_argument
   * empty video
   * @return
   * the maximum */
  virtual coeff smax (void) const;
  /** Returns the maximum value. Signs will be considered. 
   * @exception invalid_argument
   * empty video
   * @return
   * the maximum */
  virtual coeff smin (void) const;

  /** Compares two videos. Return {\em true} if both are equal.
   * @param buf
   * The other {\em ColorBuffer} object
   * @return 
   * if equals: {\em true}, else {\em false} */
  virtual bool equals (ColorBuffer &buf) const;
  /** Rough comparison. See if two videos are similar according to a given
   * {\em epsilon} (important for floating-point comparisons).
   * @param buf
   * the other {\em ColorBuffer} object
   * @param epsilon
   * the epsilon
   * @return
   * if both are identical: {\em true}, else {\em false} */
  virtual bool epsilons (ColorBuffer &buf, coeff epsilon) const;

  /** 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, coeff 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;

  /** Do a dumb kind of beautification by just truncating values to 
   * their possible maxima depending on the video format or whatever.
   * The original data will be overwritten, so this should only be 
   * used on clones or if the original data is not needed anymore.
   * @param min
   * the minimum allowed value
   * @param max
   * the maximum allowed value */
  virtual void truncate (coeff min = 0, coeff max = 255);
  
  /** Do an intelligent kind of beautification considering previous
   * transform steps. The original data will be overwritten, so this
   * should only be used on clones or if the video is not going to be
   * transformed afterwise. One region inside the video could be the 
   * low-pass subband and should thus be handled separately. If that is 
   * desired, the {\em ysize} and {\em xsize} parameters should be set
   * to nonzero values. 
   * Note that the current implementation is very memory consuming.
   * @param ysize
   * the horizontal size of the region that should be handled separately
   * @param xsize 
   * the vertical size of the region that should be handled separately
   * @param yoffs
   * the horizontal offset of the region that should be handled separately
   * @param xoffs 
   * the vertical offset of the region that should be handled separately */
  virtual void beautify (int yoffs = 0, int xoffs = 0,
                         int ysize = 0, int xsize = 0);
                         

  /** Return the average color for a rectangular region inside the video
   * drawn from one point within and the second point just outside the
   * region
   * @param fromY the first point's row
   * @param fromX the first point's col
   * @param toY the second point's row (-1 if lower video border)
   * @param toX the second point's col (-1 if right video border)
   * @return the average color */
  virtual coeff averageColor (int fromY = 0, int fromX = 0, 
                              int toY = -1, int toX = -1);

  /** Export the video for other applications. This only works if the video
   * has three channels!
   * @exception invalid_argument if the current colormodel or the one 
   * passed as parameter is unsupported.
   * @param cmodel the color model in which the video is exported. Currently
   * {\em cm_rgb} and {\em cm_yuv} are supported.
   * @return a new buffer containing the video pixels in the format according
   * on the cmodel parameter */
  virtual unsigned char *toPixmap (clrmodel cmodel);

  /** Produce a copy of this video
   * @return the copy */
  ColorVideo *clone (void) const;

#ifndef _WIN32_WCE
  /** Try to determine some of a video file's dimensions:
   * If the file is a raw video, two out of the three need to be 
   * specified, for AVI files all three can be determined from the header.
   * @exception invalid_argument if there was an error
   * @param fname the file name of the video
   * @param y the video's rows
   * @param x the video's columns
   * @param z the video's number of frames
   * @param colors the number of colors (always 3 with AVI files)
   * @param skip the size of the header (only raw videos) */
  static void videoDimensions (const char *fname, int &y, int &x, int &z, 
                               int colors, int skip = 0);
#endif

protected:
  /** A the number of frames */
  int m_frames;
  /** A counter for the current frame */
  int m_current;
  /** The video's total size. */
  int m_xyzsize;
  /** An array of {\em colors} Image objects */
  VideoArray < coeff > **m_arrays;
  /** All channels of the current frame bundled in a ColorImage object. */
  ColorImage *m_colorImage;
  /** The frame rate (if known) */
  double m_frameRate;
  /** Deletes allocated objects. */
  virtual void destroy (void);
  /** Compares two videos
   * @param cv the other video
   * @param epsilon the min difference 
   * @return true if they equal according to epsilon */
  bool epsilonsVideo (ColorVideo &cv, coeff epsilon) const;
  /** Allocate objects and set standard values.
   * @exception invalid_argument if any of the arguments does not make sense
   * @param colors
   * the number of colors 
   * @param cmodel
   * the initial color model
   * @param rows
   * the number of rows
   * @param cols
   * the number of cols
   * @param frames
   * the number of frames */
  void init (int colors, clrmodel cmodel, int rows, int cols, int frames);
}; /* class ColorVideo */

/** @} */

#endif /* COLOR_VIDEO_HH__ */

