#ifndef ELM_WIDGET_GENLIST_H
#define ELM_WIDGET_GENLIST_H

#include "elm_gen_common.h"
#include "Elementary.h"

/* DO NOT USE THIS HEADER UNLESS YOU ARE PREPARED FOR BREAKING OF YOUR
 * CODE. THIS IS ELEMENTARY'S INTERNAL WIDGET API (for now) AND IS NOT
 * FINAL. CALL elm_widget_api_check(ELM_INTERNAL_API_VERSION) TO CHECK
 * IT AT RUNTIME.
 */

/**
 * @addtogroup Widget
 * @{
 *
 * @section elm-genlist-class The Elementary Genlist Class
 *
 * Elementary, besides having the @ref Genlist widget, exposes its
 * foundation -- the Elementary Genlist Class -- in order to create
 * other widgets which are a genlist with some more logic on top.
 */

/**
 * Base widget smart data extended with genlist instance data.
 */
typedef struct _Elm_Genlist_Data Elm_Genlist_Data;

typedef enum
{
   ELM_GENLIST_TREE_EFFECT_NONE = 0,
   ELM_GENLIST_TREE_EFFECT_EXPAND = 1,
   ELM_GENLIST_TREE_EFFECT_CONTRACT = 2
} Elm_Genlist_Item_Move_Effect_Mode;

struct _Elm_Genlist_Data
{
   Eina_Inlist_Sorted_State             *state;
   Evas_Object                          *hit_rect;
   Evas_Object                          *pan_obj;
   Evas_Object                          *stack[2]; // stacking markers in pan
   Evas_Object                          *obj; // the object itself

   Eina_List                            *selected; /* a list of
                                                    * selected
                                                    * items */
   Eina_List                            *deselecting; /* a list of items currently being deselected */
   Eina_List                            *group_items; /* a list of
                                                       * groups index
                                                       * items */
   Eina_Inlist                          *items; /* an inlist of all items */
   Elm_Gen_Item                         *reorder_it; /* item currently
                                                     * being
                                                     * repositioned */
   Elm_Object_Item                      *last_selected_item;
   Elm_Object_Item                      *focused_item; /**< a focused item by keypad arrow or mouse. This is set to NULL if widget looses focus. */
   Elm_Object_Item                      *last_focused_item; /**< This records the last focused item when widget looses focus. This is required to set the focus on last focused item when widgets gets focus. */
   int                                   walking;
   int                                   minw, minh;
   unsigned int                          item_count;
   Evas_Coord                            pan_x, pan_y;
   Elm_Object_Select_Mode                select_mode;
   Elm_Object_Multi_Select_Mode          multi_select_mode; /**< select mode for multiple selection */

   Eina_Inlist                          *blocks; /* an inlist of all
                                                  * blocks. a block
                                                  * consists of a
                                                  * certain number of
                                                  * items. maximum
                                                  * number of items in
                                                  * a block is
                                                  * 'max_items_per_block'. */
   Evas_Coord                            reorder_old_pan_y, w, h, realminw;
   Evas_Coord                            prev_viewport_w; /* previous scrollable
                                                           * interface's
                                                           * viewport size.
                                                           * This is used only
                                                           * when genlist is in
                                                           * a compress mode. */
   Ecore_Job                            *update_job;
   Ecore_Idle_Enterer                   *queue_idle_enterer;
   Ecore_Idler                          *must_recalc_idler;
   Eina_List                            *queue;
   Elm_Gen_Item                         *show_item, *anchor_item, *mode_item,
                                        *reorder_rel, *expanded_item, *pin_item;
   Eina_Inlist                          *item_cache; /* an inlist of
                                                      * edje object it
                                                      * cache. */
   Evas_Coord                            anchor_y;
   Evas_Coord                            reorder_start_y; /* reorder
                                                           * it's
                                                           * initial y
                                                           * coordinate
                                                           * in the
                                                           * pan. */
   Elm_List_Mode                         mode;
   Ecore_Timer                          *multi_timer, *scr_hold_timer;
   Ecore_Animator                       *reorder_move_animator;
   const char                           *decorate_it_type;
   double                                start_time;
   Evas_Coord                            prev_x, prev_y, prev_mx, prev_my;
   Evas_Coord                            cur_x, cur_y, cur_mx, cur_my;

   struct
   {
      Evas_Coord x, y;
   } history[SWIPE_MOVES];

   int                                   multi_device;
   int                                   item_cache_count;

   /* maximum number of cached items. (max_items_per_block * 2) */
   int                                   item_cache_max;
   int                                   movements;

   /* maximum number of items per block */
   int                                   max_items_per_block;

   /* longpress timeout. this value comes from _elm_config by
    * default. this can be changed by
    * elm_genlist_longpress_timeout_set() */
   double                                longpress_timeout;
   Eina_Compare_Cb                       item_compare_cb;
   Eina_Compare_Cb                       item_compare_data_cb;

   /* a scrollto type which remembers where to scroll ex) in, top,
    * middle */
   Elm_Genlist_Item_Scrollto_Type        scroll_to_type;
   Evas_Object                          *event_block_rect; /**< This object blocks the event in some cases. For example, when the tree effect is running and not finished, this object blocks events to the genlist. */
   Eina_List                            *move_items; /* items move for
                                                      * tree effect */
   Elm_Gen_Item                         *expanded_next_item;
   Elm_Genlist_Item_Move_Effect_Mode     move_effect_mode;
   int                                   reorder_fast;

   Eina_List                            *filter_queue;
   Eina_List                            *filtered_list;
   void                                 *filter_data;
   unsigned int                          processed_count;
   unsigned int                          filtered_count;
   unsigned int                          top_level_parent_items;
   Ecore_Idle_Enterer                   *queue_filter_enterer;
   Eina_Hash                             *size_caches;

   Eina_Hash                            *content_item_map;
   Eo                                   *provider;
   Elm_Gen_Item                         *focus_on_realization;

   Eina_Bool                             scr_minw : 1; /* a flag for determining
                                                        * minimum width to limit
                                                        * as their content size */
   Eina_Bool                             scr_minh : 1; /* a flag for determining
                                                        * minimum height to limit
                                                        * as their content size */
   Eina_Bool                             filter : 1;
   Eina_Bool                             focus_on_selection_enabled : 1;
   Eina_Bool                             tree_effect_enabled : 1;
   Eina_Bool                             auto_scroll_enabled : 1;
   Eina_Bool                             decorate_all_mode : 1;
   Eina_Bool                             height_for_width : 1;
   Eina_Bool                             reorder_pan_move : 1;
   Eina_Bool                             multi_timeout : 1;
   Eina_Bool                             multi_touched : 1;
   Eina_Bool                             reorder_mode : 1; /* a flag
                                                            * for
                                                            * reorder
                                                            * mode
                                                            * enable/disable */
   /* this flag means genlist is supposed to be scrolled. if this flag
    * is set to @c EINA_TRUE, genlist checks whether it's ok to scroll
    * genlist now or not. */
   Eina_Bool                             check_scroll : 1;
   Eina_Bool                             pan_changed : 1;
   Eina_Bool                             wasselected : 1;
   Eina_Bool                             homogeneous : 1;
   Eina_Bool                             longpressed : 1;
   /* a flag for items can be highlighted or not. by default this flag
    * is true. */
   Eina_Bool                             mouse_down : 1;
   Eina_Bool                             multi_down : 1;
   Eina_Bool                             on_sub_del : 1;

   Eina_Bool                             highlight : 1;
   Eina_Bool                             h_bounce : 1;
   Eina_Bool                             v_bounce : 1;
   Eina_Bool                             bring_in : 1; /* a flag to
                                                        * describe the
                                                        * scroll
                                                        * animation. (show,
                                                        * bring in) */

   /* this is set to @c EINA_TRUE when the item is re-queued. this
    * happens when the item is un-queued but the rel item is still in
    * the queue. this item will be processed later. */
   Eina_Bool                             requeued : 1;
   Eina_Bool                             on_hold : 1;
   Eina_Bool                             multi : 1; /* a flag for item
                                                     * multi
                                                     * selection */

   Eina_Bool                             swipe : 1;
   /**< value whether item loop feature is enabled or not. */
   Eina_Bool                             item_loop_enable : 1;
   Eina_Bool                             item_looping_on : 1;

   Eina_Bool                             tree_effect_animator : 1;
   Eina_Bool                             pin_item_top : 1;
   Eina_Bool                             need_calc : 1; /* _calc_job() must be called in group_calc */
};

typedef struct _Item_Block Item_Block;
typedef struct _Item_Cache Item_Cache;
typedef struct _Item_Size Item_Size;

struct Elm_Gen_Item_Type
{
   Elm_Gen_Item           *it;

   Elm_Genlist_Data       *wsd;

   Item_Block             *block;
   Eina_List              *items;
   Evas_Coord              w, h, minw, minh;
   Elm_Gen_Item           *group_item;
   Elm_Genlist_Item_Type   type;
   Eina_List              *deco_it_texts, *deco_it_contents;
   Eina_List              *deco_all_texts, *deco_all_contents;
   Eina_List              *flip_contents;
   Ecore_Timer            *swipe_timer;
   Evas_Coord              scrl_x, scrl_y, old_scrl_y;

   Elm_Gen_Item           *rel;
   Eina_List              *rel_revs; // FIXME: find better way not to use this
   Evas_Object            *deco_it_view;
   int                     expanded_depth;
   int                     order_num_in; // Written by _item_order_update()

   Eina_Bool               decorate_all_item_realized : 1;
   Eina_Bool               tree_effect_finished : 1; /* tree effect */
   Eina_Bool               move_effect_enabled : 1;
   Eina_Bool               tree_effect_hide_me : 1; /* item hide for
                                                    * tree effect */

   Eina_Bool               stacking_even : 1;
   Eina_Bool               want_realize : 1;
   Eina_Bool               nocache_once : 1; /* do not use cache for
                                              * this item only once */
   Eina_Bool               nostacking : 1;
   Eina_Bool               expanded : 1;
   Eina_Bool               mincalcd : 1;
   Eina_Bool               updateme : 1;
   Eina_Bool               nocache : 1; /* do not use cache for this item */
   Eina_Bool               queued : 1;
   Eina_Bool               before : 1;
   Eina_Bool               show_me : 1;
   Eina_Bool               unfocusable : 1; /* item is not focusable; propagate to content */
};

struct _Item_Block
{
   EINA_INLIST;

   int                     count;
   int                     vis_count;
   int                     num;
   int                     reorder_offset;
   Elm_Genlist_Data       *sd;
   Eina_List              *items;
   Evas_Coord              x, y, w, h, minw, minh;
   int                     position;
   int                     item_position_stamp;
   Eo                     *adapter;

   Eina_Bool               position_update : 1;
   Eina_Bool               want_unrealize : 1;
   Eina_Bool               must_recalc : 1;
   Eina_Bool               realized : 1;
   Eina_Bool               updateme : 1;
   Eina_Bool               changed : 1;
   Eina_Bool               show_me : 1;
};

struct _Item_Cache
{
   EINA_INLIST;

   Evas_Object *base_view, *spacer;
   const Elm_Genlist_Item_Class  *item_class; // it->itc
   Eina_Bool    tree : 1; // it->group
   Eina_List   *contents; // content objects for reusing
};

struct _Item_Size
{
   const Elm_Genlist_Item_Class *itc;
   Evas_Coord minw;
   Evas_Coord minh;
   int expanded_depth;
};

typedef struct _Elm_Genlist_Pan_Data Elm_Genlist_Pan_Data;
struct _Elm_Genlist_Pan_Data
{
   Evas_Object            *wobj;
   Elm_Genlist_Data       *wsd;
   Ecore_Job              *resize_job;
};

/**
 * Structure added to genlist for internal filter iterator implementation
 * Can be extended to genlist as a whole in future if needed.
 */
typedef struct _Elm_Genlist_Filter Elm_Genlist_Filter;
struct _Elm_Genlist_Filter
{
   Eina_Iterator iterator;
   const Eina_Inlist *head;
   const Eina_Inlist *current;
   Evas_Object *obj;
};

#define ELM_GENLIST_FILTER_ITERATOR_ITEM_GET(ptr,                 \
                                  type) ((type *)((char *)ptr - \
                                                  offsetof(type, __in_list)))


/**
 * @}
 */

#define ELM_GENLIST_DATA_GET(o, sd) \
  Elm_Genlist_Data * sd = efl_data_scope_get(o, ELM_GENLIST_CLASS)

#define ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd) \
  Elm_Genlist_Data * sd = it->item->wsd

#define ELM_GENLIST_PAN_DATA_GET(o, sd) \
  Elm_Genlist_Pan_Data * sd = efl_data_scope_get(o, ELM_GENLIST_PAN_CLASS)

#define ELM_GENLIST_DATA_GET_OR_RETURN(o, ptr)       \
  ELM_GENLIST_DATA_GET(o, ptr);                      \
  if (EINA_UNLIKELY(!ptr))                           \
    {                                                \
       ERR("No widget data for object %p (%s)",      \
           o, evas_object_type_get(o));              \
       return;                                       \
    }

#define ELM_GENLIST_DATA_GET_OR_RETURN_VAL(o, ptr, val) \
  ELM_GENLIST_DATA_GET(o, ptr);                         \
  if (EINA_UNLIKELY(!ptr))                              \
    {                                                   \
       ERR("No widget data for object %p (%s)",         \
           o, evas_object_type_get(o));                 \
       return val;                                      \
    }

#define ELM_GENLIST_CHECK(obj)                              \
  if (EINA_UNLIKELY(!efl_isa((obj), ELM_GENLIST_CLASS))) \
    return

#define ELM_GENLIST_ITEM_CHECK(it)                          \
  ELM_WIDGET_ITEM_CHECK_OR_RETURN(it->base, ); \
  ELM_GENLIST_CHECK(it->base->widget);

#define ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, ...)                      \
  ELM_WIDGET_ITEM_CHECK_OR_RETURN(it->base, __VA_ARGS__); \
  ELM_GENLIST_CHECK(it->base->widget) __VA_ARGS__;

#define ELM_GENLIST_ITEM_CHECK_OR_GOTO(it, label)              \
  ELM_WIDGET_ITEM_CHECK_OR_GOTO(it->base, label); \
  if (!it->base->widget || !efl_isa                              \
        ((it->base->widget), ELM_GENLIST_CLASS)) goto label;

#define ELM_GENLIST_ITEM_DATA_GET(o, sd) \
  Elm_Gen_Item* sd = efl_data_scope_get(o, ELM_GENLIST_ITEM_CLASS)

#endif
