#ifndef zori_H_INCLUDED
#define zori_H_INCLUDED

#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


/* 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 int zori_id;

/* 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)


#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: 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 does 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) \
        ((TYPE *)(((char *)(PTR)) - offsetof(TYPE, MEMBER)))


/** 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'),
  ZORI_EVENT_UPDATE,
  ZORI_EVENT_DRAW,
  ZORI_EVENT_DONE,
  ZORI_EVENT_FREE,
};


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     * sysev;
}; 

/* 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;
};


/** 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;
};


/* 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.sysev;
}


/* 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;
}


struct zori_stylepart {
  zori_color       color;
  zori_bitmap    * image;
  zori_font      * font;
};

struct zori_style {
  struct zori_stylepart fore;
  struct zori_stylepart back;
  struct zori_stylepart text;
  struct zori_stylepart border;
};

struct zori_widget;

/* Event handler results. */
enum zori_handle_result {
  ZORI_HANDLE_ERROR   = -1, 
  ZORI_HANDLE_OK      = 0,
  ZORI_HANDLE_IGNORE  = 1,
};



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 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,
};


/* 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;
  /* 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;
};


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

/* forward declaration. */
struct zori_screen;

/* 
 * 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;
};

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

/* The top level widget for a single display. */
struct zori_screen { 
  /* A screen is a widget. */ 
  struct zori_widget widget;
  /* It also manages the cursors. */
  struct zori_cursors cursors;
  /* Display this screen is on. */
  zori_display * display; 
  /* The GUI page that is active on this screen if any. */
  struct zori_page * active_page; 
};


/* In Zori, the GUI is paginated. This means that on any 
 * screen, only a single GUI page can be active. The intent is to 
 * support different GUI modes such as startup screen, status view, 
 * settings, HUD, and so on between which can be switched easily. */
struct zori_page {
  /* A page is a widget. */
  struct zori_widget widget;
};

/* 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.  */
zori_id zori_new_screen(zori_display * display);

/* Creates a new page widget on the given screen. The page is not 
 * made the active page, unless if it is the first one created. */
zori_id zori_new_page(zori_id screen);

/* 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_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, 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, struct zori_widget *parent, zori_rebox *box, struct zori_style *style, size_t amount, struct zori_handler *handlers);
void zori_draw_all(void);
int zori_widget_visible(struct zori_widget *widget);
int zori_widget_active(struct zori_widget *widget);
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);

void zori_widget_drawroundframe(struct zori_widget *self);


typedef int (zori_console_command)(struct zori_widget *, const char * command, void * data);

/* A console is a console for command-line interaction and error display. When it's active it captures all input (as long as it's active) */
struct zori_console {
  struct zori_widget widget;
  USTRList  text;
  int       count;
  int       max;
  int       start;
  int       charw;
  int       cursor;
  char    * buf;
  USTR    * input;
  zori_console_command * command; // called when a command has been entered, if set.
  void    * command_data; // command data.
};



struct zori_console *zori_widget_console(struct zori_widget *widget);
int zori_console_docommand(struct zori_console *self, const char *text);
int zori_console_addstr(struct zori_console *self, const char *str);
int zori_console_addustr(struct zori_console *self, const ALLEGRO_USTR *ustr);
int zori_console_puts(struct zori_console *self, const char *str);
int zori_console_vprintf(struct zori_console *self, const char *format, va_list args);
int zori_console_printf(struct zori_console *self, const char *format, ...);
int zori_console_draw(union zori_event *zevent);
void zori_console_active_(struct zori_console *self, int active);
int zori_console_active(struct zori_console *self);
int zori_console_scroll(struct zori_console *self, int direction);
int zori_console_handle_keychar(union zori_event *zevent);
int zori_console_handle_keydown(union zori_event *zevent);
int zori_console_handle_mouseaxes(union zori_event *zevent);
int zori_console_handle_ignore(union zori_event *zevent);
int zori_console_handle(struct zori_widget *widget, zori_system_event *sevent);
int zori_console_done(struct zori_widget *widget);
int zori_console_free(struct zori_widget *widget);
struct zori_console *zori_console_alloc(void);
struct zori_console *zori_console_initall(struct zori_console *self, int id, zori_rebox *bounds, struct zori_style *style);
struct zori_console *zori_console_new(int id, zori_rebox *bounds, struct zori_style *style);



#endif