#ifndef _EO_BASE_EO_H_
#define _EO_BASE_EO_H_

#ifndef _EO_BASE_EO_CLASS_TYPE
#define _EO_BASE_EO_CLASS_TYPE

typedef Eo Eo_Base;

#endif

#ifndef _EO_BASE_EO_TYPES
#define _EO_BASE_EO_TYPES

/** This struct holds the description of a specific event.
 *
 * @ingroup Eo_Event
 */
typedef struct _Eo_Event_Description
{
  const char *name; /** name of the event. */
  Eina_Bool unfreezable; /** Eina_True if the event cannot be frozen. */
  Eina_Bool legacy_is; /** Internal use: if is a legacy event. */
  Eina_Bool restart; /** Eina_True if when the event is triggered again from a
                      * callback, it should start from where it was */
} Eo_Event_Description;

/** Parameter passed in event callbacks holding extra event parameters
 *
 * @ingroup Eo
 */
typedef struct _Eo_Event
{
  Eo_Base *object; /** The object the event was called on. */
  const Eo_Event_Description *desc; /** The event description. */
  void *info; /** Extra event information passed by the event caller. */
} Eo_Event;

/** An event callback prototype. return @c EO_CALLBACK_STOP to stop calling
 * additional callbacks for the event, @c EO_CALLBACK_CONTINUE to continue.
 *
 * @ingroup Eo
 */
typedef void (*Eo_Event_Cb)(void *data, const Eo_Event *event);

/**
 * @brief An item in an array of callback desc/func.
 *
 * See also @ref eo_event_callback_array_add.
 *
 * @ingroup Eo
 */
typedef struct _Eo_Callback_Array_Item
{
  const Eo_Event_Description *desc; /** The event description. */
  Eo_Event_Cb func; /** The callback function. */
} Eo_Callback_Array_Item;

/** The structure for the debug info used by Eo.
 *
 * @ingroup Eo
 */
typedef struct _Eo_Dbg_Info
{
  Eina_Stringshare *name; /** The name of the part (stringshare). */
  Eina_Value value; /** The value. */
} Eo_Dbg_Info;

/**
 * @brief Callback priority value. Range is -32k - 32k. The lower the number,
 * the higher the priority.
 *
 * See @ref EO_CALLBACK_PRIORITY_AFTER, @ref EO_CALLBACK_PRIORITY_BEFORE @ref
 * EO_CALLBACK_PRIORITY_DEFAULT
 *
 * @ingroup Eo
 */
typedef short Eo_Callback_Priority;


#endif
#define EO_BASE_CLASS eo_base_class_get()

EWAPI const Eo_Class *eo_base_class_get(void);

/**
 * @brief The parent of an object.
 *
 * Parents keep references to their children so in order to delete objects that
 * have parents you need to set parent to NULL or use eo_del() that does that
 * for you (and also unrefs the object).
 *
 * The Eo parent is conceptually user set. That means that a parent should not
 * be changed behind the scenes in a surprising manner.
 *
 * For example: if you have a widget that has a box internally, and when you
 * swallow into that widget the object you swallow ends up in the box, the
 * parent should be the widget, and not the box.
 *
 * @param[in] parent the new parent
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_parent_set(Eo *obj, Eo_Base *parent);

/**
 * @brief The parent of an object.
 *
 * Parents keep references to their children so in order to delete objects that
 * have parents you need to set parent to NULL or use eo_del() that does that
 * for you (and also unrefs the object).
 *
 * The Eo parent is conceptually user set. That means that a parent should not
 * be changed behind the scenes in a surprising manner.
 *
 * For example: if you have a widget that has a box internally, and when you
 * swallow into that widget the object you swallow ends up in the box, the
 * parent should be the widget, and not the box.
 *
 * @return the new parent
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_parent_get(const Eo *obj);

/**
 * @brief The name of the object.
 *
 * Every object can have a string name. Names may not contain the following
 * charactors: / ? * [ ] ! \ : They are illegal. Using it in a name will result
 * in undefined behavior later on. An empty string is considered the same as a
 * NULL string or no string for the name at all.
 *
 * @param[in] name the name
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_name_set(Eo *obj, const char *name);

/**
 * @brief The name of the object.
 *
 * Every object can have a string name. Names may not contain the following
 * charactors: / ? * [ ] ! \ : They are illegal. Using it in a name will result
 * in undefined behavior later on. An empty string is considered the same as a
 * NULL string or no string for the name at all.
 *
 * @return the name
 *
 * @ingroup Eo_Base
 */
EOAPI const char *eo_name_get(const Eo *obj);

/**
 * @brief A human readable comment for the object
 *
 * Every object can have a string comment intended for developers and
 * debugging. An empty string is considered the same as a NULL string or no
 * string for the comment at all.
 *
 * @param[in] comment the comment
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_comment_set(Eo *obj, const char *comment);

/**
 * @brief A human readable comment for the object
 *
 * Every object can have a string comment intended for developers and
 * debugging. An empty string is considered the same as a NULL string or no
 * string for the comment at all.
 *
 * @return the comment
 *
 * @ingroup Eo_Base
 */
EOAPI const char *eo_comment_get(const Eo *obj);

/**
 * @brief Return freeze events of object.
 *
 * Return event freeze count.
 *
 * @return The event freeze count of the object
 *
 * @ingroup Eo_Base
 */
EOAPI int eo_event_global_freeze_count_get(const Eo *obj);

/**
 * @brief Return freeze events of object.
 *
 * Return event freeze count.
 *
 * @return The event freeze count of the object
 *
 * @ingroup Eo_Base
 */
EOAPI int eo_event_freeze_count_get(const Eo *obj);

/** True if the object is already finalized, false otherwise.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_finalized_get(const Eo *obj);

/**
 * @brief Generic data with string key on an object.
 *
 * The user is in charge of freeing the data.
 *
 * @param[in] key the key associated with the data
 * @param[in] data the data to set
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_key_data_set(Eo *obj, const char * key, const void *data);

/**
 * @brief Generic data with string key on an object.
 *
 * The user is in charge of freeing the data.
 *
 * @param[in] key the key associated with the data
 *
 * @return the data to set
 *
 * @ingroup Eo_Base
 */
EOAPI void *eo_key_data_get(const Eo *obj, const char * key);

/**
 * @brief Generic object reference with string key to object.
 *
 * The object will be automatically ref'd when set and unref'd when replaced or
 * deleted or referring object is deleted. If the referenced object is deleted
 * then the key is deleted automatically.
 *
 * This is the same key store used by key_data and key_value so keys are shared
 * and can store only one thing
 *
 * @param[in] key the key associated with the object ref
 * @param[in] objdata the object to set
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_key_ref_set(Eo *obj, const char * key, const Eo_Base *objdata);

/**
 * @brief Generic object reference with string key to object.
 *
 * The object will be automatically ref'd when set and unref'd when replaced or
 * deleted or referring object is deleted. If the referenced object is deleted
 * then the key is deleted automatically.
 *
 * This is the same key store used by key_data and key_value so keys are shared
 * and can store only one thing
 *
 * @param[in] key the key associated with the object ref
 *
 * @return the object to set
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_key_ref_get(const Eo *obj, const char * key);

/**
 * @brief Generic weak object reference with string key to object.
 *
 * The object key will be removed if the object is removed, but will not take
 * or removed references like key_obj.
 *
 * This is the same key store used by key_data and key_value so keys are shared
 * and can store only one thing
 *
 * @param[in] key the key associated with the object ref
 * @param[in] objdata the object to set
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_key_wref_set(Eo *obj, const char * key, const Eo_Base *objdata);

/**
 * @brief Generic weak object reference with string key to object.
 *
 * The object key will be removed if the object is removed, but will not take
 * or removed references like key_obj.
 *
 * This is the same key store used by key_data and key_value so keys are shared
 * and can store only one thing
 *
 * @param[in] key the key associated with the object ref
 *
 * @return the object to set
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_key_wref_get(const Eo *obj, const char * key);

/**
 * @brief Value on with string key on the object.
 *
 * This stores the value with the given string key on the object and it will be
 * freed when replaced or deleted or the referring object is deleted.
 *
 * This is the same key store used by key_data and key_obj so keys are shared
 * and can store only one thing
 *
 * @param[in] key the key associated with the value
 * @param[in] value the value to set
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_key_value_set(Eo *obj, const char * key, Eina_Value *value);

/**
 * @brief Value on with string key on the object.
 *
 * This stores the value with the given string key on the object and it will be
 * freed when replaced or deleted or the referring object is deleted.
 *
 * This is the same key store used by key_data and key_obj so keys are shared
 * and can store only one thing
 *
 * @param[in] key the key associated with the value
 *
 * @return the value to set
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Value *eo_key_value_get(const Eo *obj, const char * key);

/**
 * @brief Unrefs the object and reparents it to NULL.
 *
 * Because eo_del() unrefs and reparents to NULL, it doesn't really delete the
 * object.
 *
 * This method accepts a const object for convenience, so all objects could be
 * passed to it easily.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_del(const Eo *obj);

/**
 * @brief Searches up in the object tree for a provider which knows the given
 * class/interface.
 *
 * The object from the provider will then be returned. The base implementation
 * calls the provider_find function on the object parent, and returnes its
 * result. If no parent is present NULL is returned. Each implementation has to
 * support this function by overriding it and returning itself if the interface
 * matches the parameter. If this is not done the class cannot be found up in
 * the object tree.
 *
 * @param[in] klass The class identifier to search for
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_provider_find(Eo *obj, const Eo_Base *klass);

/**
 * @brief Call the object's constructor.
 *
 * Should not be used with #eo_do. Only use it with #eo_do_super.
 *
 * @return The new object created, can be NULL if aborting
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_constructor(Eo *obj);

/**
 * @brief Call the object's destructor.
 *
 * Should not be used with #eo_do. Only use it with #eo_do_super.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_destructor(Eo *obj);

/**
 * @brief Called at the end of #eo_add. Should not be called, just overridden.
 *
 * @return The new object created, can be NULL if aborting
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_finalize(Eo *obj);

/**
 * @brief Find a child object with the given name and return it. The search
 * string can be a glob (shell style). It can also specify class name in the
 * format of "class:name" where ":" separates class and name. Both class and
 * name can be globs. If class is specified, and name is empty like "class:"
 * then the search will match any object of that class.
 *
 * @param[in] search the name search string
 *
 * @return the first object found
 *
 * @ingroup Eo_Base
 */
EOAPI Eo_Base *eo_name_find(Eo *obj, const char *search);

/**
 * @brief Add a new weak reference to obj.
 *
 * This function registers the object handle pointed by wref to obj so when obj
 * is deleted it'll be updated to NULL. This functions should be used when you
 * want to keep track of an object in a safe way, but you don't want to prevent
 * it from being freed.
 *
 * @param[in] wref
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_wref_add(Eo *obj, Eo_Base **wref);

/**
 * @brief Delete the weak reference passed.
 *
 * @param[in] wref
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_wref_del(Eo *obj, Eo_Base **wref);

/**
 * @brief thaw events of object.
 *
 * Lets event callbacks be called for the object.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_thaw(Eo *obj);

/**
 * @brief freeze events of object.
 *
 * Prevents event callbacks from being called for the object.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_freeze(Eo *obj);

/**
 * @brief thaw events of object.
 *
 * Lets event callbacks be called for the object.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_global_thaw(const Eo *obj);

/**
 * @brief freeze events of object.
 *
 * Prevents event callbacks from being called for the object.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_global_freeze(const Eo *obj);

/**
 * @brief Add a callback for an event with a specific priority.
 *
 * callbacks of the same priority are called in reverse order of creation.
 *
 * @param[in] desc The description of the event to listen to
 * @param[in] priority The priority of the callback
 * @param[in] cb the callback to call
 * @param[in] data additional data to pass to the callback
 *
 * @return Return true when the callback has been successfully added.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_event_callback_priority_add(Eo *obj, const Eo_Event_Description *desc, Eo_Callback_Priority priority, Eo_Event_Cb cb, const void *data);

/**
 * @brief Del a callback with a specific data associated to it for an event.
 *
 * @param[in] desc The description of the event to listen to
 * @param[in] func the callback to delete
 * @param[in] user_data The data to compare
 *
 * @return Return true when the callback has been successfully removed.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_event_callback_del(Eo *obj, const Eo_Event_Description *desc, Eo_Event_Cb func, const void *user_data);

/**
 * @brief Add a callback array for an event with a specific priority.
 *
 * callbacks of the same priority are called in reverse order of creation.
 *
 * @param[in] array an #Eo_Callback_Array_Item of events to listen to
 * @param[in] priority The priority of the callback
 * @param[in] data additional data to pass to the callback
 *
 * @return Return true when the callback has been successfully added.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_event_callback_array_priority_add(Eo *obj, const Eo_Callback_Array_Item *array, Eo_Callback_Priority priority, const void *data);

/**
 * @brief Del a callback array with a specific data associated to it for an
 * event.
 *
 * @param[in] array an #Eo_Callback_Array_Item of events to listen to
 * @param[in] user_data The data to compare
 *
 * @return Return true when the callback has been successfully removed.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_event_callback_array_del(Eo *obj, const Eo_Callback_Array_Item *array, const void *user_data);

/**
 * @brief Call the callbacks for an event of an object.
 *
 * @param[in] desc The description of the event to call
 * @param[in] event_info Extra event info to pass to the callbacks
 *
 * @return @c false if one of the callbacks aborted the call, @c true otherwise
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_event_callback_call(Eo *obj, const Eo_Event_Description *desc, void *event_info);

/**
 * @brief Stop the current callback call.
 *
 * This stops the current callback call. Any other callbacks for the current
 * event will not be called. This is useful when you want to filter out events.
 * You just add higher priority events and call this on certain conditions to
 * block a certain event.
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_callback_stop(Eo *obj);

/**
 * @brief Add an event callback forwarder for an event and an object.
 *
 * @param[in] desc The description of the event to listen to
 * @param[in] new_obj The object to emit events from
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_callback_forwarder_add(Eo *obj, const Eo_Event_Description *desc, Eo_Base *new_obj);

/**
 * @brief Remove an event callback forwarder for an event and an object.
 *
 * @param[in] desc The description of the event to listen to
 * @param[in] new_obj The object to emit events from
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_event_callback_forwarder_del(Eo *obj, const Eo_Event_Description *desc, Eo_Base *new_obj);

/**
 * @brief Get dbg information from the object.
 *
 * @param[in] root_node node of the tree
 *
 * @ingroup Eo_Base
 */
EOAPI void eo_dbg_info_get(Eo *obj, Eo_Dbg_Info *root_node);

/** Get an iterator on all childrens
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Iterator *eo_children_iterator_new(Eo *obj);

/**
 * @brief Make an object a composite object of another.
 *
 * The class of comp_obj must be part of the extensions of the class of the
 * parent. It is not possible to attach more then 1 composite of the same
 * class. This functions also sets the parent of comp_obj to parent.
 *
 * See @ref eo_composite_detach, @ref eo_composite_part_is.
 *
 * @param[in] comp_obj the object that will be used to composite the parent.
 *
 * @return @c true if successful. @c false otherwise.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_composite_attach(Eo *obj, Eo_Base *comp_obj);

/**
 * @brief Detach a composite object from another object.
 *
 * This functions also sets the parent of comp_obj to @c null.
 *
 * See @ref eo_composite_attach, @ref eo_composite_part_is.
 *
 * @param[in] comp_obj the object that will be removed from the parent.
 *
 * @return @c true if successful. @c false otherwise.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_composite_detach(Eo *obj, Eo_Base *comp_obj);

/**
 * @brief Check if an object is part of a composite object.
 *
 * See @ref eo_composite_attach, @ref eo_composite_part_is.
 *
 * @return @c true if it is. @c false otherwise.
 *
 * @ingroup Eo_Base
 */
EOAPI Eina_Bool eo_composite_part_is(Eo *obj);

EOAPI extern const Eo_Event_Description _EO_EVENT_CALLBACK_ADD;
EOAPI extern const Eo_Event_Description _EO_EVENT_CALLBACK_DEL;
EOAPI extern const Eo_Event_Description _EO_EVENT_DEL;

/** A callback was added.
 *
 * @ingroup Eo_Base
 */
#define EO_EVENT_CALLBACK_ADD (&(_EO_EVENT_CALLBACK_ADD))

/** A callback was deleted.
 *
 * @ingroup Eo_Base
 */
#define EO_EVENT_CALLBACK_DEL (&(_EO_EVENT_CALLBACK_DEL))

/** Obj is being deleted.
 *
 * @ingroup Eo_Base
 */
#define EO_EVENT_DEL (&(_EO_EVENT_DEL))

#endif
