#ifndef _RASTER_PIXMAP_H_
#define _RASTER_PIXMAP_H_

#include <sys/types.h>
#include <raster/geo.h>
#include <sys/cdefs.h>

/****************************************************************************************************************
 *
 * Pixmap operations
 *
 */
struct pixmap;
struct blitarea;

struct pixmap_operations {
  // Clear screen
  void (*cls)(struct pixmap *pix, uint pixel);

  // Draw a single pixel
  void (*setpixel)(uint8 *line, int x, uint pixel);
  uint (*getpixel)(const uint8 *line, int x);
  void (*plot)(struct pixmap *pix, int x, int y, uint pixel);

  // Draw a line
  void (*line)(struct pixmap *pix, int x1, int y1, int x2, int y2, uint pixel);
  void (*hline)(struct pixmap *pix, int x1, int x2, int y, uint pixel);
  void (*vline)(struct pixmap *pix, int x, int y1, int y2, uint pixel);

  // Rectangles
  void (*box)(struct pixmap *pix, int x, int y, int w, int h, uint pixel);
  void (*fill)(struct pixmap *pix, int x, int y, int w, int h, uint pixel);

  // Circles
  void (*circle)(struct pixmap *pix, int x0, int y0, int radius, uint pixel);
  void (*fill_circle)(struct pixmap *pix, int x0, int y0, int radius, uint pixel);

  // Copying
  void (*blit_store)(const struct blitarea *bla);
  void (*blit_masked)(const struct blitarea *bla);
  void (*blit_alpha)(const struct blitarea *bla);

  // Color conversion
  uint (*rgb2pixel)(uint);			// Converts a color into a pixel value
  uint (*pixel2rgb)(uint);			// Converts a pixel value into a color
};

/****************************************************************************************************************
 *
 * Pixmap
 *
 */
struct pixmap {
  uint pm_width;				// Geometry
  uint pm_height;
  uint32 pm_bytes_per_line;			// Bytes used by a single line
  uint32 pm_bytes_per_screen;			// Size of whole data buffer
  uint32 pm_fgpixel;				// Foreground color (Pixel value)
  uint32 pm_bgpixel;				// Background color (Pixel value)
  uint8  pm_bpp;				// Bits per pixel
  uint8  pm_color;				// 1 if display has color
  uint8  pm_pixelformat;			// Pixel format (see below)
  uint8  pm_blit_mode;				// Helps selecting the fastest blitter
  uint8  pm_alpha_mode;				// Store or blend

  uint8 *pm_data;				// Actual image bits
  uint8 *pm_alpha;				// Optional 8-bit alpha channel

  int pm_fd;					// Framebuffer device file descriptor

  struct pixmap_operations pm_ops;		// Drawing primitives
};

/****************************************************************************************************************/
// pm_alpha_mode: Determines how pixels are written to the pixmap

#define ALPHA_BLEND	0	// Alpha data of source pixel is used to blend source with destination
#define ALPHA_STORE	1	// Alpha data of source pixel overwrites alpha data of destination

void pixmap_set_alpha_mode(struct pixmap *pm, int mode);

/****************************************************************************************************************/
// pm_blit_mode: Determines how blitter uses the alpha data of source pixels

#define BLIT_BLEND	0	// Full alpha blending
#define BLIT_STORE	1	// Alpha data of source pixel ignored
#define BLIT_MASK	2	// All source pixels with alpha >= 0x80 are skipped

void pixmap_set_blit_mode(struct pixmap *pm, int mode);

/****************************************************************************************************************
 *
 * Animation
 *
 */
struct animation_frame {
  uint16 afr_x;					// Offset from top left of animation (If frame is smaller)
  uint16 afr_y;
  uint16 afr_delay;				// Time (ms) this frame is shown
  uint8 afr_disposal;				// Disposal method (same as in GIF files)
  uint8 afr_pad;
  struct pixmap *afr_pm;
};

struct animation {
  struct size ani_size;				// Nominal size of animation
  uint ani_count;				// Number of animations
  struct animation_frame ani_frame[0];		// List of frames making up the animation
};

struct animation_player {
  struct animation *apl_ani;			// Animation being played
  struct pixmap *apl_dst;			// Destination pixmap we're playing on
  struct pixmap *apl_buf;			// Double buffering
  struct pixmap *apl_prev;			// Previous frame
  struct pixmap *apl_bs;			// Stores background
  struct point apl_org;				// Position this is played
  uint apl_last_delay;				// Need to reuse last delay if a frame comes up with delay=0
  uint apl_last;				// Frame that was painted last go (>= count if none)
  uint apl_next;				// Next frame coming up
};

/****************************************************************************************************************/

struct pixel_format {
  uint8 red_max;
  uint8 green_max;
  uint8 blue_max;
  uint8 red_shift;
  uint8 green_shift;
  uint8 blue_shift;
};

/****************************************************************************************************************/
//
// Pixel format
//
// Note:
//   Blitter code assumes that 16bpp modes are organized such: BAAAAABBBBBCCCCC
//   In 24 and 32 bit modes, blitter assumes xxxxxxxxAAAAAAAABBBBBBBBCCCCCCCC
//   Where A, B & C denote one of the colors R, G & B, order is not important.
//
#define PXRGB_STRAIGHT		0x00	// Color
#define PXRGB_8BPP		0x01
#define PXRGB_16BPP_ATMEL_BGR	0x02
#define PXRGB_24BPP_ATMEL_BGR	0x03
#define PXRGB_16BPP_ATMEL_RGB	0x04

#define PXRGB_18BPP_SHIFTED	0x05	// 18 bits per pixel, 666, use bits 0..5 per byte

#define PXRGB_1BPP		0x40	// Monochrome or Grayscale
#define PXRGB_2BPP_GRAY		0x41
#define PXRGB_4BPP_GRAY		0x42

#define PXRGB_MONOCHROME_FLAG	0x40

// As parameter to pixmap_alloc(), auto decide from bits-per-pixel
#define PXRGB_AUTO		 ~0U

/****************************************************************************************************************/
//
// Pixmap creation
//
__BEGIN_DECLS
struct pixmap *pixmap_alloc(int width, int height, uint bpp, uint format, uint with_mask);
struct pixmap *pixmap_alloc_like_parent(struct pixmap *parent, int width, int height, uint with_mask);
struct pixmap *pixmap_window(struct pixmap *parent, int x, int y, int width, int height);
void pixmap_init(struct pixmap *pm);
void pixmap_free(struct pixmap *pm);

const struct pixel_format *pixmap_pixel_format(struct pixmap *pm);
__END_DECLS

/****************************************************************************************************************/
//
// Pixmap on hardware framebuffer
//
__BEGIN_DECLS
struct pixmap *pixmap_open(const char *dev);
struct pixmap *pixmap_fdopen(int fd);
int pixmap_resize(struct pixmap *pm, int width, int height, int bpp);
int pixmap_setup(struct pixmap *pm, unsigned int display_id);
int pixmap_move(struct pixmap *pm, uint x, uint y);
int pixmap_move_point(struct pixmap *pm, struct point *pt);

// True if pixmap is on a physical device and all changes are immediately visible.
static inline __attribute__((always_inline)) int pixmap_is_physical(struct pixmap *pm) { return pm->pm_fd >= 0; }

// Flush data cache, need to call on hardware framebuffer after major screen updates.
void pixmap_flush(struct pixmap *pm);
void pixmap_fdflush(int fd);
__END_DECLS

/****************************************************************************************************************/

struct imageinfo {
  uint   width;				// Image size
  uint   height;
  uint16 palette_size;			// Number of palette entries
  uint16 flags;				// Properties of image
  uint8  bpp;				// Bits per pixel
  uint8  filetype;			// Type of file image is loaded from
};

#define IMGFILE_NONE		0
#define IMGFILE_BMP		1
#define IMGFILE_GIF		2
#define IMGFILE_PNG		3
#define IMGFILE_JPG		4

#define IMGFLAG_TRANSPARENCY	1	// File format supports a transparent color
#define IMGFLAG_ALPHA		2	// File format supports an alpha channel

/****************************************************************************************************************/
//
// Image file loading
//
#define TRANSPARENCY_FROM_IMAGE	(-2)		// Determine transparency from image
#define TRANSPARENCY_NONE	(-1)		// Do not load transparency information

__BEGIN_DECLS
// Load info about an image from a file
int raster_load_info(struct imageinfo *iif, const char *filename);

// Load image from a file into <pm> at <x>,<y>, Pixel with RGB color <transparent> is considered transparent.
int raster_load(struct pixmap *pm, const char *filename, int x, int y, uint transparent);

// Load image from a file into newly allocated pixmap that has organization like <parent> (bpp, pixelformat).
struct pixmap *raster_load_pixmap(struct pixmap *parent, const char *filename, int transparent);

//
// Animation
//

// Load animation sequence from a file
struct animation *ani_load(struct pixmap *parent, const char *filename, int transparent);

// Free resources
void ani_free(struct animation *ani);

//
// Animation player
//

// Init playback
struct animation_player *apl_init(struct animation *ani, struct pixmap *dst, uint x, uint y);

// Play a frame
int apl_next(struct animation_player *apl);

// Remove animation and restore save under
void apl_restore(struct animation_player *apl);

// "Stop" animation -- Restore playfield if not already stopped
void apl_stop(struct animation_player *apl);

// Drop a player
void apl_free(struct animation_player *apl);

__END_DECLS

/****************************************************************************************************************/
//
// Image file saving
//
__BEGIN_DECLS

typedef int (raster_save_t)(void *data, const void *savedata, size_t savelen);

struct raster_save {
  const char *type;			// Image type

  raster_save_t *saver;			// Callback for writing data
  void *saver_data;

  int x, y, w, h;			// Range. If w or h is zero, use full size instead.

  uint32 args[4];			// Type dependent arguments
};

int raster_save(struct raster_save *rs, struct pixmap *pm);
int raster_save_file(struct raster_save *rs, struct pixmap *pm, const char *filename);
int raster_save_fd(struct raster_save *rs, struct pixmap *pm, int fd);

__END_DECLS

/****************************************************************************************************************/

#include <raster/primitives.h>

/****************************************************************************************************************/

static inline uint pixmap_rgb2pixel(struct pixmap *pm, uint rgb) __attribute__((always_inline));
static inline uint pixmap_rgb2pixel(struct pixmap *pm, uint rgb) {
  return pm->pm_ops.rgb2pixel(rgb);
}

/****************************************************************************************************************/

static inline uint pixmap_pixel2rgb(struct pixmap *pm, uint rgb) __attribute__((always_inline));
static inline uint pixmap_pixel2rgb(struct pixmap *pm, uint rgb) {
  return pm->pm_ops.pixel2rgb(rgb);
}

/****************************************************************************************************************/
//
// The grayscale is mixed using factors taken from PAL: R=0.299 G=0.587 B=0.114
// Together with the initial ranges of RGB, the result is 0..0xFFFFFF
//
static inline uint pixmap_rgb2gray(uint rgb) {
  return ((rgb & 0xFF) * 19672) + (((rgb >> 8) & 0xFF) * 38621) + (((rgb >> 16) & 0xFF) * 7500);
}

/****************************************************************************************************************/
#endif
