/*
  libuta - a C++ widget library based on SDL (Simple Direct Layer)
  Copyright (C) 1999-2002  Karsten Laux

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  Boston, MA  02111-1307, SA.
*/
// written by Karsten Laux, June 1999  

#ifndef _SURFACE_H_
#define _SURFACE_H_

#include <SDL/SDL.h>

#include "color.h"
#include "point.h"
#include "pixelformat.h"
#include "rect.h"


namespace uta {

  /** type definition for gamma function. 
 *  The "texture" always has to have a size of 256*256 values !
 */
typedef struct
{
  char* values;            // array of 256*256 values 
  unsigned char offset_x;  // offset to use
  unsigned char offset_y;
} GammaFunction;

///
class Surface
{
 public:

  ///
  friend class Painter;
  ///
  friend class Application;
  ///
  friend class DirectSurface;

  ///create an empty surface
  Surface();
  ///
  Surface(unsigned w, unsigned h, int pixelformat=Pixelformat::ABGR8888);
  /// 
  Surface(const Surface& surf);
  /** Create a surface with the given pixel data.
      The pixels are _not_ copied ! This uses interally SDL_CreateRGBSurfaceFrom(..).
      When this surface gets deleted, the pixel data gets not freed, because
      it is not owned by this surface.
  */
  Surface(void* pixels, unsigned w, unsigned h, unsigned pitch, int pixelformat);

  /** Create a child surface.
   * This is a dangerous feature ... the created surface is only valid as long
   *  as the parent surface is alive. If you delete the parent surface and after 
   *  that access the child surface bad things might happen !
   *  Deleting the child surface after the parent surface is ok, though.
   *  ( ... I guess I need to add reference counting ...)
   */
  Surface* createChildSurface(const Rect& rect);

  virtual ~Surface();
  /** Read from included XPM.
   *  Use #include "image.xpm" to compile images into your binaries.
   *  You may then construct your surface by surface.readFromXPM(image_xpm).
   */  
  bool readFromXPM(char** data);
  /** Read from included gimp header file.
   *  This is the alternative to readFromXPM; it uses header files
   *  generated by GIMP.
  */
  bool readFromHeader(char* header_data, unsigned w, unsigned h);
  /// Load surface from a PNG or BMP file.
  bool readFromFile(const std::string&);
  /** Load surface from a PNG or BMP file.
   */
  bool load(const std::string& filename) { return readFromFile(filename); };
  ///Write surface to a png formatted file.
  bool writeToFile(const std::string&);
  /** Write surface to a png formatted file.
   */
  bool write(const std::string& filename) { return writeToFile(filename); };

  ///
  Surface& operator = (const Surface& surf);
  /** Change palette of this surface.
   *  This does not change the pixel data !
  */
  void setPalette(const std::vector<Color>& pal);

  /** check for existing pixeldata.
   *  An empty surface results from an unsuccessfull file read for example.
  */
  bool empty() const { return sdlSurface_ == 0; };
  ///
  virtual int width() const { return ((sdlSurface_ == 0) ? 0 : sdlSurface_->w); };
  ///
  virtual int height() const { return ((sdlSurface_ == 0) ? 0 : sdlSurface_->h); };
  ///return surface pitch 
  virtual int pitch() const { return ((sdlSurface_ == 0) ? 0 : sdlSurface_->pitch);};

  ///return pixelformat of this surface
  const Pixelformat& pixelformat() const { return format; };

  /**Convert surface into given pixelformat.
   * Since version 0.3.37 a instance of Pixelformat is passed; by this
   * the palette of the target format can be set !
   * Per default floyd-steinberg dithering is used.
   */
  virtual bool convert(const Pixelformat& pixelformat, bool dither = true);
  
  ///mirror this surface horizontally
  bool mirror();
  ///scale this surface to the new size
  bool scale(int new_width, int new_height);
  ///scale this surface by given factor
  bool scale(float n);

  /// lighten (g > 0) or darken (g < 0) the surface
  void lighten_darken(char g);
  void lighten_darken(const GammaFunction *gamma);

  ///enable transparent blits
  void setTransparency(bool transp);
  ///set transparent color (enables transparent blits)
  void setTransColor(const Color& color);
  ///
  const Color& transparentColor() const { return transCol_; };

  ///set alpha in procent (0..100)
  void setAlpha(unsigned char alpha);
  ///
  bool usesTransparency() const { return transparent_; };
  ///check if surface contains an alpha channel
  bool hasAlphaChannel() const { return (format.aMask() != 0);};
  ///
  void useRLEAcceleration(bool flag) {RLEAccelerated_ = flag;};
  
  ///
  virtual Rect blit(Surface*) const;
  ///
  virtual Rect blit(Surface*, const Rect& dest) const;
  ///
  virtual Rect blit(Surface*, const Rect& dest, const Rect& src) const;
  ///
  virtual Rect scaledBlit(Surface*) const;
  ///
  virtual Rect scaledBlit(Surface*, const Rect& dest) const;
  ///
  virtual Rect scaledBlit(Surface*, const Rect& dest, const Rect& src) const;
  ///
  virtual Rect textureBlit(Surface*, 
			   const Point&, const Point&,
			   const Point&, const Point&) const;
  ///
  virtual Rect textureBlit(Surface*, 
			   const Point&, const Point&,
			   const Point&, const Point&, const Rect& src) const;
  

  ///clear surface to background color 
  void clear();
  ///clear given rect
  void clear(const Rect& r) { fill(r, transCol_);};
  ///fill whole surface with the given color
  void fill(const Color&);
  ///fill the given area
  void fill(const Rect&, const Color&);
 
  /**this allows pixelaccess.... you should know what you do here.
   * pixels accesses should be enclosed in lock()/unlock() pairs
  */
  void* pixels() 
    {  return ((sdlSurface_ == NULL) ? 0 : sdlSurface_->pixels); };
  /**this allows pixelaccess.... you should know what you do here.
   * pixels accesses should be enclosed in lock()/unlock() pairs
  */
  const void* pixels() const 
    {  return ((sdlSurface_ == NULL) ? 0 : sdlSurface_->pixels); };

  /**lock pixel data for direct access.
   * This is also need for pure read access, so it seems 
   * acceptable to make lock() a const method.
   */
  virtual void lock() const ;
  /**unlock pixel data.
   * Never forget to call this after a lock(), otherwise the application 
   * might run into a deadlock.
   */
  virtual void unlock() const;

 protected:
  ///delete surface data
  void clearSurfaceData();
  ///
  Pixelformat format;
  ///
  SDL_Surface* sdlSurface_;
  ///
  Color transCol_;
  ///
  bool transparent_;
  ///
  unsigned char alpha_;  
  ///pixel address in bytes
  void writePixel(Uint32 pixeladdr, Uint32 pixel);
  ///pixel address in bytes
  Uint32 readPixel(Uint32 pixeladdr);
  ///apply Surface's palette to the surface data
  void applyPalette();
  ///extract palette (if any) from the surface data
  void gatherPalette();
  ///
  bool RLEAccelerated_;

};

}

#endif
