#ifndef zori_H_INCLUDED
#define zori_H_INCLUDED

/*
* ZORI is a a GUI subsystem for use with Allegro 5 that handles 
* the user interface of Eruta. 
*
* ZORI is a classic retained-mode GUI system that has an additional 
* pseudo-direct mode API. ZORI uses a handler system where events are handled 
* by call back functions which can be installed as specific handlers that are 
* activated on certain events. But, in the spirit of direct mode GUI's, all 
* widgets also keep state flags and value flags that can be used to see if they 
* have been activated and that can be inspected in a direct-mode GUI fashion. 
* With ZORI you can have your retained mode GUI pie and also eat it in direct 
* mode.
*
* In ZORI, there is only one top-level widget, the root, however, every widget 
* can contain any amount of child widgets. One level below the root widget are 
* the screen widgets which are intent to handle the GUI on one physical screen 
* or display. Every GUI screen can have different "pages", but only one page 
* should be active per screen. The idea is that a GUI page represents a single
* GUI mode such as the main menu, the settings menu, the in game menu, etc,
* where you definitely want only one to be active at any given time.
* 
* The box model used in Zori will be as follows (XXX implement this):
*  ......................................
*  .margin                              .
*  .  +-------------border-----------+  .
*  .  | padding                      |  .
*  .  |  ..........................  |  .
*  .  |  .                        .  |  .
*  .  |  . contents               .  |  .
*  .  |  .                        .  |  .
*  .  |  ..........................  |  .
*  .  | padding                      |  .
*  .  +------------------------------+  .
*  .margin                              .
*  ......................................
*
* The padding determines the minimal distance between the border or the
* parent widget and it's contents and/or child widgets.
*
* The border's thickness is only relevant for visual effects. 
* It does not change the layout. The border is effectively "inside" 
* the padding and/or contents of the widget and is unrelated to the padding.
*
* The margin of a widget determines how closely that widget may be packed
* to it's sibling widgets.
*
* The work in UI is divided between the top-level widgets, 
* namely the root and the page, and the other widgets. 
* The root and the page handle everything that depends on and/or may influence 
* several widgets at once, such as event dispatching but also setting the
* focus, determining which widget is being hovered, or dragged, etc. 
* The latter functions change the state of several widgets, so they 
* are handled on the level of the container widgets.
* The zori_widget and the other concrete widgets handle the individual 
* state and actions of the various widgets individually.
* 

* 

* 
* 
*/


#include <stdbool.h>
#include "eruta.h"
#include "rebox.h"
#include "miao.h"
#include "str.h"

/* Defines for C11/C99/C89 portability */
#ifndef inline
#define inline inline
#endif

/* Macros for possible later system library portability. */
#define ZORI_EVENT_TYPE_IS_USER(T) ALLEGRO_EVENT_TYPE_IS_USER(T)

#define ZORI_SYSTEM_EVENT_KEY_DOWN          ALLEGRO_EVENT_KEY_DOWN
#define ZORI_SYSTEM_EVENT_KEY_UP            ALLEGRO_EVENT_KEY_UP
#define ZORI_SYSTEM_EVENT_KEY_CHAR          ALLEGRO_EVENT_KEY_CHAR
#define ZORI_SYSTEM_EVENT_MOUSE_AXES        ALLEGRO_EVENT_MOUSE_AXES
#define ZORI_SYSTEM_EVENT_MOUSE_AXES        ALLEGRO_EVENT_MOUSE_AXES
#define ZORI_SYSTEM_EVENT_MOUSE_BUTTON_DOWN ALLEGRO_EVENT_MOUSE_BUTTON_DOWN
#define ZORI_SYSTEM_EVENT_MOUSE_BUTTON_UP   ALLEGRO_EVENT_MOUSE_BUTTON_UP


/* Typedefs for possible later system library portability. */
typedef ALLEGRO_COLOR  zori_color;
typedef ALLEGRO_BITMAP zori_bitmap;
typedef ALLEGRO_FONT   zori_font;

typedef ALLEGRO_EVENT zori_system_event;
typedef ALLEGRO_EVENT_TYPE zori_event_type;
typedef ALLEGRO_DISPLAY zori_display;

typedef Point zori_point;
typedef Rebox zori_rebox;
typedef Rebox zori_box;
typedef int   zori_id;
typedef ALLEGRO_USTR zori_string;
typedef ALLEGRO_USTR zori_text;


/* Macros for possible portability. */
#define zori_font_lineheight(FONT) al_get_font_line_height(FONT)
#define zori_font_drawstr(FONT, COLOR, X, Y, ATTR, TEXT) al_draw_ustr(FONT, COLOR, X, Y, ATTR, TEXT)


/* Default sizes of a widget. */
#define ZORI_WIDGET_DEFAULT_W 640
#define ZORI_WIDGET_DEFAULT_H 480

/* Defaut padding and margin. */
#define ZORI_PADDING_DEFAULT  4
#define ZORI_MARGIN_DEFAULT   0

/* Zori ID's. */
#define ZORI_ID_OK_P(ID)  ((ID) > -1)
#define ZORI_ID_OK        ((zori_id)(0))
#define ZORI_ID_ERROR     ((zori_id)(-1))
#define ZORI_ID_ENOMEM    ((zori_id)(-2))
#define ZORI_ID_EINVAL    ((zori_id)(-3))


/** Macro for the number of elements of an array .*/
#define ZORI_ARRAY_AMOUNT(A) ((size_t)(sizeof(A))/(sizeof(*A)))

/** Macro that expands to the the array itself, and the amount of elements
 * in that array, in that order. Only works on a statically sized array. */
#define ZORI_ARRAY_AND_AMOUNT(A) (A), ZORI_ARRAY_AMOUNT(A) 
 

/* Macro: ZORI_CONTAINER_OF(PTR, TYPE, MEMBER)
 This macro returns, for the given pointer, a pointer to a containing struct
 of type TYPE, in which PTR is a member named MEMBER. 
 This enables cool ways of type genericity and extension in plain C.
 It should not run afoul of strict aliasing since it passes over a char * pointer 
 and  a pointer of a containing struct or union.
*/
#define ZORI_CONTAINER_OF(PTR, TYPE, MEMBER) \
        ((PTR) ? ((TYPE *)(((char *)(PTR)) - offsetof(TYPE, MEMBER))) : NULL)


/** Custom event types, used in conjunction with Allegro event types. */
enum zori_custom_event_type {
  ZORI_EVENT_CUSTOM = ALLEGRO_GET_EVENT_TYPE('z', 'o', 'r', 'i'),
  /** Update event, called at regular intervals to update the state of the UI.*/
  ZORI_EVENT_UPDATE,
  /** First draw pass. */
  ZORI_EVENT_DRAW,  
  /** Second draw pass for cursors. */
  ZORI_EVENT_OVERDRAW,
  /** Cleanup event. */
  ZORI_EVENT_DONE,
  /** Destruction event. */
  ZORI_EVENT_FREE,
  /** Activation event, such as a button being clicked or a menu being opened. */
  ZORI_EVENT_ACTION, 
  /** Close event, such as a menu being closed. Not for cleanup.*/
  ZORI_EVENT_CLOSE,
  /** Child added event to ease setup. */
  ZORI_EVENT_NEW_CHILD,
};


struct zori_widget;

/* Common fields for an event. */
struct zori_any_event {
  /* Type of the event, possibly copied from sysev. */ 
  zori_event_type      type;  
  /* Widget the event is sent to. */
  struct zori_widget * widget;
  /* Data specified in the handler. */
  void               * data; 
};

/* System event, that is coming from the underlying library used, e.g. Allegro. */
struct zori_sys_event {
    struct zori_any_event   any;
    zori_system_event     * ev;
}; 

/* Update event, when UI has to update (animations, etc). */
struct zori_update_event {
    struct zori_any_event   any;
    double dt;
};

/* Draw event when the UI has to draw itself. */
struct zori_draw_event {
    struct zori_any_event   any;
};

/* Cleanup event. */
struct zori_done_event {
    struct zori_any_event   any;
};

/* Cleanup event. */
struct zori_free_event {
    struct zori_any_event   any;
};

/* Action event. */
struct zori_action_event {
    struct zori_any_event   any;
};

/* Close event. */
struct zori_close_event {
    struct zori_any_event   any;
    /* The widget that was closed and that sent this message to it's parent. */
    struct zori_widget * from;
};

/* New child event. */
struct zori_new_child_event {
    struct zori_any_event   any;
    /* The widget that was added as a child and that sent this message to it's parent. */
    struct zori_widget  * child;
};


/** An event that ZORI can handle. The union is cunningly overlaid so 
 * that the type field and the any field can be used in all cases. */
union zori_event {
  /* Type of the event, possibly copied from sysev. */ 
  zori_event_type               type;
  struct zori_any_event         any;
  struct zori_sys_event         sys;
  struct zori_draw_event        draw;  
  struct zori_update_event      update;
  struct zori_done_event        done;
  struct zori_free_event        free;
  struct zori_action_event      action;
  struct zori_close_event       close;
  struct zori_new_child_event   new_child;
};


/* Helper functions to get widget from an event. */
static inline struct zori_widget * 
zori_event_widget(union zori_event * event) {
  if (!event) return NULL;
  return event->any.widget;
}


/* Helper functions to get data from an event. */
static inline void * zori_event_data(union zori_event * event) {
  if (!event) return NULL;
  return event->any.data;
}


/* Helper function that checks if an event is a sys_event. */
static inline bool zori_event_is_sys_event(union zori_event * event) {
  if (!event) return false;
  return (!ALLEGRO_EVENT_TYPE_IS_USER(event->type));
}

/* Helper functions to get system event from an event. Type checks the type. */
static inline zori_system_event * zori_event_system_event(union zori_event * event) {
  if (!event) return NULL;
  if (!zori_event_is_sys_event(event)) return NULL;
  return event->sys.ev;
}

/* Helper functions to set widget for an event. */
static inline struct zori_widget *  
zori_event_set_widget(union zori_event * event, struct zori_widget * widget) {
  if (!event) return NULL;
  return event->any.widget = widget;
}

/* Helper functions to set data for an event. */
static inline void * zori_event_set_data(union zori_event * event, void * data) {
  if (!event) return NULL;
  return event->any.data = data;
}

/* A style part is a color, image, font, and font flags applied to a part of the GUI. */
struct zori_stylepart {
  zori_color       color;
  zori_bitmap    * bitmap;
  zori_font      * font;
  int              font_flags; 
};

/* A style is a set of style parts for different parts of the GUI. */
struct zori_style {
  struct zori_stylepart fore;
  struct zori_stylepart back;
  struct zori_stylepart text;
  struct zori_stylepart border;
  struct zori_stylepart hover;
  struct zori_stylepart mark;
};

struct zori_widget;

/* Event handler results. */
enum zori_handle_result {
  ZORI_HANDLE_ERROR   = -1, /* An error ocurred, stop propagating to children.*/ 
  ZORI_HANDLE_DONE    = 0, /* The event was handled and consumed, no need to propagate to children automatically (widget may re-send event manually to children.)*/
  ZORI_HANDLE_IGNORE  = 1, /* Event wasn't handled, propagate to children. */
  ZORI_HANDLE_PASS    = 2, /* Event was handled, but needs to be propagated.*/
};


/* Returns whether or not a event needs to be propagated based on the 
 * result of a handler. */
static int zori_propagate_event_p(enum zori_handle_result result) {
  switch(result) {
     case ZORI_HANDLE_ERROR:  return 0;
     case ZORI_HANDLE_DONE:   return 0;
     case ZORI_HANDLE_IGNORE: return !0;
     case ZORI_HANDLE_PASS:   return !0;
     default:                 return !0;
  }
}


typedef enum zori_handle_result (zori_handler_func)(union zori_event * event);

/* A single event handler */
struct zori_handler {
  zori_event_type type;
  zori_handler_func * handler;
  void * data;
};


/* A dynamic array of event handlers event handlers. */
struct zori_handlers miao_of_type(struct zori_handler);


/* An entry in a widget registry.  */
struct zori_registry_entry {
  zori_id              id;
  struct zori_widget * widget;
};

/* A widget registry as a dynamic array of entries. */
struct zori_registry miao_of_type(struct zori_registry_entry);

/* Generic state flags for several zori structs. */
enum zori_flag {
  /* The object is not visible, though it may still be interacted with.*/ 
  ZORI_FLAG_HIDDEN      = 1 << 0,
  /* The object cannot be interacted with, though it is still visible. */
  ZORI_FLAG_DISABLED    = 1 << 1,
  /* The object is both hidden and disabled. */
  ZORI_FLAG_DEACTIVATED = ZORI_FLAG_HIDDEN | ZORI_FLAG_DISABLED,
  /* The object is being hovered by the mouse cursor. */
  ZORI_FLAG_HOVERED     = 1 << 2,
  /* The object is "marked" for activation by the keyjoy cursor */
  ZORI_FLAG_MARKED      = 1 << 3,
};

/* Typedef for the type of a widget.  
 * Not an enum since every widget may define this itself. 
 */
typedef uint32_t zori_widget_type;

/* Generic widget types. */
#define ZORI_WIDGET_TYPE_NONE ((zori_widget_type)(0)) 

/* Macro to help generate widget types. */
#define ZORI_WIDGET_TYPE(A, B, C, D) ((zori_widget_type)((A<<24) | (B<<16) | (C<<8) | D))

/* Mouse or keyboard/joystick cursor. */
struct zori_cursor { 
  zori_point           p; 
  struct zori_widget * hover;
  struct zori_widget * focus;
  zori_bitmap        * bitmap;
  enum zori_flag       flags;
};


/* Support multiple cursors...  */
struct zori_cursors { 
  struct zori_cursor mouse;
  struct zori_cursor keyjoy;
};


/*
on_enter
on_enter(data = {})
on_event(*args)
on_event(*data)
on_key_down(*args)
on_leave(name=nil)
on_mouse_axes(t, x, y, z, w, dx, dy, dz, dw)
on_mouse_button_down(t, x, y, z, w, b)
on_mouse_button_up(t, x, y, z, w, b)
on_mouse_in(x, y, from)
on_mouse_out(x, y, to)
on_resize
*/

struct zori_widget;

struct zori_widget_list miao_of_type(struct zori_widget *);

struct zori_widget {
  /* ID of the widget, used in most external API's. */
  zori_id id;
  
  /* Root level widget under which this widget is active. */
  struct zori_widget  * root;
  
  /* Position and size of the widget. */
  zori_rebox box;
  
  /* Outer rectangle of the widget, with the margin added. */
  zori_rebox outer;
  
  /* Inner rectangle of the widget, with the padding removed. */
  zori_rebox inner;
  
  /* Z ordering. */
  int z;
  
  /* Style. */
  struct zori_style    style;
  
  /* Handlers. */
  struct zori_handlers handlers;
  
  /* Related widgets. */
  struct zori_widget *    parent;
  struct zori_widget_list children;
  
  /* Flags. */
  enum zori_flag   flags;
  
  /* Type of the widget. */
  zori_widget_type type;
  
  /* Generic "result", of last operation on widget, normally zero. */
  int result;
  
  /* Cannot easily use the handers for destroying the widget, so in stead
   * provide a destructor. */
   void (*destroy)(struct zori_widget * widget);
  
};


/* An array of widget pointers. */
struct zori_widget_array miao_of_type(struct zori_widget *);

/* Forward declarations. */
struct zori_screen;
struct zori_console;

/* 
 * Root level widget, my spread out over several displays. 
 * In Zori, there can only be a single root level widget active.
 * It's ID is always 0;
*/
struct zori_root {
  /* A root is a widget. */ 
  struct zori_widget widget;
  /* Current active screen widget if any. */
  struct zori_screen * active_screen;
  /* Current active console if any. */
  struct zori_console * console;
};

/* Forward declaration of a page. */
struct zori_page;

struct zori_root * zori_get_root(void);

/* Initializes Zori and creates a top level widget. Returns 0 on success 
 * or negative on error. The style will be copied and set as default 
 * if it is not NULL. Otherwise a built-in style will be used. 
 * Not that ZORI will NOT clean up any images or fonts it uses by itself.
 */
zori_id zori_start(struct zori_style * default_style);

/* Shut down Zori and destroys all widgets. Return 0 on succes or 
 * negative on error.
 */
zori_id zori_shutdown();

/* Creates a new screen widget. Normally this should be the first widget 
 * you create after zori_start.  */

/* Activates the page on it's display. All other pages are dectivated and 
 * hidden. */
zori_id zori_activate_page(zori_id page);

/* Creates a new generic widget on the given screen with the given
 * dimensions. */
zori_id zori_new(zori_id screen, zori_rebox * box);

/* Sets the flags of a widget.  */
zori_id zori_set_flags(zori_id widget, enum zori_flag flags);


/* Sets the whole style of a widget. */
zori_id zori_set_style(zori_id id, struct zori_style * style);

/* Sets the background color of the widget. */
zori_id zori_set_background_color(zori_id id, zori_color color);

/* Sets the foreground color of the widget. */
zori_id zori_set_foreground_color(zori_id id, zori_color color);

/* Creates a new frame widget. */
zori_id zori_new_frame_widget(zori_id parent, zori_rebox box);

/* Creates a new (vertical) menu widget. */
zori_id zori_new_menu_widget(zori_id parent, zori_rebox box, char * text);

/* Creates a new button widget. */
zori_id zori_new_button_widget(zori_id parent, zori_rebox box, char * text);

/* Creates a new conversation widget. */
zori_id zori_new_conversation_widget(zori_id parent, zori_rebox box, char * text);

/* Draws the whole UI and all visible parts. */
void zori_draw_all(void);

/* Updates the state of the UI. Pass in the time passed since last update. */
void zori_update(double dt);

/* Registers an event handler for a widget. */
zori_id zori_register(zori_id id,  zori_event_type type, zori_handler_func handler, void * extra);


int zori_registry_entry_compare(const void *v1, const void *v2);
zori_id zori_registry_init(struct zori_registry *registry);
zori_id zori_registry_add(struct zori_registry *registry, zori_id id, struct zori_widget *widget);
struct zori_registry_entry *zori_registry_lookup_entry(struct zori_registry *registry, zori_id id);
struct zori_widget *zori_registry_lookup(struct zori_registry *registry, zori_id id);
zori_id zori_registry_remove(struct zori_registry *registry, zori_id id);
int zori_handler_compare(const void *v1, const void *v2);
struct zori_handler *zori_handlers_add(struct zori_handlers *me, zori_event_type type, zori_handler_func *handler, void *data);
void zori_handlers_done(struct zori_handlers *me);
void zori_handlers_init(struct zori_handlers *me);
struct zori_handler *zori_handlers_search(struct zori_handlers *me, zori_event_type type);
int zori_handlers_handle(struct zori_handlers *me, union zori_event *event);

int zori_widget_raise_system_event(struct zori_widget *widget, zori_system_event *sysev);
int zori_widget_raise_draw_event(struct zori_widget *widget);
int zori_widget_raise_done_event(struct zori_widget *widget);
int zori_widget_raise_free_event(struct zori_widget *widget);
int zori_widget_raise_update_event(struct zori_widget *widget, double dt);
int zori_widget_raise_action_event(struct zori_widget *widget);
int zori_widget_raise_close_event(struct zori_widget *widget, struct zori_widget * from);

int zori_widget_compare(const void *v1, const void *v2);
struct zori_widget *zori_get_widget(zori_id id);
zori_id zori_get_unused_id(void);
struct zori_handler *zori_widget_add_handler(struct zori_widget *widget, zori_event_type type, zori_handler_func *handler, void *data);
struct zori_handler *zori_widget_add_handlers(struct zori_widget *widget, struct zori_handler *handlers, size_t amount);
zori_id zori_start(struct zori_style *default_style);
struct zori_widget *zori_widget_done(struct zori_widget *widget);
void zori_widget_free(struct zori_widget *widget);
struct zori_widget *zori_widget_add_child(struct zori_widget *parent, struct zori_widget *child);
struct zori_widget *zori_widget_init(struct zori_widget *widget, zori_widget_type type, zori_id id, struct zori_widget *parent, zori_rebox *box, struct zori_style *style);
zori_id zori_shutdown(void);
struct zori_widget *zori_widget_initall(struct zori_widget *widget, zori_widget_type type, zori_id id, struct zori_widget *parent, zori_rebox *box, struct zori_style *style, struct zori_handler *handlers, size_t amount);
void zori_draw_all(void);

int zori_widget_visible(struct zori_widget *widget);
int zori_widget_visible_(struct zori_widget *widget, int set);
int zori_widget_active(struct zori_widget *widget);
int zori_widget_active_(struct zori_widget *widget, int set);
int zori_widget_hover(struct zori_widget *widget);
int zori_widget_hover_(struct zori_widget *widget, int set);
int zori_widget_marked(struct zori_widget *widget);
int zori_widget_marked_(struct zori_widget *widget, int set);


int zori_widget_active_(struct zori_widget *widget, int set);
void zori_update(double dt);
zori_font *zori_widget_font(struct zori_widget *widget);
zori_color zori_widget_forecolor(struct zori_widget *widget);
zori_color zori_widget_backcolor(struct zori_widget *widget);
int zori_widget_h(struct zori_widget *widget);
int zori_widget_w(struct zori_widget *widget);
int zori_widget_x(struct zori_widget *widget);
int zori_widget_y(struct zori_widget *widget);
int zori_xy_inside_widget_p(struct zori_widget * widget, double x, double y);

zori_font * zori_widget_text_font(struct zori_widget * widget);
zori_font * zori_text_font(zori_id id);


void zori_widget_drawroundframe(struct zori_widget *self);

int zori_handle_system_event(zori_system_event * sysev);

int zori_result(int zori_id);

int zori_mark_widget(struct zori_widget * widget);


int zori_registry_entry_compare(const void *v1, const void *v2);
zori_id zori_registry_init(struct zori_registry *registry);
zori_id zori_registry_add(struct zori_registry *registry, zori_id id, struct zori_widget *widget);
struct zori_registry_entry *zori_registry_lookup_entry(struct zori_registry *registry, zori_id id);
struct zori_widget *zori_registry_lookup(struct zori_registry *registry, zori_id id);
zori_id zori_registry_remove(struct zori_registry *registry, zori_id id);



int zori_handler_compare(const void *v1, const void *v2);
struct zori_handler *zori_handlers_add(struct zori_handlers *me, zori_event_type type, zori_handler_func *handler, void *data);
void zori_handlers_done(struct zori_handlers *me);
void zori_handlers_init(struct zori_handlers *me);
struct zori_handler *zori_handlers_search(struct zori_handlers *me, zori_event_type type);
int zori_handlers_handle(struct zori_handlers *me, union zori_event *event);
struct zori_root *zori_get_root(void);
struct zori_widget *zori_get_widget(zori_id id);
zori_id zori_get_unused_id(void);
zori_id zori_start(struct zori_style *default_style);
zori_id zori_set_margins(zori_id id, int left, int top, int right, int bottom);
zori_id zori_set_margin(zori_id id, int size);
zori_id zori_set_paddings(zori_id id, int left, int top, int right, int bottom);
zori_id zori_set_padding(zori_id id, int size);
zori_font *zori_text_font(zori_id id);
zori_id zori_shutdown(void);
void zori_draw_all(void);
int zori_handle_system_event(zori_system_event *sysev);
void zori_update(double dt);
int zori_result(zori_id id);


#endif



