#ifndef GFD_H_
#define GFD_H_


#include <stdarg.h>
#include <stdint.h>

#include "gfd_framebuffer.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef uint8_t gfd_pixel_t;

typedef struct gfd_framebuffer_struct_t gfd_framebuffer_t;
typedef struct gfd_font256_tag_t gfd_font256_t;

typedef enum {
  gfd_ok=0,

  gfd_err_null_fb,
  gfd_err_inv_fb,
  gfd_err_inv_buffer,
  gfd_err_inv_width,
  gfd_err_inv_height,
  gfd_err_inv_pitch,
  gfd_err_inv_x,
  gfd_err_inv_y

} gfd_errorno_t;

typedef struct {
  int x;
  int y;
} gfd_point_t;

typedef struct {
  gfd_point_t p0;
  gfd_point_t p1;
} gfd_rect_t;

extern int gfd_fb_init(gfd_framebuffer_t* const fb, int width, int height, int pitch, gfd_pixel_t* const buffer);
extern int gfd_fb_subframe(gfd_framebuffer_t* const fb, const gfd_framebuffer_t* const src, int ox, int oy, int width, int height);
extern int gfd_fb_subbbox(gfd_framebuffer_t* const fb, const gfd_framebuffer_t* const src, int ox, int oy, int width, int height);

extern void gfd_fb_clear(gfd_framebuffer_t* const fb);
extern void gfd_fb_clear_phy(gfd_framebuffer_t* const fb);
extern void gfd_fb_fill(gfd_framebuffer_t* const fb, int c);
extern void gfd_fb_limit_rect(gfd_rect_t* rect, const gfd_framebuffer_t* const fb);
extern void gfd_clip_rect(gfd_rect_t* const rect, int x0, int y0, int x1, int y1);

extern void gfd_fb_set_pixel(const gfd_framebuffer_t* const fb, int x, int y, int c);
extern int gfd_fb_get_pixel(const gfd_framebuffer_t* const fb, int x, int y);
extern void gfd_fb_set_pixel2(const gfd_framebuffer_t* const fb, int x, int y, int c);
extern int gfd_fb_get_pixel2(const gfd_framebuffer_t* const fb, int x, int y);
extern gfd_pixel_t* gfd_fb_get_pixel_address(const gfd_framebuffer_t* const fb, int x, int y);

extern void gfd_fb_blit(gfd_framebuffer_t* const fb, int x, int y, const gfd_framebuffer_t* const src, int w, int h, int src_x, int src_y);
extern void gfd_fb_draw_sprite(gfd_framebuffer_t* const fb, int x, int y, gfd_framebuffer_t* const src, int color, int drawOcclusion);
extern void gfd_fb_draw_sprite_occ_ex(gfd_framebuffer_t* const fb, int x, int y, gfd_framebuffer_t* const src, int color, int drawOcclusion);

extern int gfd_fb_fill_rect(gfd_framebuffer_t* const fb, int x, int y, int w, int h, int c);
extern int gfd_fb_fill_rect_bordered(gfd_framebuffer_t* const fb, int x, int y, int w, int h, int c, int border_c);

extern int gfd_fb_printf(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, ...);
extern int gfd_fb_vprintf(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list);


extern int gfd_fb_text_measure(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, ...);
extern int gfd_fb_text_vmeasure(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list);
extern int gfd_font_height(const gfd_font256_t* const font);
extern int gfd_font_occl_height(const gfd_font256_t* const font);

extern void gfd_fb_draw_line(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color);
extern void gfd_fb_draw_line_w1(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color, int occolor, bool occlusion);
extern void gfd_fb_draw_line_w2(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color, int occolor, bool occlusion);
extern void gfd_fb_draw_math_line(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int width, int color);

extern void gfd_fb_draw_rect(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color);
extern void gfd_fb_draw_rect_limH(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color);
extern void gfd_fb_draw_rect_limV(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color);

//extern int gfd_line_clip(gfd_framebuffer_t* const fb, int* const x0, int* const y0, int* const x1, int* const y1);

extern void gfd_fb_draw_hline(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int color);
extern void gfd_fb_draw_vline(gfd_framebuffer_t* const fb, int sx, int sy, int ey, int color);
extern void gfd_fb_draw_hline_lim(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int color);
extern void gfd_fb_draw_vline_lim(gfd_framebuffer_t* const fb, int sx, int sy, int ey, int color);

extern void gfd_fb_draw_circle(gfd_framebuffer_t* const fb, int x, int y, int radius, int color);
extern void gfd_fb_draw_circle_2cols(gfd_framebuffer_t* const fb, int x, int y, int radius, int color1, int color2);

/*the draw arc179 is optimized to draw an arc that span less then 179deg.*/
extern void gfd_fb_draw_arc179(gfd_framebuffer_t* const fb, int x, int y, int radius, double angle_start, double angle_len, int color);
extern int gfd_count_circle_pix(int radius);
extern void gfd_fb_draw_arc179_w2_occ(gfd_framebuffer_t* const fb, int x, int y, int radius, double angle_start, double angle_len, int color, int occolor, bool occlusion, int cnt);

extern void gfd_fb_draw_arc_bb(gfd_framebuffer_t* const fb, gfd_rect_t* bb, int x, int y, int radius, double angle_start, double angle_len, int color);
extern void gfd_calc_arc_bb(gfd_rect_t* const bb, int x0, int y0, int radius, double angle_start, double angle_stop);
extern void gfd_fb_draw_arc_(gfd_framebuffer_t* const fb, int x0, int y0, int radius, double angleStart, double angleStop, int color);

/* "*_cs": "color shift", to implement a fast alpha blending based on the 256 colors palette:
 *   destination[x][y]=destination[x][y] + color
 * Note that the memory fb doesn't know nothing about the palette. The real work on color is left to the physical device
 */
extern int gfd_fb_printf_cs(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, ...);


extern void gfd_fb_set_pixel_cs(const gfd_framebuffer_t* const fb, int x, int y, int c);
extern void gfd_fb_draw_rect_cs(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color);
extern int gfd_fb_fill_rect_cs(gfd_framebuffer_t* const fb, int x, int y, int w, int h, int c);
extern void gfd_fb_draw_line_cs(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color);
extern void gfd_fb_draw_line_cs_2cols(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color1, int color2);
extern int gfd_fb_vprintf_cs(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list args);
extern int gfd_textLen(const char* text, int maxCnt);
extern char* gfd_textCpy(char *dest, const char *source, int count);

/*make_occlusion: a very slow routine to add occlusion to an existing framebuffer*/
extern int gfd_fb_make_occlusion(gfd_framebuffer_t* const fb, int color);
extern int gfd_fb_make_occlusion_mask(gfd_framebuffer_t* const fb);
extern void gfd_fb_flood_triangle(gfd_framebuffer_t* const fb, int x, int y, int color);

//Remove isolated holes in the bitmap
extern int gfd_fb_despackle_mask(gfd_framebuffer_t* const fb);

/* Some service routines/macros*/
#define gfd_min(a_, b_) ((a_)<(b_) ? a_ : b_)
#define gfd_abs(x_) ((x_)<0 ? -(x_) : x_)
#define gfd_nint(x_)  ((x_)<0 ? (int)((x_)-0.5) : (int)((x_)+0.5))
#define gfd_swap(a_, b_) ((void)((a_)=(a_)^(b_),(b_)=(a_)^(b_),(a_)=(a_)^(b_)))
#define gfd_round(x_) ((x_)<0 ? (int)((x_)-0.5) : (int)((x_)+0.5))
#define gfd_trunc(x_) ((int)(x_))

#ifdef __cplusplus
}
#endif

#endif /*GFD_H_*/
