#ifndef IM_H
#define IM_H

/***************/
/* BASIC TYPES */
/***************/
typedef char ImBool;

#ifndef TRUE
#define TRUE 1
#endif /* !TRUE */

#ifndef FALSE
#define FALSE 0
#endif /* !FALSE */


#ifdef IM_NO_XLIB
#define Pixmap int
/* ... */
#endif /* !IM_USE_XLIB */


/**********/
/* ERRORS */
/**********/
typedef enum {
  IM_ERROR_NONE,
  /* ... */
  IM_ERROR_LAST
} ImError;

char *im_strerror(ImError err);


/***********************/
/* GEOMETRY PRIMITIVES */
/***********************/
typedef struct {
  int x, y;
} ImPoint;

typedef struct {
  int w, h;
} ImSize;

typedef struct {
  ImPoint point;
  ImSize  size;
} ImRect;

typedef struct {
  int top, left, bottom, right;
} ImBorder;

typedef struct {
  unsigned char r, g, b, a;
} ImColor;

typedef struct {
  ImPoint point;
  ImColor color;
} ImPixel;

typedef ImPixel ImVertex;

typedef struct {
  ImSize size;
  void *mask;
} ImMask;


/***********/
/* CONTEXT */
/***********/
#define IM_CONTEXT(a) ((ImContext*) (a))
typedef struct _ImContext ImContext;
typedef struct _ImContext {
  char blend,
       antialias;
  /* FIXME: add more stuff here (but not too much stuff) */

  ImContext *next;
};

ImContext *im_context_new(void);
void im_context_free(ImContext *context);

void im_context_push(ImContext *context);
ImContext *im_context_pop(void);


/****************/
/* CLASS STRUCT */
/****************/
typedef int ImClassType;
#define IM_CLASS_TOPLEVEL -1

typedef IM_CLASS(a) ((ImClass*) (a))
typedef struct {
  ImClassType type,
              parent;
  char *name;

  /* private callbacks (internal to class) */
  void *(*dup)(void *object, void *data);
  Pixmap (*pixmap)(void *image, void *object, ImColor color);
  ImMask (*mask)(void *object);
  void (*resize)(void *object, ImSize size);
  void (*draw)(void *image, void *object, ImColor color, void *data);
  void (*finalize)(void *object, void *data);
} ImClass;

ImClassType im_class_add(ImClassType parent_class, char *name);
ImClassType im_class_get_name(ImClassType klass);
ImClassType im_class_get_parent(ImClassType klass);

ImBool im_class_is_a(ImClassType child_class, ImClassType parent_class);
ImClassType im_class_get_type(char *class_name);
ImClass *im_class_get(ImClassType klass);


/***************/
/* BASE OBJECT */
/***************/
#define IM_OBJECT(a) ((ImObject*) (a))
typedef struct {
  ImID        id;    /* object id */
  ImClassType klass; /* class (used for introspection, etc) */

  ImRect  rect;

  ImHash *user_data; /* user data associated with this object */
} ImObject;

void im_object_init(ImObject *object);
void im_object_free(void *object);

/* object class info (introspection) methods */
ImID im_object_get_id(ImObject *object);
ImClassType im_object_get_class(ImObject *object);

/* object rectangle methods */
ImRect im_object_get_rect(ImObject *object);
void im_object_set_rect(ImObject *object, ImRect rect);

/* object location methods */
ImPoint im_object_get_location(ImObject *object);
void im_object_set_location(ImObject *object, ImPoint point);
void im_object_set_xy(ImObject *object, int x, int y);

/* object size methods */
ImSize im_object_get_size(ImObject *object);
void im_object_get_width(ImObject *object);
void im_object_get_height(ImObject *object);
void im_object_set_size(ImObject *object, ImSize size);
void im_object_set_wh(ImObject *object, int w, int h);

/* object mask methods */
ImMask im_object_get_mask(ImObject *object);

/* object user data methods */
void *im_object_get_data(ImObject *object, char *key);
void *im_object_set_data(ImObject *object, char *key, void *value);
void *im_object_remove_data(ImObject *object, char *key);

/* object X methods */
Pixmap im_object_get_x_pixmap(ImObject *object);


/*********************/
/* GEOMETRIC OBJECTS */
/*********************/
/* for now these typedefs should suffice.  at some point we may want to
 * add additional members to these types, in which case they need to be
 * converted to actual structures */
#define IM_RECTANGLE(a) ((ImRectangle*) (a))
typedef ImObject ImRectangle;

ImRectangle *im_rectangle_new(int w, int h);
void im_rectangle_init(ImRectangle *rectangle);


#define IM_ELLIPSE(a) ((ImEllipse*) (a))
typedef ImObject ImEllipse;

ImEllipse *im_ellipse_new(int w, int h);
void im_ellipse_init(ImEllipse *ellipse);


/*******************/
/* GRADIENT OBJECT */
/*******************/
typedef struct {
  ImColor color;
  int offset;
} ImGradientPrivatePoint;

#define IM_GRADIENT(a) ((ImGradient*) (a))
typedef struct {
  ImRectangle rectangle;
  /* again, this doesn't have to be a plib object, just any old
   * resizeable data structure */
  PVector points;
} ImGradient;

ImGradient *im_gradient_new(ImColor *color);
void im_gradient_init(ImGradient *grad);

/* gradient point add/remove methods */
int im_gradient_add_point(ImGradient *grad, ImColor color, int offset);
void im_gradient_remove_point(ImGradient *grad, int point);

ImColor im_gradient_point_get_color(ImGradient *grad, int point);
void im_gradient_point_set_color(ImGradient *grad, int point, ImColor color);

int im_gradient_point_get_offset(ImGradient *grad, int point);
void im_gradient_point_set_offset(ImGradient *grad, int point, int offset);

/* gradient misc methods */
int im_gradient_num_points(ImGradient *grad);


/******************/
/* POLYGON OBJECT */
/******************/
#define IM_POLYGON(a) ((ImPolygon*) (a))
typedef struct {
  ImObject object;
  /* this isn't implying that I want to use PLib; I just wanted a nice
   * resizeable indexed data structure */
  PVector *vertices; 

  ImBool homogenous_color;
} ImPolygon;

ImPolygon *im_polygon_new(void);
void im_polygon_init(ImPolygon *poly);

/* polygon vertex add/remove methods */
int im_polygon_add_vertex(ImPolygon *poly, int x, int y, ImColor color);
void im_polygon_remove_vertex(ImPolygon *poly, int vtx);

/* polygon vertex get/set methods */
ImVertex im_polygon_get_vertex(ImPolygon *poly, int vtx);
void im_polygon_set_vertex(ImPolygon *poly, int vtx, ImVertex vertex);

/* polygon vertex member methods */
im_polygon_set_vertex_xy(ImPolygon *poly, int vtx, int x, int y);
im_polygon_set_vertex_color(ImPolygon *poly, int vtx, ImColor color);

/* polygon misc methods */
int im_polygon_num_vertices(ImPolygon *poly);
void im_polygon_rotate(ImPolygon *poly, double angle);
void im_polygon_scale(ImPolygon *poly, double x_scale, double y_scale);


/****************/
/* IMAGE OBJECT */
/****************/
#define IM_IMAGE(a) ((ImImage*) (a))
typedef struct {
  ImObject object;
  ImRect   rect;

  void    *clip_mask; /* 1-bit clipping mask */
  void    *image_data;
} ImImage;


/*****************/
/* IMAGE METHODS */
/*****************/
ImImage *im_image_new(int w, int h);
void im_image_init(ImImage *image);

/* image load operations */
ImImage *im_image_load(char *path);
ImImage *im_image_load_full(char *path, PHash *module_options, ImError *error_return);

/* image save operations */
ImBool im_image_save(char *path);
ImBool im_image_save_full(ImImage *image, char *path, PHash *module_options, ImError *error_return);

void im_image_set_border(ImImage *image, ImBorder border);
ImBorder im_image_get_border(ImImage *image);

/* image clipping mask */
void im_image_clipmask_add(ImImage *image, void *object);
void im_image_clipmask_remove(ImImage *image, void *object);
void im_image_invert_clipmask(ImImage *image);
void im_image_clear_clipmask(ImImage *image);

/* warning: don't use these unless you know what you're doing */
void im_image_set_clipmask(ImImage *image, ImMask clipmask);
ImMask im_image_get_clipmask(ImImage *image);

/* pixel operations */
ImColor im_image_get_pixel(ImImage *image, int x, int y);
void im_image_set_pixel(ImImage *image, int x, int y, ImColor color);

/* geometric object draw / fill */
void im_image_draw(ImImage *image, void *object, ImColor color, ImBool fill);
void im_image_draw_at(ImImage *image, void *object, int x, int y, ImColor color, ImBool fill);

/* geometric non-object draw calls */
void im_image_draw_rectangle(ImImage *image, int x, int y, int w, int h, ImColor color, ImBool fill);
void im_image_draw_ellipse(ImImage *image, int x, int y, int w, int h, ImColor color, ImBool fill);

#endif /* !IM_H */

