#include "edje_private.h"

static void _edje_emit_cb(Edje *ed, const char *sig, const char *src, Edje_Message_Signal_Data *data, Eina_Bool prop);
static void _edje_param_copy(Edje *ed, Edje_Real_Part *src_part, const char *src_param, Edje_Real_Part *dst_part, const char *dst_param);
static void _edje_param_set(Edje *ed, Edje_Real_Part *part, const char *param, const char *value);

static double _edje_transition_duration_scale = 0;

static Eina_Bool
_edje_animator_cb(void *data)
{
   const Efl_Event event = { NULL, NULL, NULL };
   _edje_timer_cb(data, &event);
   return EINA_TRUE;
}

static Eina_Bool
_edje_emit_aliased(Edje *ed, const char *part, const char *sig, const char *src)
{
   char *alias, *aliased;
   int alien, nslen, length;

   /* lookup for alias */
   if ((!ed->collection) || (!ed->collection->alias)) return EINA_FALSE;
   alias = eina_hash_find(ed->collection->alias, part);
   if (!alias) return EINA_FALSE;

   alien = strlen(alias);
   nslen = strlen(sig);
   length = alien + nslen + 2;

   aliased = alloca(length);
   memcpy(aliased, alias, alien);
   aliased[alien] = EDJE_PART_PATH_SEPARATOR;
   memcpy(aliased + alien + 1, sig, nslen + 1);

   _edje_emit(ed, aliased, src);
   return EINA_TRUE;
}

static Eina_Bool
_edje_emit_child(Edje *ed, Edje_Real_Part *rp, const char *part, const char *sig, const char *src)
{
   Edje *ed2;
   char *idx;

   /* search for the index if present and remove it from the part */
   idx = strchr(part, EDJE_PART_PATH_SEPARATOR_INDEXL);
   if (idx)
     {
        char *end;

        end = strchr(idx + 1, EDJE_PART_PATH_SEPARATOR_INDEXR);
        if (end && end != idx + 1)
          {
             char *tmp;

             tmp = alloca(end - idx);
             memcpy(tmp, idx + 1, end - idx - 1);
             tmp[end - idx - 1] = '\0';
             *idx = '\0';
             idx = tmp;
          }
        else
          {
             idx = NULL;
          }
     }

   /* search for the right part now */
   if (!rp)
     rp = _edje_real_part_get(ed, part);
   if (!rp) return ed->collection->broadcast_signal;

   switch (rp->part->type)
     {
      case EDJE_PART_TYPE_GROUP:
        if (((rp->type != EDJE_RP_TYPE_SWALLOW) ||
             (!rp->typedata.swallow)) ||
            (!rp->typedata.swallow->swallowed_object))
          break;
        ed2 = _edje_fetch(rp->typedata.swallow->swallowed_object);
        if (!ed2) break;

        _edje_emit(ed2, sig, src);
        return EINA_FALSE;

      case EDJE_PART_TYPE_EXTERNAL:
        if (((rp->type != EDJE_RP_TYPE_SWALLOW) ||
             (!rp->typedata.swallow)) ||
            (!rp->typedata.swallow->swallowed_object))
          break;

        if (!idx)
          {
             _edje_external_signal_emit(rp->typedata.swallow->swallowed_object, sig, src);
          }
        else
          {
             Evas_Object *child;

             child = _edje_children_get(rp, idx);
             ed2 = _edje_fetch(child);
             if (!ed2) break;
             _edje_emit(ed2, sig, src);
          }
        return EINA_FALSE;

      case EDJE_PART_TYPE_BOX:
      case EDJE_PART_TYPE_TABLE:
        if (idx)
          {
             Evas_Object *child;

             child = _edje_children_get(rp, idx);
             ed2 = _edje_fetch(child);
             if (!ed2) break;
             _edje_emit(ed2, sig, src);
             return EINA_FALSE;
          }
        break;

      default:
        //              ERR("Unknown part type. You should never be here!");
        break;
     }
   return ed->collection->broadcast_signal;
}

static Edje_Message_Signal_Data *
_edje_signal_data_setup(void *data, Ecore_Cb free_func, void *seat_data, Ecore_Cb seat_free_func)
{
   Edje_Message_Signal_Data *out = NULL;

   if (data || seat_data)
     {
        out = calloc(1, sizeof(*out));
        if (!out) return NULL;

        out->ref = 1;
        out->data = data;
        out->free_func = free_func;
        out->seat_data = seat_data;
        out->seat_free_func = seat_free_func;
     }
   return out;
}

void
_edje_signal_data_free(Edje_Message_Signal_Data *mdata)
{
   if (!mdata) return;
   if (--(mdata->ref)) return;

   if (mdata->free_func)
     {
        mdata->free_func(mdata->data);
     }
   if (mdata->seat_free_func)
     {
        mdata->seat_free_func(mdata->seat_data);
     }
   free(mdata);
}

void
_edje_signal_data_ref(Edje_Message_Signal_Data *mdata)
{
   if (mdata) mdata->ref++;
}

static void
_edje_emit_send(Edje *ed, Eina_Bool broadcast, const char *sig, const char *src, Edje_Message_Signal_Data *mdata)
{
   Edje_Message_Signal emsg;

   emsg.sig = sig;
   emsg.src = src;
   emsg.data = mdata;
   /* new sends code */
   if (broadcast)
     edje_object_message_send(ed->obj, EDJE_MESSAGE_SIGNAL, 0, &emsg);
   else
     _edje_util_message_send(ed, EDJE_QUEUE_SCRIPT, EDJE_MESSAGE_SIGNAL, 0, &emsg);
   /* old send code - use api now
      _edje_util_message_send(ed, EDJE_QUEUE_SCRIPT, EDJE_MESSAGE_SIGNAL, 0, &emsg);
      EINA_LIST_FOREACH(ed->subobjs, l, obj)
      {
      Edje *ed2;

      ed2 = _edje_fetch(obj);
      if (!ed2) continue;
      if (ed2->delete_me) continue;
      _edje_util_message_send(ed2, EDJE_QUEUE_SCRIPT, EDJE_MESSAGE_SIGNAL, 0, &emsg);
      }
    */
}

/*============================================================================*
*                                   API                                      *
*============================================================================*/

EOLIAN Eina_Stringshare*
_efl_canvas_layout_seat_name_get(const Eo *obj EINA_UNUSED, Edje *ed, Efl_Input_Device *device)
{
   return _edje_seat_name_get(ed, device);
}

EOLIAN Efl_Input_Device *
_efl_canvas_layout_seat_get(const Eo *obj EINA_UNUSED, Edje *ed, Eina_Stringshare *name)
{
   return _edje_seat_get(ed, name);
}

EAPI void
edje_frametime_set(double t)
{
   ecore_animator_frametime_set(t);
}

EAPI double
edje_frametime_get(void)
{
   return ecore_animator_frametime_get();
}

EAPI double
edje_transition_duration_factor_get(void)
{
   return _edje_transition_duration_scale;
}

EAPI void
edje_transition_duration_factor_set(double scale)
{
   _edje_transition_duration_scale = FROM_DOUBLE(scale);
}

Eina_Bool
_edje_object_signal_callback_add(Evas_Object *obj, Edje *ed,
                                 const char *emission, const char *source,
                                 Edje_Signal_Cb func_legacy,
                                 Efl_Signal_Cb func_eo, Eina_Free_Cb func_free_cb, void *data)
{
   Edje_Signal_Callback_Group *gp;
   const char *sig;
   const char *src;
   Eina_Bool ok;

   if (!ed->callbacks)
     ed->callbacks = _edje_signal_callback_alloc();
   if (!ed->callbacks) return EINA_FALSE;

   sig = eina_stringshare_add(emission);
   src = eina_stringshare_add(source);

   // Only load seat callbacks and trigger events just before we might need them.
   if (!ed->need_seat && sig && !strncmp(sig, "seat,", 5))
     {
        ed->need_seat = EINA_TRUE;
        if (ed->collection)
          _edje_devices_add(ed, evas_object_evas_get(obj));
     }

   gp = (Edje_Signal_Callback_Group *) ed->callbacks;
   ok = _edje_signal_callback_push(gp, sig, src, func_legacy, func_eo, func_free_cb, data, EINA_TRUE);

   eina_stringshare_del(sig);
   eina_stringshare_del(src);

   return ok;
}

void
edje_object_propagate_callback_add(Evas_Object *obj, Efl_Signal_Cb func, void *data)
{
   Edje *ed;

   ed = _edje_fetch(obj);
   if (!ed || ed->delete_me) return;
   _edje_object_signal_callback_add(obj, ed, "*", "*", func, NULL, NULL, data);
}

Eina_Bool
_efl_canvas_layout_efl_layout_signal_signal_callback_add(Eo *obj EINA_UNUSED, Edje *ed, const char *emission, const char *source, void *func_data, EflLayoutSignalCb func, Eina_Free_Cb func_free_cb)
{
   return _edje_object_signal_callback_add(obj, ed, emission, source, NULL, func, func_free_cb, func_data);
}

Eina_Bool
_efl_canvas_layout_efl_layout_signal_signal_callback_del(Eo *obj EINA_UNUSED, Edje *ed, const char *emission, const char *source, void *func_data, EflLayoutSignalCb func, Eina_Free_Cb func_free_cb)
{
   Edje_Signal_Callback_Group *gp;
   Eina_Bool ok;

   if (ed->delete_me) return EINA_FALSE;
   if ((!emission) || (!source) || (!func)) return EINA_FALSE;

   gp = (Edje_Signal_Callback_Group *) ed->callbacks;
   if (!gp) return EINA_FALSE;

   emission = eina_stringshare_add(emission);
   source = eina_stringshare_add(source);

   ok = _edje_signal_callback_disable(gp, emission, source, NULL, func, func_free_cb, func_data);

   eina_stringshare_del(emission);
   eina_stringshare_del(source);

   return ok;
}

EOLIAN void
_efl_canvas_layout_efl_layout_signal_signal_emit(Eo *obj EINA_UNUSED, Edje *ed, const char *emission, const char *source)
{
   if (ed->delete_me) return;
   if ((!emission) || (!source)) return;
   _edje_emit(ed, emission, source);
}

/* FIXDOC: Verify/Expand */
EOLIAN void
_efl_canvas_layout_animated_set(Eo *obj, Edje *ed, Eina_Bool on)
{
   Eina_List *l;
   unsigned short i;

   if (!ed) return;
   if (ed->delete_me) return;
   _edje_block(ed);
   ed->no_anim = !on;
   _edje_util_freeze(ed);
   if (!on)
     {
        Eina_List *newl = NULL;
        Edje_Running_Program *data;

        EINA_LIST_FOREACH(ed->actions, l, data)
          {
             data->ref++;
             newl = eina_list_append(newl, data);
          }
        while (newl)
          {
             Edje_Running_Program *runp;

             runp = eina_list_data_get(newl);
             newl = eina_list_remove(newl, eina_list_data_get(newl));
             runp->ref--;
             _edje_program_run_iterate(runp, runp->start_time + TO_DOUBLE(runp->program->tween.time));
             if (_edje_block_break(ed))
               {
                 EINA_LIST_FREE(newl, data)
                    {
                       data->ref--;
                       if ((data->delete_me) && (data->ref == 0))
                         {
                            _edje_program_run_cleanup(ed, data);
                            free(data);
                         }
                    }
                  goto break_prog;
               }
          }
     }
   else
     {
        _edje_emit(ed, "load", NULL);
        if (evas_object_visible_get(obj))
          {
             evas_object_hide(obj);
             evas_object_show(obj);
          }
     }
break_prog:

   for (i = 0; i < ed->table_parts_size; i++)
     {
        Edje_Real_Part *rp;
        rp = ed->table_parts[i];
        if ((rp->part->type == EDJE_PART_TYPE_GROUP) &&
            ((rp->type == EDJE_RP_TYPE_SWALLOW) &&
             (rp->typedata.swallow)) &&
            (rp->typedata.swallow->swallowed_object))
          edje_object_animation_set(rp->typedata.swallow->swallowed_object, on);
     }

   _edje_util_thaw(ed);
   _edje_unblock(ed);
}

EOLIAN Eina_Bool
_efl_canvas_layout_animated_get(const Eo *obj EINA_UNUSED, Edje *ed)
{
   if (!ed) return EINA_FALSE;
   if (ed->delete_me) return EINA_FALSE;
   if (ed->no_anim) return EINA_FALSE;

   return EINA_TRUE;
}

/* Private Routines */
void
_edje_program_run_cleanup(Edje *ed, Edje_Running_Program *runp)
{
   ed->actions = eina_list_remove(ed->actions, runp);
   if (!ed->actions)
     {
        efl_event_callback_del(ed->obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _edje_timer_cb, ed);
        ecore_animator_del(ed->animator);
        ed->animator = NULL;
     }
}

Eina_Bool
_edje_program_run_iterate(Edje_Running_Program *runp, double tim)
{
   FLOAT_T t, total, t_scale = runp->edje->duration_scale;
   Eina_List *l;
   Edje *ed;
   Edje_Program_Target *pt;
   Edje_Real_Part *rp;

   ed = runp->edje;
   if (ed->delete_me) return EINA_FALSE;
   _edje_block(ed);
   _edje_ref(ed);
   _edje_util_freeze(ed);

   if (runp->program->tween.use_duration_factor)
     t_scale = _edje_transition_duration_scale;
   t = FROM_DOUBLE(tim - runp->start_time);
   total = runp->program->tween.time * t_scale;
   t = DIV(t, total);
   if (t > FROM_INT(1)) t = FROM_INT(1);
   EINA_LIST_FOREACH(runp->program->targets, l, pt)
     {
        if (pt->id >= 0)
          {
             rp = ed->table_parts[pt->id % ed->table_parts_size];
             if (rp)
               _edje_part_pos_set(ed, rp,
                                  runp->program->tween.mode, t,
                                  runp->program->tween.v1,
                                  runp->program->tween.v2,
                                  runp->program->tween.v3,
                                  runp->program->tween.v4);
          }
     }
   if (t >= FROM_INT(1))
     {
        Edje_Program_After *pa;

        EINA_LIST_FOREACH(runp->program->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if (rp)
                    {
                       _edje_part_description_apply(ed, rp,
                                                    runp->program->state,
                                                    runp->program->value,
                                                    NULL,
                                                    0.0);
                       _edje_part_pos_set(ed, rp,
                                          runp->program->tween.mode, ZERO,
                                          runp->program->tween.v1,
                                          runp->program->tween.v2,
                                          runp->program->tween.v3,
                                          runp->program->tween.v4);
                       rp->program = NULL;
                    }
               }
          }
        _edje_recalc(ed);
        runp->delete_me = EINA_TRUE;
        if (!ed->walking_actions)
          _edje_program_run_cleanup(ed, runp);
        //	_edje_emit(ed, "program,stop", runp->program->name);
        if (_edje_block_break(ed))
          {
             if (!ed->walking_actions)
               {
                  if (runp->ref == 0)
                    {
                       _edje_program_run_cleanup(ed, runp);
                       free(runp);
                    }
               }
             goto break_prog;
          }
        EINA_LIST_FOREACH(runp->program->after, l, pa)
          {
             Edje_Program *pr;

             if (pa->id >= 0)
               {
                  pr = ed->collection->patterns.table_programs[pa->id % ed->collection->patterns.table_programs_size];
                  if (pr) _edje_program_run(ed, pr, 0, "", "", NULL);
                  if (_edje_block_break(ed))
                    {
                       if ((!ed->walking_actions) && (runp->ref == 0))
                         {
                            _edje_program_run_cleanup(ed, runp);
                            free(runp);
                         }
                       goto break_prog;
                    }
               }
          }
        _edje_util_thaw(ed);
        _edje_unref(ed);
        if ((!ed->walking_actions) && (runp->ref == 0))
          {
             _edje_program_run_cleanup(ed, runp);
             free(runp);
          }
        _edje_unblock(ed);
        return EINA_FALSE;
     }
break_prog:
   _edje_recalc(ed);
   _edje_util_thaw(ed);
   _edje_unref(ed);
   _edje_unblock(ed);
   return EINA_TRUE;
}

void
_edje_program_end(Edje *ed, Edje_Running_Program *runp)
{
   Eina_List *l;
   Edje_Program_Target *pt;
   //   const char *pname = NULL;
   int free_runp = 0;

   if (ed->delete_me) return;
   _edje_ref(ed);
   _edje_util_freeze(ed);
   EINA_LIST_FOREACH(runp->program->targets, l, pt)
     {
        Edje_Real_Part *rp;

        if (pt->id >= 0)
          {
             rp = ed->table_parts[pt->id % ed->table_parts_size];
             if (rp)
               {
                  _edje_part_description_apply(ed, rp,
                                               runp->program->state,
                                               runp->program->value,
                                               NULL,
                                               0.0);
                  _edje_part_pos_set(ed, rp,
                                     runp->program->tween.mode, ZERO,
                                     runp->program->tween.v1,
                                     runp->program->tween.v2,
                                     runp->program->tween.v3,
                                     runp->program->tween.v4);

                  rp->program = NULL;
               }
          }
     }
   _edje_recalc(ed);
   runp->delete_me = EINA_TRUE;
   //   pname = runp->program->name;
   if (!ed->walking_actions)
     {
        _edje_program_run_cleanup(ed, runp);
        free_runp = 1;
     }
   //   _edje_emit(ed, "program,stop", pname);
   _edje_util_thaw(ed);
   _edje_unref(ed);
   if ((free_runp) && (runp->ref == 0)) free(runp);
}

#ifdef HAVE_EPHYSICS
static Eina_Bool
_edje_physics_action_set(Edje *ed, Edje_Program *pr, void (*func)(EPhysics_Body *body, double x, double y, double z))
{
   Edje_Program_Target *pt;
   Edje_Real_Part *rp;
   Eina_List *l;

   if (_edje_block_break(ed)) return EINA_FALSE;

   EINA_LIST_FOREACH(pr->targets, l, pt)
     {
        if (pt->id >= 0)
          {
             rp = ed->table_parts[pt->id % ed->table_parts_size];
             if ((rp) && (rp->body))
               func(rp->body, pr->physics.x, pr->physics.y, pr->physics.z);
          }
     }

   return EINA_TRUE;
}

#endif

static void
_edje_seat_name_emit(Edje *ed, const char *name, const char *sig, const char *src)
{
   char buf[128];

   /* keep sending signals without seat information for legacy compatibility */
   _edje_emit_full(ed, sig, src, NULL, NULL);

   if (!name) return;

   snprintf(buf, sizeof(buf), "seat,%s,%s", name, sig);
   _edje_emit_full(ed, buf, src, NULL, NULL);
}

void
_edje_part_focus_set(Edje *ed, const char *seat_name, Edje_Real_Part *rp)
{
   Edje_Real_Part *focused_part;
   Eina_Stringshare *sname;
   Efl_Input_Device *seat;
   Evas *e;

   if (seat_name)
     sname = eina_stringshare_add(seat_name);
   else /* Use default seat name */
     {
        e = evas_object_evas_get(ed->obj);
        seat = evas_default_device_get(e, EVAS_DEVICE_CLASS_SEAT);
        sname = eina_stringshare_ref(_edje_seat_name_get(ed, seat));
     }

   focused_part = _edje_focused_part_get(ed, sname);

   if (focused_part != rp)
     {
        if (rp && (rp->part->allowed_seats) &&
            (!_edje_part_allowed_seat_find(rp, sname)))
          goto not_allowed;

        if (focused_part)
          _edje_seat_name_emit(ed, sname, "focus,part,out",
                               focused_part->part->name);
        _edje_focused_part_set(ed, sname, rp);
        if (rp)
          _edje_seat_name_emit(ed, sname, "focus,part,in", rp->part->name);
     }

not_allowed:
   eina_stringshare_del(sname);
}

void
_edje_program_run(Edje *ed, Edje_Program *pr, Eina_Bool force, const char *ssig, const char *ssrc, Edje_Message_Signal_Data *mdata)
{
   Eina_List *l;
   Edje_Real_Part *rp;
   Edje_Program_Target *pt;
   Edje_Program *pr2;
   Edje_Program_After *pa;
   /* limit self-feeding loops in programs to 64 levels */
   static int recursions = 0;
   static int recursion_limit = 0;

   if (ed->delete_me) return;
   if ((pr->in.from > 0.0) && (pr->in.range >= 0.0) && (!force))
     {
        Edje_Pending_Program *pp;
        double r = 0.0;

        pp = calloc(1, sizeof(Edje_Pending_Program));
        if (!pp) return;
        if (pr->in.range > 0.0) r = ((double)rand() / RAND_MAX);
        pp->timer = ecore_timer_add(pr->in.from + (pr->in.range * r),
                                    _edje_pending_timer_cb, pp);
        if (!pp->timer)
          {
             free(pp);
             return;
          }
        pp->edje = ed;
        pp->program = pr;
        ed->pending_actions = eina_list_append(ed->pending_actions, pp);
        return;
     }
   if ((recursions >= 64) || (recursion_limit))
     {
        ERR("Programs recursing up to recursion limit of %i in '%s' with sig='%s', src='%s' from '%s', '%s'. Disabled.",
            64, pr->name, ssig, ssrc, ed->path, ed->group);
        if (pr->action == EDJE_ACTION_TYPE_STATE_SET && (EQ(pr->tween.time, ZERO) || (ed->no_anim)))
          ERR("Possible solution: try adding transition time to prevent Schrödinger's part state");
        recursion_limit = 1;
        return;
     }
   recursions++;
   _edje_block(ed);
   _edje_ref(ed);
   _edje_util_freeze(ed);
   switch (pr->action)
     {
      case EDJE_ACTION_TYPE_STATE_SET:
        if ((pr->tween.time > ZERO) && (!ed->no_anim))
          {
             Edje_Running_Program *runp;

             runp = calloc(1, sizeof(Edje_Running_Program));
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if (rp)
                         {
                            if ((rp->object) && (pr->tween.mode & EDJE_TWEEN_MODE_OPT_FROM_CURRENT))
                              {
                                 Edje_Calc_Params *tmp;

                                 tmp = calloc(1, sizeof(Edje_Calc_Params));
                                 if (!tmp) goto low_mem_current;
                                 _edje_part_recalc(ed, rp, FLAG_XY, tmp);

                                 if (rp->current)
                                   {
#ifdef EDJE_CALC_CACHE
                                      _edje_calc_params_clear(rp->current);
#endif
                                      free(rp->current);
                                   }
                                 rp->current = tmp;
                              }
                            else
                              {
low_mem_current:
                                 if (rp->current)
                                   {
#ifdef EDJE_CALC_CACHE
                                      _edje_calc_params_clear(rp->current);
#endif
                                      free(rp->current);
                                   }
                                 rp->current = NULL;
                              }

                            if (rp->program)
                              _edje_program_end(ed, rp->program);
                            _edje_part_description_apply(ed, rp,
                                                         rp->param1.description->state.name,
                                                         rp->param1.description->state.value,
                                                         pr->state,
                                                         pr->value);
                            _edje_part_pos_set(ed, rp, pr->tween.mode, ZERO,
                                               pr->tween.v1,
                                               pr->tween.v2,
                                               pr->tween.v3,
                                               pr->tween.v4);
                            rp->program = runp;
                         }
                    }
               }
             // _edje_emit(ed, "program,start", pr->name);
             if (_edje_block_break(ed))
               {
                  ed->actions = eina_list_append(ed->actions, runp);
                  goto break_prog;
               }

             if (!ed->actions)
               {
                  if (ed->canvas_animator)
                    efl_event_callback_add(ed->obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _edje_timer_cb, ed);
                  else
                    ed->animator = ecore_animator_add(_edje_animator_cb, ed);
               }
             ed->actions = eina_list_append(ed->actions, runp);

             runp->start_time = ecore_loop_time_get();
             runp->edje = ed;
             runp->program = pr;
          }
        else
          {
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if (rp)
                         {
                            if (rp->program)
                              _edje_program_end(ed, rp->program);
                            _edje_part_description_apply(ed, rp,
                                                         pr->state,
                                                         pr->value,
                                                         NULL,
                                                         0.0);
                            _edje_part_pos_set(ed, rp, pr->tween.mode, ZERO,
                                               pr->tween.v1,
                                               pr->tween.v2,
                                               pr->tween.v3,
                                               pr->tween.v4);
                         }
                    }
               }
             // _edje_emit(ed, "program,start", pr->name);
             if (_edje_block_break(ed)) goto break_prog;
             // _edje_emit(ed, "program,stop", pr->name);
             if (_edje_block_break(ed)) goto break_prog;

             EINA_LIST_FOREACH(pr->after, l, pa)
               {
                  if (pa->id >= 0)
                    {
                       pr2 = ed->collection->patterns.table_programs[pa->id % ed->collection->patterns.table_programs_size];
                       if (pr2) _edje_program_run(ed, pr2, 0, "", "", mdata);
                       if (_edje_block_break(ed)) goto break_prog;
                    }
               }
             _edje_recalc(ed);
          }
        break;

      case EDJE_ACTION_TYPE_ACTION_STOP:
        // _edje_emit(ed, "program,start", pr->name);
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             Eina_List *ll;
             Edje_Running_Program *runp;
             Edje_Pending_Program *pp;

             for (ll = ed->actions; ll; )
               {
                  runp = ll->data;
                  ll = ll->next;
                  if (pt->id == runp->program->id)
                    {
                       _edje_program_end(ed, runp);
                       //		       goto done;
                    }
               }
             for (ll = ed->pending_actions; ll; )
               {
                  pp = ll->data;
                  ll = ll->next;
                  if (pt->id == pp->program->id)
                    {
                       ed->pending_actions = eina_list_remove(ed->pending_actions, pp);
                       ecore_timer_del(pp->timer);
                       pp->timer = NULL;
                       free(pp);
                       //		       goto done;
                    }
               }
             //	     done:
             //	        continue;
          }
        // _edje_emit(ed, "program,stop", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        break;

      case EDJE_ACTION_TYPE_SIGNAL_EMIT:
        // _edje_emit(ed, "program,start", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        if (pr->targets)
          {
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id < 0) continue;
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if (!rp) continue;
                  if (!_edje_emit_aliased(ed, rp->part->name, pr->state, pr->state2))
                    {
                       Eina_Bool broadcast;

                       broadcast = _edje_emit_child(ed, rp, rp->part->name, pr->state, pr->state2);
                       _edje_emit_send(ed, broadcast, pr->state, pr->state2, mdata);
                    }
               }
          }
        else
          _edje_emit_full_data(ed, pr->state, pr->state2, mdata);
        if (_edje_block_break(ed)) goto break_prog;
        // _edje_emit(ed, "program,stop", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        break;

      case EDJE_ACTION_TYPE_DRAG_VAL_SET:
        // _edje_emit(ed, "program,start", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if ((rp) && (rp->drag) && (rp->drag->down.count == 0))
                    {
                       rp->drag->val.x = pr->value;
                       rp->drag->val.y = pr->value2;
                       if (rp->drag->val.x < 0.0) rp->drag->val.x = 0.0;
                       else if (rp->drag->val.x > 1.0)
                         rp->drag->val.x = 1.0;
                       if (rp->drag->val.y < 0.0) rp->drag->val.y = 0.0;
                       else if (rp->drag->val.y > 1.0)
                         rp->drag->val.y = 1.0;
                       _edje_dragable_pos_set(ed, rp, rp->drag->val.x, rp->drag->val.y);
                       _edje_emit(ed, "drag,set", rp->part->name);
                       if (_edje_block_break(ed)) goto break_prog;
                    }
               }
          }
        // _edje_emit(ed, "program,stop", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        break;

      case EDJE_ACTION_TYPE_DRAG_VAL_STEP:
        // _edje_emit(ed, "program,start", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if ((rp) && (rp->drag) && (rp->drag->down.count == 0))
                    {
                       rp->drag->val.x += pr->value * rp->drag->step.x * rp->part->dragable.x;
                       rp->drag->val.y += pr->value2 * rp->drag->step.y * rp->part->dragable.y;
                       if (rp->drag->val.x < 0.0) rp->drag->val.x = 0.0;
                       else if (rp->drag->val.x > 1.0)
                         rp->drag->val.x = 1.0;
                       if (rp->drag->val.y < 0.0) rp->drag->val.y = 0.0;
                       else if (rp->drag->val.y > 1.0)
                         rp->drag->val.y = 1.0;
                       _edje_dragable_pos_set(ed, rp, rp->drag->val.x, rp->drag->val.y);
                       _edje_emit(ed, "drag,step", rp->part->name);
                       if (_edje_block_break(ed)) goto break_prog;
                    }
               }
          }
        // _edje_emit(ed, "program,stop", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        break;

      case EDJE_ACTION_TYPE_DRAG_VAL_PAGE:
        // _edje_emit(ed, "program,start", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if ((rp) && (rp->drag) && (rp->drag->down.count == 0))
                    {
                       rp->drag->val.x += pr->value * rp->drag->page.x * rp->part->dragable.x;
                       rp->drag->val.y += pr->value2 * rp->drag->page.y * rp->part->dragable.y;
                       if (rp->drag->val.x < 0.0) rp->drag->val.x = 0.0;
                       else if (rp->drag->val.x > 1.0)
                         rp->drag->val.x = 1.0;
                       if (rp->drag->val.y < 0.0) rp->drag->val.y = 0.0;
                       else if (rp->drag->val.y > 1.0)
                         rp->drag->val.y = 1.0;
                       _edje_dragable_pos_set(ed, rp, rp->drag->val.x, rp->drag->val.y);
                       _edje_emit(ed, "drag,page", rp->part->name);
                       if (_edje_block_break(ed)) goto break_prog;
                    }
               }
          }
        // _edje_emit(ed, "program,stop", pr->name);
        if (_edje_block_break(ed)) goto break_prog;
        break;

      case EDJE_ACTION_TYPE_SCRIPT:
      {
         // _edje_emit(ed, "program,start", pr->name);
         if (_edje_block_break(ed)) goto break_prog;
         _edje_embryo_test_run(ed, pr, ssig, ssrc);
         // _edje_emit(ed, "program,stop", pr->name);
         if (_edje_block_break(ed)) goto break_prog;
         _edje_recalc_do(ed);
      }
      break;

      case EDJE_ACTION_TYPE_FOCUS_SET:
      {
        if (!pr->targets)
          _edje_part_focus_set(ed, pr->seat, NULL);
        else
          {
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if (rp)
                         _edje_part_focus_set(ed, pr->seat, rp);
                    }
               }
          }
      }
      break;

      case EDJE_ACTION_TYPE_FOCUS_OBJECT:
      {
         Efl_Input_Device *seat = NULL;

         if (pr->seat)
           {
              Eina_Stringshare *seat_name;

              seat_name = eina_stringshare_add(pr->seat);
              seat = _edje_seat_get(ed, seat_name);
              eina_stringshare_del(seat_name);
           }
         if (!seat)
           {
              Evas *e;

              e = evas_object_evas_get(ed->obj);
              seat = evas_default_device_get(e, EVAS_DEVICE_CLASS_SEAT);
           }
        if (!pr->targets)
          {
             Evas_Object *focused;

             focused = evas_seat_focus_get(evas_object_evas_get(ed->obj), seat);
             if (focused)
               {
                  unsigned int i;

                  /* Check if the current swallowed object is one of my child. */
                  for (i = 0; i < ed->table_parts_size; ++i)
                    {
                       rp = ed->table_parts[i];
                       if ((rp) &&
                           ((rp->type == EDJE_RP_TYPE_SWALLOW) &&
                            (rp->typedata.swallow)) &&
                           (rp->typedata.swallow->swallowed_object == focused))
                         {
                            efl_canvas_object_seat_focus_del(focused, seat);
                            break;
                         }
                    }
               }
          }
        else
          {
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if (rp &&
                           ((rp->type == EDJE_RP_TYPE_SWALLOW) &&
                            (rp->typedata.swallow)) &&
                           (rp->typedata.swallow->swallowed_object))
                         efl_canvas_object_seat_focus_add(
                            rp->typedata.swallow->swallowed_object, seat);
                    }
               }
          }
      }
      break;

      case EDJE_ACTION_TYPE_SOUND_SAMPLE:
        if (_edje_block_break(ed))
          goto break_prog;
        _edje_multisense_internal_sound_sample_play(ed, pr->sample_name, pr->speed, pr->channel);
        break;

      case EDJE_ACTION_TYPE_SOUND_TONE:
        if (_edje_block_break(ed))
          goto break_prog;
        _edje_multisense_internal_sound_tone_play(ed, pr->tone_name, pr->duration, pr->channel);
        break;

      case EDJE_ACTION_TYPE_VIBRATION_SAMPLE:
        if (_edje_block_break(ed))
          goto break_prog;
        _edje_multisense_internal_vibration_sample_play(ed, pr->vibration_name, pr->vibration_repeat);
        break;

      case EDJE_ACTION_TYPE_PARAM_COPY:
      {
         Edje_Real_Part *src_part, *dst_part;

         // _edje_emit(ed, "program,start", pr->name);
         if (_edje_block_break(ed)) goto break_prog;

         src_part = ed->table_parts[pr->param.src % ed->table_parts_size];
         dst_part = ed->table_parts[pr->param.dst % ed->table_parts_size];
         _edje_param_copy(ed, src_part, pr->state, dst_part, pr->state2);

         if (_edje_block_break(ed)) goto break_prog;
         // _edje_emit(ed, "program,stop", pr->name);
         if (_edje_block_break(ed)) goto break_prog;
      }
      break;

      case EDJE_ACTION_TYPE_PARAM_SET:
      {
         Edje_Real_Part *part;

         // _edje_emit(ed, "program,start", pr->name);
         if (_edje_block_break(ed)) goto break_prog;

         part = ed->table_parts[pr->param.dst % ed->table_parts_size];
         _edje_param_set(ed, part, pr->state, pr->state2);

         if (_edje_block_break(ed)) goto break_prog;
         // _edje_emit(ed, "program,stop", pr->name);
         if (_edje_block_break(ed)) goto break_prog;
      }
      break;

#ifdef HAVE_EPHYSICS
      case EDJE_ACTION_TYPE_PHYSICS_IMPULSE:
        if (EPH_LOAD())
          {
             if (!_edje_physics_action_set(ed, pr, EPH_CALL(ephysics_body_central_impulse_apply)))
               goto break_prog;
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_TORQUE_IMPULSE:
        if (EPH_LOAD())
          {
             if (!_edje_physics_action_set(ed, pr, EPH_CALL(ephysics_body_torque_impulse_apply)))
               goto break_prog;
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_FORCE:
        if (EPH_LOAD())
          {
             if (!_edje_physics_action_set(ed, pr, EPH_CALL(ephysics_body_central_force_apply)))
               goto break_prog;
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_TORQUE:
        if (EPH_LOAD())
          {
             if (!_edje_physics_action_set(ed, pr, EPH_CALL(ephysics_body_torque_apply)))
               goto break_prog;
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_FORCES_CLEAR:
        if (EPH_LOAD())
          {
             if (_edje_block_break(ed))
               goto break_prog;
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if ((rp) && (rp->body))
                         EPH_CALL(ephysics_body_forces_clear)(rp->body);
                    }
               }
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_VEL_SET:
        if (EPH_LOAD())
          {
             if (!_edje_physics_action_set(ed, pr, EPH_CALL(ephysics_body_linear_velocity_set)))
               goto break_prog;
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_ANG_VEL_SET:
        if (EPH_LOAD())
          {
             if (!_edje_physics_action_set(ed, pr, EPH_CALL(ephysics_body_angular_velocity_set)))
               goto break_prog;
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_STOP:
        if (EPH_LOAD())
          {
             if (_edje_block_break(ed))
               goto break_prog;
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if ((rp) && (rp->body))
                         EPH_CALL(ephysics_body_stop)(rp->body);
                    }
               }
          }
        break;

      case EDJE_ACTION_TYPE_PHYSICS_ROT_SET:
        if (EPH_LOAD())
          {
             if (_edje_block_break(ed))
               goto break_prog;
             EINA_LIST_FOREACH(pr->targets, l, pt)
               {
                  if (pt->id >= 0)
                    {
                       rp = ed->table_parts[pt->id % ed->table_parts_size];
                       if ((rp) && (rp->body))
                         {
                            EPhysics_Quaternion quat;
                            EPH_CALL(ephysics_quaternion_set)(&quat, pr->physics.x,
                                                              pr->physics.y, pr->physics.z,
                                                              pr->physics.w);
                            EPH_CALL(ephysics_quaternion_normalize)(&quat);
                            EPH_CALL(ephysics_body_rotation_set)(rp->body, &quat);
                         }
                    }
               }
          }
        break;

#endif

#ifdef BUILD_VG_LOADER_JSON
      case EDJE_ACTION_TYPE_VG_ANIM_STOP:
        if (_edje_block_break(ed))
          goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if (rp)
                    _edje_part_vector_anim_stop(ed, rp);
               }
          }
        break;

      case EDJE_ACTION_TYPE_VG_ANIM_PAUSE:
        if (_edje_block_break(ed))
          goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if (rp)
                    _edje_part_vector_anim_pause(ed, rp);
               }
          }
        break;

      case EDJE_ACTION_TYPE_VG_ANIM_RESUME:
        if (_edje_block_break(ed))
          goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if (rp)
                    _edje_part_vector_anim_resume(ed, rp);
               }
          }
        break;

      case EDJE_ACTION_TYPE_VG_ANIM_LOOP:
      case EDJE_ACTION_TYPE_VG_ANIM_REWIND:
      case EDJE_ACTION_TYPE_VG_ANIM_PLAY:
        if (_edje_block_break(ed))
          goto break_prog;
        EINA_LIST_FOREACH(pr->targets, l, pt)
          {
             if (pt->id >= 0)
               {
                  rp = ed->table_parts[pt->id % ed->table_parts_size];
                  if (rp)
                    {
                       Eina_Bool vector_anim_backward = EINA_FALSE;
                       Eina_Bool vector_anim_loop = EINA_FALSE;

                       if (pr->action == EDJE_ACTION_TYPE_VG_ANIM_REWIND)
                         vector_anim_backward = EINA_TRUE;

                       if (pr->action == EDJE_ACTION_TYPE_VG_ANIM_LOOP)
                         vector_anim_loop = EINA_TRUE;

                       _edje_part_vector_anim_play(ed, rp,
                                                   vector_anim_backward,
                                                   vector_anim_loop);
                    }
               }
          }
        break;
#else
      case EDJE_ACTION_TYPE_VG_ANIM_STOP:
      case EDJE_ACTION_TYPE_VG_ANIM_PAUSE:
      case EDJE_ACTION_TYPE_VG_ANIM_RESUME:
      case EDJE_ACTION_TYPE_VG_ANIM_LOOP:
      case EDJE_ACTION_TYPE_VG_ANIM_REWIND:
      case EDJE_ACTION_TYPE_VG_ANIM_PLAY:
        ERR("Evas Vg Json (Lottie) Loader is not supported, Only Static Vector Image(SVG) is available!");
        break;
#endif

      default:
        // _edje_emit(ed, "program,start", pr->name);
        // _edje_emit(ed, "program,stop", pr->name);
        break;
     }
   if (!((pr->action == EDJE_ACTION_TYPE_STATE_SET)
         /* hmm this fucks somethgin up. must look into it later */
         /* && (pr->tween.time > ZERO) && (!ed->no_anim))) */
         ))
     {
        EINA_LIST_FOREACH(pr->after, l, pa)
          {
             if (pa->id >= 0)
               {
                  pr2 = ed->collection->patterns.table_programs[pa->id % ed->collection->patterns.table_programs_size];
                  if (pr2) _edje_program_run(ed, pr2, 0, "", "", mdata);
                  if (_edje_block_break(ed)) goto break_prog;
               }
          }
     }
break_prog:
   _edje_util_thaw(ed);
   _edje_unref(ed);
   recursions--;
   if (recursions == 0) recursion_limit = 0;
   _edje_unblock(ed);
}

void
_edje_emit(Edje *ed, const char *sig, const char *src)
{
   _edje_emit_full(ed, sig, src, NULL, NULL);
}

void
_edje_seat_emit(Edje *ed, Efl_Input_Device *dev, const char *sig, const char *src)
{
   Edje_Message_Signal_Data *mdata = NULL;
   Efl_Input_Device *seat = NULL;
   char buf[128];
   char *sname;
   const char *s;

   if (dev) seat = efl_input_device_seat_get(dev);
   if (seat)
     {
        sname = eina_strdup(efl_name_get(seat));
        mdata = _edje_signal_data_setup(NULL, NULL, sname, free);
     }
   /* keep sending old style signals for legacy compatibility, but provide */
   /* queryable seat data for callers that can make use of it.  */
   _edje_emit_full_data(ed, sig, src, mdata);

   /* send extra signal with ",$SEAT" suffix if the input device originating
    * the signal belongs to a seat */
   if (!seat) return;

   s = _edje_seat_name_get(ed, seat);
   if (!s) s = "";
   snprintf(buf, sizeof(buf), "seat,%s,%s", s, sig);
   _edje_emit_full_data(ed, buf, src, mdata);
   _edje_signal_data_free(mdata);
}

/* data should either be NULL or a malloc allocated data */
void
_edje_emit_full(Edje *ed, const char *sig, const char *src, void *data, void (*free_func)(void *))
{
   Edje_Message_Signal_Data *mdata;

   mdata = _edje_signal_data_setup(data, free_func, NULL, NULL);
   _edje_emit_full_data(ed, sig, src, mdata);
   _edje_signal_data_free(mdata);
}

void
_edje_emit_full_data(Edje *ed, const char *sig, const char *src, Edje_Message_Signal_Data *mdata)
{
   const char *sep;
   Eina_Bool broadcast;

   if (!ed->collection) return;
   if (ed->delete_me) return;

   _edje_signal_data_ref(mdata);

   sep = strchr(sig, EDJE_PART_PATH_SEPARATOR);
   /* If we are not sending the signal to a part of the child, the
    * signal if for ourself
    */
   if (sep)
     {
        const char *newsig;
        char *part;
        unsigned int length;

        /* the signal contains a colon, split the signal into "parts:signal" */
        length = sep - sig + 1;
        part = alloca(length);
        memcpy(part, sig, length - 1);
        part[length - 1] = '\0';

        newsig = sep + 1;

        if (_edje_emit_aliased(ed, part, newsig, src)) goto out;

        broadcast = _edje_emit_child(ed, NULL, part, newsig, src);
     }
   else
     broadcast = ed->collection->broadcast_signal;

   _edje_emit_send(ed, broadcast, sig, src, mdata);
out:
   _edje_signal_data_free(mdata);
}

void
_edje_focused_part_set(Edje *ed, Eina_Stringshare *seat_name, Edje_Real_Part *rp)
{
   Edje_Seat *seat;
   Eina_List *l;

   //defaulting seat NULL to seat1
   if (!seat_name) seat_name = eina_stringshare_add("seat1");

   EINA_LIST_FOREACH(ed->seats, l, seat)
     {
        if (seat_name == seat->name)
          {
             seat->focused_part = rp;
             return;
          }
     }

   /* A part to be set for a seat not yet announced by Evas */
   seat = calloc(1, sizeof(Edje_Seat));
   EINA_SAFETY_ON_NULL_RETURN(seat);

   seat->name = eina_stringshare_ref(seat_name);
   seat->focused_part = rp;
   ed->seats = eina_list_append(ed->seats, seat);

   return;
}

Edje_Real_Part *
_edje_focused_part_get(Edje *ed, Eina_Stringshare *seat_name)
{
   Edje_Seat *seat;
   Eina_List *l;

   //defaulting seat NULL to seat1
   if (!seat_name) seat_name = eina_stringshare_add("seat1");

   EINA_LIST_FOREACH(ed->seats, l, seat)
     {
        if (seat_name == seat->name)
          return seat->focused_part;
     }

   return NULL;
}

Eina_Stringshare*
_edje_seat_name_get(Edje *ed, Efl_Input_Device *device)
{
   Edje_Seat *seat;
   Eina_List *l;

   EINA_LIST_FOREACH(ed->seats, l, seat)
     {
        if (seat->device == device)
          return seat->name;
     }

   return NULL;
}

Efl_Input_Device *
_edje_seat_get(Edje *ed, Eina_Stringshare *name)
{
   Edje_Seat *seat;
   Eina_List *l;

   EINA_LIST_FOREACH(ed->seats, l, seat)
     {
        if (seat->name == name)
          return seat->device;
     }

   return NULL;
}

struct _Edje_Program_Data
{
   Eina_List  *matches;
   Edje       *ed;
   const char *source;
};

static Eina_Bool
_edje_glob_callback(Edje_Program *pr, void *dt)
{
   struct _Edje_Program_Data *data = dt;
   Edje_Real_Part *rp = NULL;
   Eina_Bool exec = EINA_TRUE;

   if (pr->filter.state)
     {
        rp = _edje_real_part_get(data->ed, pr->filter.part ? pr->filter.part : data->source);
        if (rp)
          exec = !strcmp(rp->chosen_description->state.name, pr->filter.state);
     }

   pr->exec = exec;

   data->matches = eina_list_append(data->matches, pr);

   return EINA_FALSE;
}

/* FIXME: what if we delete the evas object??? */
void
_edje_emit_handle(Edje *ed, const char *sig, const char *src,
                  Edje_Message_Signal_Data *sdata, Eina_Bool prop)
{
   if (ed->delete_me) return;
   if (!sig) sig = "";
   if (!src) src = "";
   DBG("EDJE EMIT: (%p) signal: \"%s\" source: \"%s\"", ed, sig, src);
   _edje_block(ed);
   _edje_ref(ed);
   _edje_util_freeze(ed);

   if (ed->collection && ed->L)
     _edje_lua2_script_func_signal(ed, sig, src);

   if (ed->collection)
     {
#ifdef EDJE_PROGRAM_CACHE
        Edje_Part_Collection *ec;
        char *tmps;
        int l1, l2;
#endif
        int done;

#ifdef EDJE_PROGRAM_CACHE
        ec = ed->collection;
        l1 = strlen(sig);
        l2 = strlen(src);
        tmps = alloca(l1 + l2 + 3); /* \0, \337, \0 */
        strcpy(tmps, sig);
        tmps[l1] = '\377';
        strcpy(&(tmps[l1 + 1]), src);
#endif
        done = 0;

#ifdef EDJE_PROGRAM_CACHE
        {
           Eina_List *matches;
           Eina_List *l;
           Edje_Program *pr;

           if (eina_hash_find(ec->prog_cache.no_matches, tmps))
             {
                done = 1;
             }
           else if ((matches = eina_hash_find(ec->prog_cache.matches, tmps)))
             {
                EINA_LIST_FOREACH(matches, l, pr)
                  {
                     Eina_Bool exec = EINA_TRUE;

                     if (pr->filter.state)
                       {
                          Edje_Real_Part *rp;

                          rp = _edje_real_part_get(ed, pr->filter.part ? pr->filter.part : src);
                          if (rp)
                            {
                               if (rp->program)
                                 exec = EINA_FALSE;
                               else
                                 exec = (rp->chosen_description->state.name != pr->filter.state) ?
                                   !strcmp(rp->chosen_description->state.name, pr->filter.state) : EINA_TRUE;
                            }
                       }

                     pr->exec = exec;
                  }

                EINA_LIST_FOREACH(matches, l, pr)
                  if (pr->exec)
                    {
                       _edje_program_run(ed, pr, 0, sig, src, sdata);
                       if (_edje_block_break(ed))
                         {
                            goto break_prog;
                         }
                    }

                done = 1;
             }
        }
#endif
        if (!done)
          {
             struct _Edje_Program_Data data;

             data.ed = ed;
             data.source = src;
             data.matches = NULL;

             if (ed->collection->patterns.table_programs_size > 0)
               {
                  const Eina_Inarray *match;
#ifdef EDJE_PROGRAM_CACHE
                  const Eina_List *l;
#endif
                  Edje_Program *pr;

                  if (ed->collection->patterns.programs.u.programs.globing)
                    if (edje_match_programs_exec(ed->collection->patterns.programs.signals_patterns,
                                                 ed->collection->patterns.programs.sources_patterns,
                                                 sig,
                                                 src,
                                                 ed->collection->patterns.programs.u.programs.globing,
                                                 _edje_glob_callback,
                                                 &data,
                                                 prop) == 0)
                      goto break_prog;

                  match = edje_match_signal_source_hash_get(sig, src,
                                                            ed->collection->patterns.programs.exact_match);
                  if (match)
                    {
                       Edje_Program **tpr;

                       EINA_INARRAY_FOREACH(match, tpr)
                       _edje_glob_callback(*tpr, &data);
                    }

#ifdef EDJE_PROGRAM_CACHE
                  EINA_LIST_FOREACH(data.matches, l, pr)
#else
                  EINA_LIST_FREE(data.matches, pr)
#endif
                    {
                       if (pr->exec)
                         _edje_program_run(ed, pr, 0, sig, src, sdata);

                       if (_edje_block_break(ed))
                         {
                            eina_list_free(data.matches);
                            data.matches = NULL;
                            goto break_prog;
                         }
                    }
               }

#ifdef EDJE_PROGRAM_CACHE
             if (tmps)
               {
                  if (data.matches == NULL)
                    {
                       if (!ec->prog_cache.no_matches)
                         ec->prog_cache.no_matches = eina_hash_string_superfast_new(NULL);
                       eina_hash_add(ec->prog_cache.no_matches, tmps, ed);
                    }
                  else
                    {
                       if (!ec->prog_cache.matches)
                         ec->prog_cache.matches = eina_hash_string_superfast_new(NULL);
                       eina_hash_add(ec->prog_cache.matches, tmps, data.matches);
                    }
               }
#endif
          }
        _edje_emit_cb(ed, sig, src, sdata, prop);
        if (_edje_block_break(ed))
          {
             goto break_prog;
          }
     }
break_prog:
   _edje_util_thaw(ed);
   _edje_unref(ed);
   _edje_unblock(ed);
}

/* Extra data for callbacks */
static void *callback_extra_data = NULL;
static void *callback_seat_data = NULL;

EAPI void *
edje_object_signal_callback_extra_data_get(void)
{
   return callback_extra_data;
}

#ifdef EFL_BETA_API_SUPPORT
EAPI void *
edje_object_signal_callback_seat_data_get(void)
{
   return callback_seat_data;
}
#endif

/* FIXME: what if we delete the evas object??? */
static void
_edje_emit_cb(Edje *ed, const char *sig, const char *src, Edje_Message_Signal_Data *data, Eina_Bool prop)
{
   const Edje_Signals_Sources_Patterns *ssp;
   Edje_Signal_Callback_Matches *m;
   const Eina_Inarray *match;
   int r = 1;

   if (ed->delete_me) return;
   if (!ed->callbacks || !ed->callbacks->matches) return;

   _edje_ref(ed);
   _edje_util_freeze(ed);
   _edje_block(ed);

   ed->walking_callbacks++;

   ssp = _edje_signal_callback_patterns_ref
     ((Edje_Signal_Callback_Group *)ed->callbacks);
   if (ssp)
     {
        m = (Edje_Signal_Callback_Matches *)ed->callbacks->matches;
        EINA_REFCOUNT_REF(m);

        callback_extra_data = (data) ? data->data : NULL;
        callback_seat_data = (data) ? data->seat_data : NULL;

        if (eina_inarray_count(&ssp->u.callbacks.globing))
          r = edje_match_callback_exec(ssp,
                                       m->matches,
                                       sig,
                                       src,
                                       ed,
                                       prop);

        if (!r) goto break_prog;

        match = edje_match_signal_source_hash_get(sig, src,
                                                  ssp->exact_match);
        if (match)
          {
             const Edje_Signal_Callback_Match *cb;
             unsigned int *i;

             EINA_INARRAY_FOREACH(match, i)
               {
                  if (ed->callbacks->flags[*i].delete_me) continue;
                  if ((prop) && (ed->callbacks->flags[*i].propagate)) continue;

                  cb = &m->matches[*i];

                  if (ed->callbacks->flags[*i].legacy)
                    cb->legacy((void *)ed->callbacks->custom_data[*i], ed->obj, sig, src);
                  else
                    cb->eo((void *)ed->callbacks->custom_data[*i], ed->obj, sig, src);
                  if (_edje_block_break(ed)) break;
               }
          }

break_prog:
        _edje_signal_callback_matches_unref(m, ed->callbacks->flags, ed->callbacks->custom_data);

        _edje_signal_callback_patterns_unref(ssp);
     }

   ed->walking_callbacks--;

   if (ed->walking_callbacks == 0)
     _edje_signal_callback_reset(ed->callbacks->flags,
                                 ed->callbacks->matches->matches_count);

   _edje_unblock(ed);
   _edje_util_thaw(ed);
   _edje_unref(ed);
}

static const Edje_External_Param_Info *
_edje_external_param_info_get(const Evas_Object *obj, const char *name)
{
   const Edje_External_Type *type;
   const Edje_External_Param_Info *info;

   type = evas_object_data_get(obj, "Edje_External_Type");
   if (!type) return NULL;
   for (info = type->parameters_info; info->name; info++)
     if (!strcmp(info->name, name)) return info;

   return NULL;
}

static Edje_External_Param *
_edje_param_external_get(Edje_Real_Part *rp, const char *name, Edje_External_Param *param)
{
   Evas_Object *swallowed_object;
   const Edje_External_Param_Info *info;

   if ((rp->type != EDJE_RP_TYPE_SWALLOW) ||
       (!rp->typedata.swallow)) return NULL;
   swallowed_object = rp->typedata.swallow->swallowed_object;

   info = _edje_external_param_info_get(swallowed_object, name);
   if (!info) return NULL;

   memset(param, 0, sizeof(*param));
   param->name = info->name;
   param->type = info->type;
   if (!_edje_external_param_get(NULL, rp, param)) return NULL;
   return param;
}

/* simulate external properties for native objects */
static Edje_External_Param *
_edje_param_native_get(Edje *ed, Edje_Real_Part *rp, const char *name, Edje_External_Param *param, void **free_ptr)
{
   *free_ptr = NULL;
   if ((rp->part->type == EDJE_PART_TYPE_TEXT) ||
       (rp->part->type == EDJE_PART_TYPE_TEXTBLOCK))
     {
        if (!strcmp(name, "text"))
          {
             param->name = name;
             param->type = EDJE_EXTERNAL_PARAM_TYPE_STRING;

             _edje_recalc_do(ed);
             if (rp->part->entry_mode > EDJE_ENTRY_EDIT_MODE_NONE)
               param->s = _edje_entry_text_get(rp);
             else if ((rp->part->type == EDJE_PART_TYPE_TEXT) &&
                      ((rp->type == EDJE_RP_TYPE_TEXT) &&
                       (rp->typedata.text)))
               param->s = rp->typedata.text->text;
             else
               param->s = evas_object_textblock_text_markup_get(rp->object);
             return param;
          }
        if (rp->part->type == EDJE_PART_TYPE_TEXTBLOCK)
          {
             if (!strcmp(name, "text_unescaped"))
               {
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_STRING;

                  _edje_recalc_do(ed);
                  if (rp->part->entry_mode > EDJE_ENTRY_EDIT_MODE_NONE)
                    {
                       const char *tmp = _edje_entry_text_get(rp);
                       char *unescaped = _edje_text_unescape(tmp);
                       *free_ptr = unescaped;
                       param->s = unescaped;
                    }
                  else if ((rp->part->type == EDJE_PART_TYPE_TEXT) &&
                           ((rp->type == EDJE_RP_TYPE_TEXT) &&
                            (rp->typedata.text)))
                    param->s = rp->typedata.text->text;
                  else
                    {
                       const char *tmp;
                       char *unescaped;

                       tmp = evas_object_textblock_text_markup_get(rp->object);
                       unescaped = _edje_text_unescape(tmp);
                       *free_ptr = unescaped;
                       param->s = unescaped;
                    }

                  return param;
               }

             if (((rp->type == EDJE_RP_TYPE_TEXT) &&
                  (rp->typedata.text)) &&
                 ((rp->typedata.text->entry_data) &&
                  (rp->part->entry_mode > EDJE_ENTRY_EDIT_MODE_NONE) &&
                  (!strcmp(name, "select_allow"))))
               {
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_BOOL;
                  param->i = _edje_entry_select_allow_get(rp);
                  return param;
               }
          }
     }

   if ((rp->drag) && (rp->drag->down.count == 0))
     {
        if (!strncmp(name, "drag_", sizeof("drag_") - 1))
          {
             const char *sub_name = name + sizeof("drag_") - 1;
             if (!strcmp(sub_name, "value_x"))
               {
                  double d;

                  _edje_recalc_do(ed);
                  d = TO_DOUBLE(rp->drag->val.x);
                  if (rp->part->dragable.x < 0) d = 1.0 - d;
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = d;
                  return param;
               }
             if (!strcmp(sub_name, "value_y"))
               {
                  double d;

                  _edje_recalc_do(ed);
                  d = TO_DOUBLE(rp->drag->val.y);
                  if (rp->part->dragable.y < 0) d = 1.0 - d;
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = d;
                  return param;
               }

             if (!strcmp(sub_name, "size_w"))
               {
                  _edje_recalc_do(ed);
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = TO_DOUBLE(rp->drag->size.x);
                  return param;
               }
             if (!strcmp(sub_name, "size_h"))
               {
                  _edje_recalc_do(ed);
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = TO_DOUBLE(rp->drag->size.y);
                  return param;
               }

             if (!strcmp(sub_name, "step_x"))
               {
                  _edje_recalc_do(ed);
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = TO_DOUBLE(rp->drag->step.x);
                  return param;
               }
             if (!strcmp(sub_name, "step_y"))
               {
                  _edje_recalc_do(ed);
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = TO_DOUBLE(rp->drag->step.y);
                  return param;
               }

             if (!strcmp(sub_name, "page_x"))
               {
                  _edje_recalc_do(ed);
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = TO_DOUBLE(rp->drag->page.x);
                  return param;
               }
             if (!strcmp(sub_name, "page_y"))
               {
                  _edje_recalc_do(ed);
                  param->name = name;
                  param->type = EDJE_EXTERNAL_PARAM_TYPE_DOUBLE;
                  param->d = TO_DOUBLE(rp->drag->page.y);
                  return param;
               }

             return NULL;
          }
     }

   return NULL;
}

static Eina_Bool
_edje_param_native_set(Edje *ed, Edje_Real_Part *rp, const char *name, const Edje_External_Param *param)
{
   if ((rp->part->type == EDJE_PART_TYPE_TEXT) ||
       (rp->part->type == EDJE_PART_TYPE_TEXTBLOCK))
     {
        if (!strcmp(name, "text"))
          {
             if (param->type != EDJE_EXTERNAL_PARAM_TYPE_STRING)
               return EINA_FALSE;

             _edje_object_part_text_raw_set(ed, ed->obj, rp, rp->part->name, param->s);
             return EINA_TRUE;
          }
        if (rp->part->type == EDJE_PART_TYPE_TEXTBLOCK)
          {
             if (!strcmp(name, "text_unescaped"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_STRING)
                    return EINA_FALSE;

                  if (rp->part->type == EDJE_PART_TYPE_TEXT)
                    _edje_object_part_text_raw_set(ed, ed->obj, rp, rp->part->name, param->s);
                  else
                    {
                       char *escaped = _edje_text_escape(param->s);
                       _edje_object_part_text_raw_set(ed, ed->obj, rp, rp->part->name, escaped);
                       free(escaped);
                    }

                  return EINA_TRUE;
               }

             if (((rp->type == EDJE_RP_TYPE_TEXT) &&
                  (rp->typedata.text)) &&
                 ((rp->typedata.text->entry_data) &&
                  (rp->part->entry_mode > EDJE_ENTRY_EDIT_MODE_NONE) &&
                  (!strcmp(name, "select_allow"))))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_BOOL)
                    return EINA_FALSE;
                  _edje_entry_select_allow_set(rp, param->i);
                  return EINA_TRUE;
               }
          }
     }

   if ((rp->drag) && (rp->drag->down.count == 0))
     {
        if (!strncmp(name, "drag_", sizeof("drag_") - 1))
          {
             const char *sub_name = name + sizeof("drag_") - 1;
             if (!strcmp(sub_name, "value_x"))
               {
                  double d;
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  d = param->d;
                  if (rp->part->dragable.confine_id != -1)
                    d = CLAMP(d, 0.0, 1.0);
                  if (rp->part->dragable.x < 0) d = 1.0 - d;
                  if (EQ(rp->drag->val.x, FROM_DOUBLE(d))) return EINA_TRUE;
                  rp->drag->val.x = FROM_DOUBLE(d);
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  _edje_dragable_pos_set
                    (ed, rp, rp->drag->val.x, rp->drag->val.y);
                  _edje_emit(ed, "drag,set", rp->part->name);
                  return EINA_TRUE;
               }
             if (!strcmp(sub_name, "value_y"))
               {
                  double d;
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  d = param->d;
                  if (rp->part->dragable.confine_id != -1)
                    d = CLAMP(d, 0.0, 1.0);
                  if (rp->part->dragable.y < 0) d = 1.0 - d;
                  if (EQ(rp->drag->val.y, FROM_DOUBLE(d))) return EINA_TRUE;
                  rp->drag->val.y = FROM_DOUBLE(d);
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  _edje_dragable_pos_set
                    (ed, rp, rp->drag->val.x, rp->drag->val.y);
                  _edje_emit(ed, "drag,set", rp->part->name);
                  return EINA_TRUE;
               }

             if (!strcmp(sub_name, "size_w"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  rp->drag->size.x = FROM_DOUBLE(CLAMP(param->d, 0.0, 1.0));
                  ed->recalc_call = EINA_TRUE;
                  ed->dirty = EINA_TRUE;
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  _edje_recalc(ed);
                  return EINA_TRUE;
               }
             if (!strcmp(sub_name, "size_h"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  rp->drag->size.y = FROM_DOUBLE(CLAMP(param->d, 0.0, 1.0));
                  ed->recalc_call = EINA_TRUE;
                  ed->dirty = EINA_TRUE;
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  _edje_recalc(ed);
                  return EINA_TRUE;
               }

             if (!strcmp(sub_name, "step_x"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  rp->drag->step.x = FROM_DOUBLE(CLAMP(param->d, 0.0, 1.0));
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  return EINA_TRUE;
               }
             if (!strcmp(sub_name, "step_y"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  rp->drag->step.y = FROM_DOUBLE(CLAMP(param->d, 0.0, 1.0));
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  return EINA_TRUE;
               }

             if (!strcmp(sub_name, "page_x"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  rp->drag->page.x = FROM_DOUBLE(CLAMP(param->d, 0.0, 1.0));
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  return EINA_TRUE;
               }
             if (!strcmp(sub_name, "page_y"))
               {
                  if (param->type != EDJE_EXTERNAL_PARAM_TYPE_DOUBLE)
                    return EINA_FALSE;
                  rp->drag->page.y = FROM_DOUBLE(CLAMP(param->d, 0.0, 1.0));
#ifdef EDJE_CALC_CACHE
                  rp->invalidate = EINA_TRUE;
#endif
                  return EINA_TRUE;
               }

             return EINA_FALSE;
          }
     }

   return EINA_FALSE;
}

static const Edje_External_Param_Info *
_edje_native_param_info_get(const Edje_Real_Part *rp, const char *name)
{
   if ((rp->part->type == EDJE_PART_TYPE_TEXT) ||
       (rp->part->type == EDJE_PART_TYPE_TEXTBLOCK))
     {
        if (!strcmp(name, "text"))
          {
             static const Edje_External_Param_Info pi =
               EDJE_EXTERNAL_PARAM_INFO_STRING("text");
             return &pi;
          }
        if (rp->part->type == EDJE_PART_TYPE_TEXTBLOCK)
          {
             if (!strcmp(name, "text_unescaped"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_STRING("text_unescaped");
                  return &pi;
               }
             if (!strcmp(name, "select_allow"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_BOOL("text_unescaped");
                  return &pi;
               }
          }
     }

   if ((rp->drag) && (rp->drag->down.count == 0))
     {
        if (!strncmp(name, "drag_", sizeof("drag_") - 1))
          {
             name += sizeof("drag_") - 1;
             if (!strcmp(name, "value_x"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_value_x");
                  return &pi;
               }
             if (!strcmp(name, "value_y"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_value_y");
                  return &pi;
               }
             if (!strcmp(name, "size_w"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_size_w");
                  return &pi;
               }
             if (!strcmp(name, "size_h"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_size_h");
                  return &pi;
               }
             if (!strcmp(name, "step_x"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_step_x");
                  return &pi;
               }
             if (!strcmp(name, "step_y"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_step_y");
                  return &pi;
               }
             if (!strcmp(name, "page_x"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_page_x");
                  return &pi;
               }
             if (!strcmp(name, "page_y"))
               {
                  static const Edje_External_Param_Info pi =
                    EDJE_EXTERNAL_PARAM_INFO_DOUBLE("drag_page_y");
                  return &pi;
               }

             return NULL;
          }
     }

   return NULL;
}

static Edje_External_Param *
_edje_param_convert(Edje_External_Param *param, const Edje_External_Param_Info *dst_info)
{
   if (param->type == dst_info->type) return param;

   switch (dst_info->type)
     {
      case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
      case EDJE_EXTERNAL_PARAM_TYPE_INT:
      {
         int i;
         switch (param->type)
           {
            case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
              i = (int)param->d;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_STRING:
            case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
              i = (param->s) ? atoi(param->s) : 0;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
            case EDJE_EXTERNAL_PARAM_TYPE_INT:
              i = param->i;
              break;

            default:
              return NULL;
           }
         if (dst_info->type == EDJE_EXTERNAL_PARAM_TYPE_BOOL)
           i = !!i;
         param->type = dst_info->type;
         param->i = i;
         return param;
      }

      case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
      {
         double d;
         switch (param->type)
           {
            case EDJE_EXTERNAL_PARAM_TYPE_INT:
              d = (double)param->i;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_STRING:
            case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
              d = (param->s) ? atof(param->s) : 0.0;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
              d = (double)param->i;
              break;

            default:
              return NULL;
           }
         param->type = dst_info->type;
         param->d = d;
         return param;
      }

      case EDJE_EXTERNAL_PARAM_TYPE_STRING:
      {
         static char s[64];
         switch (param->type)
           {
            case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
            case EDJE_EXTERNAL_PARAM_TYPE_INT:
              if (!snprintf(s, sizeof(s), "%i", param->i)) return NULL;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
              if (!snprintf(s, sizeof(s), "%f", param->d)) return NULL;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
              param->type = dst_info->type;
              return param;

            default:
              return NULL;
           }
         param->type = dst_info->type;
         param->s = s;
         return param;
      }

      case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
      {
         static char s[64];
         const char *val;
         switch (param->type)
           {
            case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
            case EDJE_EXTERNAL_PARAM_TYPE_INT:
              if (!snprintf(s, sizeof(s), "%i", param->i)) return NULL;
              val = s;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
              if (!snprintf(s, sizeof(s), "%f", param->d)) return NULL;
              val = s;
              break;

            case EDJE_EXTERNAL_PARAM_TYPE_STRING:
              val = param->s;
              break;

            default:
              return NULL;
           }

         param->type = dst_info->type;
         if (param->s != val) param->s = val;
         return param;
      }

      default: return NULL;
     }
}

static Eina_Bool
_edje_param_validate(const Edje_External_Param *param, const Edje_External_Param_Info *info)
{
   switch (info->type)
     {
      case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
        return (param->i == 0) || (param->i == 1);

      case EDJE_EXTERNAL_PARAM_TYPE_INT:
        if ((info->info.i.min != EDJE_EXTERNAL_INT_UNSET) &&
            (info->info.i.min > param->i))
          return EINA_FALSE;

        if ((info->info.i.max != EDJE_EXTERNAL_INT_UNSET) &&
            (info->info.i.max < param->i))
          return EINA_FALSE;

        return EINA_TRUE;

      case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
        if (!EINA_DBL_EQ(info->info.d.min, EDJE_EXTERNAL_DOUBLE_UNSET) &&
            (info->info.d.min > param->d))
          return EINA_FALSE;

        if (!EINA_DBL_EQ(info->info.d.max, EDJE_EXTERNAL_DOUBLE_UNSET) &&
            (info->info.d.max < param->d))
          return EINA_FALSE;

        return EINA_TRUE;

      case EDJE_EXTERNAL_PARAM_TYPE_STRING:
        if (!param->s) return EINA_FALSE;
        if (info->info.s.accept_fmt)
          INF("string 'accept_fmt' validation not implemented.");
        if (info->info.s.deny_fmt)
          INF("string 'deny_fmt' validation not implemented.");
        return EINA_TRUE;

      case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
      {
         const char **itr = info->info.c.choices;
         if (!itr) return EINA_FALSE;
         for (; *itr; itr++)
           if (!strcmp(*itr, param->s))
             return EINA_TRUE;
         return EINA_FALSE;
      }

      default: return EINA_FALSE;
     }
}

static void
_edje_param_copy(Edje *ed, Edje_Real_Part *src_part, const char *src_param, Edje_Real_Part *dst_part, const char *dst_param)
{
   Edje_External_Param val;
   const Edje_External_Param_Info *dst_info;
   void *free_ptr = NULL;

   if ((!src_part) || (!src_param) || (!dst_part) || (!dst_param))
     return;

   if ((dst_part->part->type == EDJE_PART_TYPE_EXTERNAL) &&
       (dst_part->type == EDJE_RP_TYPE_SWALLOW) &&
       (dst_part->typedata.swallow))
     dst_info = _edje_external_param_info_get
         (dst_part->typedata.swallow->swallowed_object, dst_param);
   else
     dst_info = _edje_native_param_info_get(dst_part, dst_param);

   if (!dst_info)
     {
        ERR("cannot copy, invalid destination parameter '%s' of part '%s'",
            dst_param, dst_part->part->name);
        return;
     }

   if (src_part->part->type == EDJE_PART_TYPE_EXTERNAL)
     {
        if (!_edje_param_external_get
              (src_part, src_param, &val))
          {
             ERR("cannot get parameter '%s' of part '%s'",
                 src_param, src_part->part->name);
             return;
          }
     }
   else
     {
        if (!_edje_param_native_get(ed, src_part, src_param, &val, &free_ptr))
          {
             ERR("cannot get parameter '%s' of part '%s'",
                 src_param, src_part->part->name);
             return;
          }
     }

   if (!_edje_param_convert(&val, dst_info))
     {
        ERR("cannot convert parameter type %s to requested type %s",
            edje_external_param_type_str(val.type),
            edje_external_param_type_str(dst_info->type));
        goto end;
     }

   if (!_edje_param_validate(&val, dst_info))
     {
        ERR("incorrect parameter value failed validation for type %s",
            edje_external_param_type_str(dst_info->type));
        goto end;
     }

   if (dst_part->part->type == EDJE_PART_TYPE_EXTERNAL)
     {
        val.name = dst_param;
        if (!_edje_external_param_set(NULL, dst_part, &val))
          {
             ERR("failed to set parameter '%s' (%s) of part '%s'",
                 dst_param, edje_external_param_type_str(dst_info->type),
                 dst_part->part->name);
             goto end;
          }
     }
   else
     {
        if (!_edje_param_native_set(ed, dst_part, dst_param, &val))
          {
             ERR("failed to set parameter '%s' (%s) of part '%s'",
                 dst_param, edje_external_param_type_str(dst_info->type),
                 dst_part->part->name);
             goto end;
          }
     }

end:
   free(free_ptr);
}

static void
_edje_param_set(Edje *ed, Edje_Real_Part *part, const char *param, const char *value)
{
   Edje_External_Param val;
   const Edje_External_Param_Info *info;

   if ((!part) || (!param) || (!value))
     return;

   if ((part->part->type == EDJE_PART_TYPE_EXTERNAL) &&
       (part->type == EDJE_RP_TYPE_SWALLOW) &&
       (part->typedata.swallow))
     info = _edje_external_param_info_get(part->typedata.swallow->swallowed_object, param);
   else
     info = _edje_native_param_info_get(part, param);

   if (!info)
     {
        ERR("cannot copy, invalid destination parameter '%s' of part '%s'",
            param, part->part->name);
        return;
     }

   val.name = "(temp)";
   val.type = EDJE_EXTERNAL_PARAM_TYPE_STRING;
   val.s = value;

   if (!_edje_param_convert(&val, info))
     {
        ERR("cannot convert parameter type STRING to requested type %s",
            edje_external_param_type_str(info->type));
        return;
     }

   if (!_edje_param_validate(&val, info))
     {
        ERR("incorrect parameter value failed validation for type %s",
            edje_external_param_type_str(info->type));
        return;
     }

   if (part->part->type == EDJE_PART_TYPE_EXTERNAL)
     {
        val.name = param;
        if (!_edje_external_param_set(NULL, part, &val))
          {
             ERR("failed to set parameter '%s' (%s) of part '%s'",
                 param, edje_external_param_type_str(info->type),
                 part->part->name);
             return;
          }
     }
   else
     {
        if (!_edje_param_native_set(ed, part, param, &val))
          {
             ERR("failed to set parameter '%s' (%s) of part '%s'",
                 param, edje_external_param_type_str(info->type),
                 part->part->name);
             return;
          }
     }
}

