#include "zori.h"
#include "zori_widget.h"
#include "zori_menu.h"

struct zori_menu * zori_widget_to_menu(struct zori_widget * widget) {
  if (!widget) return NULL;
  return ZORI_CONTAINER_OF(widget, struct zori_menu, widget);
}

struct zori_widget * zori_menu_get_selected(struct zori_menu * menu) {
  if (!menu) return NULL;
  if (menu->selected_index < 0) return NULL;
  return miao_unsafe_get(&menu->widget.children, menu->selected_index);
}

int zori_menu_select_index(struct zori_menu * menu, int index) {
    struct zori_widget * selected = NULL;
    size_t size = miao_size(&menu->widget.children);
    if (size < 1)       return ZORI_HANDLE_ERROR;
    if (index < 0)      return ZORI_HANDLE_DONE;
    if ((size_t)index >= size)  return ZORI_HANDLE_DONE;
    menu->selected_index = index;
    selected = zori_menu_get_selected(menu);
    zori_mark_widget(selected);
    return ZORI_HANDLE_DONE;
}

int zori_menu_select_previous(struct zori_menu * menu) {
  size_t size = miao_size(&menu->widget.children);
  int new_selection = menu->selected_index - 1;
  /* "roll over" */
  if (new_selection < 0) { 
    new_selection = (int)size - 1;
  }
  return zori_menu_select_index(menu, new_selection);
}

int zori_menu_select_next(struct zori_menu * menu) {
  size_t size = miao_size(&menu->widget.children);
  int new_selection = menu->selected_index + 1;
  /* "roll over" */
  if ((size_t)new_selection >= size) { 
    new_selection = 0;
  }
  return zori_menu_select_index(menu, new_selection);
}

int zori_menu_select_first(struct zori_menu * menu) {
  return zori_menu_select_index(menu, 0);
}

int zori_menu_activate_selected(struct zori_menu * menu) {
  struct zori_widget * selected = zori_menu_get_selected(menu);
  if (selected) { 
    return zori_widget_raise_action_event(selected);
  }
  return ZORI_HANDLE_IGNORE;
}


int zori_menu_close(struct zori_menu * menu)  { 
  struct zori_widget * parent;
  parent = menu->widget.parent;
  zori_widget_raise_close_event(parent, &menu->widget);
  zori_widget_active_(&menu->widget, false);
  return ZORI_HANDLE_DONE;
}


int zori_menu_on_child_close(union zori_event * event) {
  struct zori_widget * widget = event->any.widget;
  struct zori_menu   * menu   = zori_widget_to_menu(widget);
  zori_mark_widget(zori_menu_get_selected(menu));
  return ZORI_HANDLE_DONE;
}
  

int zori_menu_on_key_down(union zori_event * event) {
  struct zori_widget * widget = event->any.widget;
  struct zori_menu   * menu = zori_widget_to_menu(widget);
  struct zori_widget * item = NULL;
  
  
  if (miao_out_of_bounds(&menu->widget.children, menu->selected_index)) {
    return ZORI_HANDLE_IGNORE;
  }
  
  item = miao_unsafe_get(&widget->children, menu->selected_index);
  switch (event->sys.ev->keyboard.keycode) {
    case ALLEGRO_KEY_UP:
      return zori_menu_select_previous(menu);
    
    case ALLEGRO_KEY_DOWN:
      return zori_menu_select_next(menu);
          
    case ALLEGRO_KEY_ENTER: 
    case ALLEGRO_KEY_SPACE: 
    case ALLEGRO_KEY_LCTRL: 
    case ALLEGRO_KEY_RCTRL:
      return zori_menu_activate_selected(menu);
        
    case ALLEGRO_KEY_BACKSPACE: 
    case ALLEGRO_KEY_LSHIFT: 
    case ALLEGRO_KEY_RSHIFT:
      return zori_menu_close(menu);
    
    default:
      return ZORI_HANDLE_IGNORE;
  }  
  return ZORI_HANDLE_IGNORE;
}


int zori_menu_on_draw(union zori_event * event) {
  struct zori_widget * widget = event->any.widget;
  zori_widget_draw_background(widget);
  return ZORI_HANDLE_PASS;
}

struct zori_handler zori_menu_handlers[] = {
  { ZORI_SYSTEM_EVENT_KEY_DOWN, zori_menu_on_key_down    , NULL}, 
  { ZORI_EVENT_DRAW           , zori_menu_on_draw        , NULL}, 
  { ZORI_EVENT_CLOSE          , zori_menu_on_child_close , NULL}, 
}; 


struct zori_menu * zori_menu_init(
  struct zori_menu * menu, zori_id id, zori_id parent_id,
  zori_rebox * box, struct zori_style * style
  ) {
    struct zori_widget * parent = zori_get_widget(parent_id);
  if (!menu) return NULL;
    menu->selected_index = 0;
    zori_widget_initall(&menu->widget, ZORI_WIDGET_TYPE_MENU, id, parent, box, style,  
      ZORI_ARRAY_AND_AMOUNT( zori_menu_handlers));
    zori_widget_hover_(&menu->widget, false);
    return menu;
}
    

struct zori_menu * zori_menu_new(zori_id id, zori_id parent_id, zori_box * box) {
  struct zori_menu * menu = NULL;
  menu = calloc(1, sizeof(*menu));
  if (!menu) return NULL;
  if (!zori_menu_init(menu, id, parent_id, box, NULL)) {
    free(menu);
    return NULL;
  }
  return menu;
}
    
    
zori_id zori_new_menu(zori_id id, zori_id parent, zori_box * box) {
  struct zori_menu * menu = zori_menu_new(id, parent, box);
  if (!menu) return ZORI_ID_ENOMEM;
  return menu->widget.id;
}
