#ifndef __DW_IMGBUF_HH__
#define __DW_IMGBUF_HH__

#ifndef __INCLUDED_FROM_DW_CORE_HH__
#   error Do not include this file directly, use "core.hh" instead.
#endif

#include "../lout/debug.hh"

namespace dw {
namespace core {

/**
 * \brief The platform independent interface for image buffers.
 *
 * %Image buffers depend on the platform (see \ref dw-images-and-backgrounds),
 * but have this general, platform independent interface. The purpose of
 * an image buffer is
 *
 * <ol>
 * <li> storing the image data,
 * <li> handling scaled versions of this buffer, and
 * <li> drawing.
 * </ol>
 *
 * The latter must be done independently from the window.
 *
 * <h3>Creating</h3>
 *
 * %Image buffers are created by calling dw::core::Platform::createImgbuf.
 *
 * <h3>Storing %Image Data</h3>
 *
 * dw::core::Imgbuf supports five image types, which are listed in the table
 * below. The representation defines, how the colors are stored within
 * the data, which is passed to dw::core::Imgbuf::copyRow.
 *
 * <table>
 * <tr><th>Type (dw::core::Imgbuf::Type) <th>Bytes per
 *                                           Pixel <th>Representation
 * <tr><td>dw::core::Imgbuf::RGB           <td>3   <td>red, green, blue
 * <tr><td>dw::core::Imgbuf::RGBA          <td>4   <td>red, green, blue, alpha
 * <tr><td>dw::core::Imgbuf::GRAY          <td>1   <td>gray value
 * <tr><td>dw::core::Imgbuf::INDEXED       <td>1   <td>index to colormap
 * <tr><td>dw::core::Imgbuf::INDEXED_ALPHA <td>1    <td>index to colormap
 * </table>
 *
 * The last two types need a colormap, which is set by
 * dw::core::Imgbuf::setCMap, which must be called before
 * dw::core::Imgbuf::copyRow. This function expects the colors as 32 bit
 * unsigned integers, which have the format 0xrrbbgg (for indexed
 * images), or 0xaarrggbb (for indexed alpha), respectively.
 *
 *
 * <h3>Scaling</h3>
 *
 * The buffer with the original size, which was created by
 * dw::core::Platform::createImgbuf, is called root buffer. Imgbuf provides
 * the ability to scale buffers. Generally, both root buffers, as well as
 * scaled buffers, may be shared, memory management is done by reference
 * counters.
 *
 * Via dw::core::Imgbuf::getScaledBuf, you can retrieve a scaled buffer.
 * Generally, something like this must work always, in an efficient way:
 *
 * \code
 * dw::core::Imgbuf *curBuf, *oldBuf;
 * int width, height,
 * // ...
 * oldBuf = curBuf;
 * curBuf = oldBuf->getScaledBuf(oldBuf, width, height);
 * oldBuf->unref();
 * \endcode
 *
 * \em oldBuf may both be a root buffer, or a scaled buffer.
 *
 * The root buffer keeps a list of all children, and all methods
 * operating on the image data (dw::core::Imgbuf::copyRow and
 * dw::core::Imgbuf::setCMap) are delegated to the scaled buffers, when
 * processed, and inherited, when a new scaled buffer is created. This
 * means, that they must only be performed for the root buffer.
 *
 * A possible implementation could be (dw::fltk::FltkImgbuf does it this way):
 *
 * <ul>
 * <li> If the method is called with an already scaled image buffer, this is
 *      delegated to the root buffer.
 *
 * <li> If the given size is the original size, the root buffer is
 *      returned, with an increased reference counter.
 *
 * <li> Otherwise, if this buffer has already been scaled to the given
 *      size, return this scaled buffer, with an increased reference
 *      counter.
 *
 * <li> Otherwise, return a new scaled buffer with reference counter 1.
 * </ul>
 *
 * Special care is to be taken, when the root buffer is not used anymore,
 * i.e. after dw::core::Imgbuf::unref the reference counter is 0, but there
 * are still scaled buffers. Since all methods operating on the image data
 * (dw::core::Imgbuf::copyRow and dw::core::Imgbuf::setCMap) are called for
 * the root buffer, the root buffer is still needed, and so must not be
 * deleted at this point. This is, how dw::fltk::FltkImgbuf solves this
 * problem:
 *
 * <ul>
 * <li> dw::fltk::FltkImgbuf::unref does, for root buffers, check, not only
 *      whether dw::fltk::FltkImgbuf::refCount is 0, but also, whether
 *      there are children left. When the latter is the case, the buffer
 *      is not deleted.
 *
 * <li> There is an additional check in dw::fltk::FltkImgbuf::detachScaledBuf,
 *      which deals with the case, that dw::fltk::FltkImgbuf::refCount is 0,
 *      and the last scaled buffer is removed.
 * </ul>
 *
 * In the following example:
 *
 * \code
 * dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
 * dw::core::Layout *layout = new dw::core::Layout (platform);
 *
 * dw::core::Imgbuf *rootbuf =
 *    layout->createImgbuf (dw::core::Imgbuf::RGB, 100, 100);
 * dw::core::Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
 * rootbuf->unref ();
 * scaledbuf->unref ();
 * \endcode
 *
 * the root buffer is not deleted, when dw::core::Imgbuf::unref is called,
 * since a scaled buffer is left. After calling dw::core::Imgbuf::unref for
 * the scaled buffer, it is deleted, and after it, the root buffer.
 *
 * <h3>Drawing</h3>
 *
 * dw::core::Imgbuf provides no methods for drawing, instead, this is
 * done by the views (implementation of dw::core::View).
 *
 * There are two situations, when drawing is necessary:
 *
 * <ol>
 * <li> To react on expose events, the function dw::core::View::drawImage
 *      should be used, with the following parameters:
 *      <ul>
 *      <li> of course, the image buffer,
 *      <li> where the root of the image would be displayed (as \em xRoot
 *           and \em yRoot), and
 *      <li> the region within the image, which should be displayed (\em x,
 *           \em y, \em width, \em height).
 *      </ul>
 *
 * <li> When a row has been copied, it has to be drawn. To determine the
 *      area, which has to be drawn, the dw::core::Imgbuf::getRowArea
 *      should be used. The result can then passed
 *      to dw::core::View::drawImage.
 * </ol>
 *
 * \sa \ref dw-images-and-backgrounds
 */
class Imgbuf: public lout::object::Object, public lout::signal::ObservedObject
{
public:
   enum Type { RGB, RGBA, GRAY, INDEXED, INDEXED_ALPHA };

   inline Imgbuf () {
      DBG_OBJ_CREATE ("dw::core::Imgbuf");
      DBG_OBJ_BASECLASS (lout::object::Object);
      DBG_OBJ_BASECLASS (lout::signal::ObservedObject);
   }

   inline ~Imgbuf () {
      DBG_OBJ_DELETE ();
   }

   /*
    * Methods called from the image decoding
    */

   virtual void setCMap (int *colors, int num_colors) = 0;
   virtual void copyRow (int row, const byte *data) = 0;
   virtual void newScan () = 0;

   /*
    * Methods called from dw::Image
    */

   virtual Imgbuf* getScaledBuf (int width, int height) = 0;
   virtual void getRowArea (int row, dw::core::Rectangle *area) = 0;
   virtual int getRootWidth () = 0;
   virtual int getRootHeight () = 0;


   /**
    * Creates an image buffer with same parameters (type, gamma etc.)
    * except size.
    */
   virtual Imgbuf *createSimilarBuf (int width, int height) = 0;

   /**
    * Copies another image buffer into this image buffer.
    */
   virtual void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
                        int xSrc, int ySrc, int widthSrc, int heightSrc) = 0;

   /*
    * Reference counting.
    */

   virtual void ref () = 0;
   virtual void unref () = 0;

   /**
    * \todo Comment
    */
   virtual bool lastReference () = 0;


   /**
    * \todo Comment
    */
   virtual void setDeleteOnUnref (bool deleteOnUnref) = 0;

   /**
    * \todo Comment
    */
   virtual bool isReferred () = 0;
};

} // namespace core
} // namespace dw

#endif // __DW_IMGBUF_HH__
