#include <Elementary.h>
#include "video.h"
#include "rage_config.h"
#include "config.h"
#include "albumart.h"
#include "mpris.h"

typedef struct _Video Video;

struct _Video
{
   Evas_Object_Smart_Clipped_Data __clipped_data;
   Evas_Object *clip, *o_vid, *o_img, *o_event;
   Ecore_Timer *smooth_timer;
   Ecore_Job *restart_job;
   const char *file;
   const char *realfile;
   const char *artfile;
   Ecore_Exe  *exe;
   Ecore_Event_Handler *exe_handler;
   double volume;
   int w, h;
   int iw, ih, piw, pih, tw, th;
   int resizes;
   struct {
      Evas_Coord x, y;
      Eina_Bool down : 1;
   } down;
   Eina_Bool nosmooth : 1;
   Eina_Bool lowqual : 1;
   Eina_Bool loop : 1;
   Eina_Bool fill : 1;
   Eina_Bool novid : 1;
   Eina_Bool doart : 1;
};

static Evas_Smart *_smart = NULL;
static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;

static void _ob_resize(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h);

static Eina_Bool
_cb_thumb_exe(void *data, int type EINA_UNUSED, void *event)
{
   Ecore_Exe_Event_Del *ev = event;
   Video *sd = evas_object_smart_data_get(data);
   char *path;

   if (!sd) return EINA_TRUE;
   if (ev->exe == sd->exe)
     {
        sd->exe = NULL;
        if (ev->exit_code == 0)
          {
             Evas_Coord ox, oy, ow, oh;

             path = albumart_file_get(sd->realfile);
             if (path)
               {
                  if (ecore_file_exists(path))
                    {
                       eina_stringshare_replace(&(sd->artfile), path);
                       evas_object_image_file_set(sd->o_img, path, NULL);
                       evas_object_image_size_get(sd->o_img, &(sd->tw), &(sd->th));
                       evas_object_geometry_get(data, &ox, &oy, &ow, &oh);
                       evas_object_show(sd->o_img);
                       _ob_resize(data, ox, oy, ow, oh);
                       mpris_metadata_change();
                    }
                  free(path);
               }
          }
     }
   return EINA_TRUE;
}

static void
_art_check(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->novid)
     {
        if (!emotion_object_video_handled_get(sd->o_vid))
          sd->novid = EINA_TRUE;
     }
   else
     {
        if (!emotion_object_video_handled_get(sd->o_vid))
          sd->novid = EINA_FALSE;
     }
   if (sd->doart)
     {
        char *thumb = NULL;

        thumb = albumart_file_get(sd->realfile);
        if (thumb)
          {
             Evas_Coord ox, oy, ow, oh;

             if (ecore_file_exists(thumb))
               {
                  evas_object_image_file_set(sd->o_img, thumb, NULL);
                  evas_object_image_size_get(sd->o_img, &(sd->tw), &(sd->th));
                  evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
                  _ob_resize(obj, ox, oy, ow, oh);
                  if (sd->tw > 0)
                    {
                       eina_stringshare_replace(&(sd->artfile), thumb);
                       mpris_metadata_change();
                    }
               }
             free(thumb);
          }
     }
   else
     {
        evas_object_hide(sd->o_img);
     }

   if (!sd->artfile)
     {
        char buf[PATH_MAX];
        const char *libdir;
        char *s;

        ecore_exe_run_priority_set(10);
        s = ecore_file_escape_name(sd->realfile);
        if (s)
          {
             libdir = elm_app_lib_dir_get();
             if (libdir)
               {
                  if (!sd->exe_handler)
                    sd->exe_handler = ecore_event_handler_add
                      (ECORE_EXE_EVENT_DEL, _cb_thumb_exe, obj);
                  snprintf(buf, sizeof(buf),
                           "%s/rage/utils/rage_thumb %s 10000 %i",
                           libdir, s, 2);
                  if (sd->exe)
                    {
                       ecore_exe_kill(sd->exe);
                       ecore_exe_free(sd->exe);
                       sd->exe = NULL;
                    }
                  sd->exe = ecore_exe_pipe_run(buf,
                                               ECORE_EXE_TERM_WITH_PARENT |
                                               ECORE_EXE_NOT_LEADER,
                                               obj);
               }
             free(s);
          }
     }
}

static void
_cb_vid_frame(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   Evas_Coord ox, oy, ow, oh;

   if (!sd) return;
   evas_object_geometry_get(data, &ox, &oy, &ow, &oh);
   if (!emotion_object_video_handled_get(sd->o_vid))
     {
        evas_object_hide(sd->o_vid);
        evas_object_show(sd->o_img);
     }
   else
     {
        evas_object_show(sd->o_vid);
        evas_object_hide(sd->o_img);
     }
   evas_object_show(sd->clip);
   _ob_resize(data, ox, oy, ow, oh);
   evas_object_smart_callback_call(data, "frame_decode", NULL);
}

static void
_cb_vid_resize(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   Evas_Coord ox, oy, ow, oh;

   if (!sd) return;
   evas_object_geometry_get(data, &ox, &oy, &ow, &oh);
   _ob_resize(data, ox, oy, ow, oh);
}

static void
_cb_restart(void *data)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   sd->restart_job = NULL;
   emotion_object_position_set(sd->o_vid, 0.0);
   emotion_object_play_set(sd->o_vid, EINA_TRUE);
}

static void
_cb_vid_stop(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   if (sd->restart_job) ecore_job_del(sd->restart_job);
   if (sd->loop)
     {
        sd->restart_job = ecore_job_add(_cb_restart, data);
        evas_object_smart_callback_call(data, "loop", NULL);
        mpris_metadata_change();
     }
   else
     {
        sd->restart_job = NULL;
        evas_object_smart_callback_call(data, "stop", NULL);
        mpris_metadata_change();
     }
}

static void
_cb_vid_progress(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "frame_decode", NULL);
}

static void
_cb_vid_ref(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "frame_decode", NULL);
}

static void
_cb_open_done(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "opened", NULL);
   _art_check(data);
   mpris_metadata_change();
}

static void
_cb_position_update(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "frame_decode", NULL);
}

static void
_cb_length_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "length", NULL);
   mpris_metadata_change();
}

static void
_cb_title_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "title", NULL);
   mpris_metadata_change();
}

static void
_cb_audio_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "audio", NULL);
   mpris_metadata_change();
}

static void
_cb_channels_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "channels", NULL);
   mpris_metadata_change();
}

static void
_cb_play_start(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "play_start", NULL);
   evas_object_smart_callback_call(data, "audio", NULL);
   mpris_metadata_change();
}

static void
_cb_play_finish(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "play_finish", NULL);
   evas_object_smart_callback_call(data, "audio", NULL);
   mpris_metadata_change();
}

static void
_cb_button_num_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "button_num", NULL);
}

static void
_cb_button_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;
   evas_object_smart_callback_call(data, "button", NULL);
}

static void
_ob_resize(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;

   emotion_object_size_get(sd->o_vid, &(sd->iw), &(sd->ih));
   if (!sd->fill)
     {
        if ((w <= 0) || (h <= 0) || (sd->iw <= 0) || (sd->ih <= 0))
          {
             w = 1;
             h = 1;
          }
        else
          {
             int iw = 1, ih = 1;
             double ratio;

             ratio = emotion_object_ratio_get(sd->o_vid);
             if (ratio > 0.0) sd->iw = (sd->ih * ratio);
             else ratio = (double)sd->iw / (double)sd->ih;

             iw = w;
             ih = ((double)w + 1.0) / ratio;
             if (ih > h)
               {
                  ih = h;
                  iw = h * ratio;
                  if (iw > w) iw = w;
               }
             x += ((w - iw) / 2);
             y += ((h - ih) / 2);
             w = iw;
             h = ih;
          }
     }
   evas_object_move(sd->o_vid, x, y);
   evas_object_resize(sd->o_vid, w, h);
   if ((sd->piw != sd->iw) || (sd->pih != sd->ih))
     {
        sd->piw = sd->iw;
        sd->pih = sd->ih;
        evas_object_smart_callback_call(obj, "frame_resize", NULL);
     }
   if ((sd->tw > 0) && (sd->th > 0) && (sd->doart) && 
       (!emotion_object_video_handled_get(sd->o_vid)))
     {
        int iw, ih;

        iw = (sd->tw * h) / sd->th;
        ih = h;
        if (iw < w)
          {
             ih = (sd->th * w) / sd->tw;
             iw = w;
          }
        x += ((w - iw) / 2);
        y += ((h - ih) / 2);
        w = iw;
        h = ih;
        evas_object_show(sd->o_img);
     }
   else
     {
        evas_object_hide(sd->o_img);
     }
   evas_object_move(sd->o_img, x, y);
   evas_object_resize(sd->o_img, w, h);
}

static void _smart_calculate(Evas_Object *obj);

static void
_smart_add(Evas_Object *obj)
{
   Video *sd;
   Evas_Object *o;

   sd = calloc(1, sizeof(Video));
   EINA_SAFETY_ON_NULL_RETURN(sd);
   evas_object_smart_data_set(obj, sd);

   _parent_sc.add(obj);

   o = evas_object_rectangle_add(evas_object_evas_get(obj));
   evas_object_smart_member_add(o, obj);
   sd->clip = o;
   evas_object_color_set(o, 255, 255, 255, 255);
}

static void
_smart_del(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->file) eina_stringshare_del(sd->file);
   if (sd->realfile) eina_stringshare_del(sd->realfile);
   if (sd->artfile) eina_stringshare_del(sd->artfile);
   if (sd->clip) evas_object_del(sd->clip);
   if (sd->o_vid) evas_object_del(sd->o_vid);
   if (sd->o_img) evas_object_del(sd->o_img);
   if (sd->o_event) evas_object_del(sd->o_event);
   if (sd->smooth_timer) sd->smooth_timer = ecore_timer_del(sd->smooth_timer);
   if (sd->restart_job) ecore_job_del(sd->restart_job);

   if (sd->exe)
     {
        ecore_exe_kill(sd->exe);
        ecore_exe_free(sd->exe);
        sd->exe = NULL;
     }
   if (sd->exe_handler)
     {
        ecore_event_handler_del(sd->exe_handler);
        sd->exe_handler = NULL;
     }
   _parent_sc.del(obj);

   emotion_shutdown();
}

static void
_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
{
   Video *sd = evas_object_smart_data_get(obj);
   Evas_Coord ox, oy, ow, oh;
   if (!sd) return;
   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
   if ((ow == w) && (oh == h)) return;
   evas_object_smart_changed(obj);
   evas_object_resize(sd->clip, ow, oh);
}

static Eina_Bool
_unsmooth_timeout(void *data)
{
   Video *sd = evas_object_smart_data_get(data);
   Evas_Coord ox, oy, ow, oh;

   if (!sd) return EINA_FALSE;
   evas_object_geometry_get(data, &ox, &oy, &ow, &oh);
   sd->smooth_timer = NULL;
   sd->nosmooth = EINA_FALSE;
   emotion_object_smooth_scale_set(sd->o_vid,
                                   (!sd->nosmooth) & (!sd->lowqual));
   return EINA_FALSE;
}

static void
_smooth_handler(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   double interval;

   if (!sd) return;
   interval = ecore_animator_frametime_get();
   if (interval <= 0.0) interval = 1.0/60.0;
   if (!sd->nosmooth)
     {
        if (sd->resizes >= 2)
          {
             sd->nosmooth = EINA_TRUE;
             sd->resizes = 0;
             emotion_object_smooth_scale_set(sd->o_vid,
                                             (!sd->nosmooth) & (!sd->lowqual));
             if (sd->smooth_timer)
               sd->smooth_timer = ecore_timer_del(sd->smooth_timer);
             sd->smooth_timer = ecore_timer_add(interval * 10,
                                                _unsmooth_timeout, obj);
          }
     }
   else
     {
        if (sd->smooth_timer)
          sd->smooth_timer = ecore_timer_del(sd->smooth_timer);
        sd->smooth_timer = ecore_timer_add(interval * 10,
                                           _unsmooth_timeout, obj);
     }
}

static void
_smart_calculate(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   Evas_Coord ox, oy, ow, oh;

   if (!sd) return;
   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
   if ((ow != sd->w) || (oh != sd->h)) sd->resizes++;
   else sd->resizes = 0;
   _smooth_handler(obj);
   sd->w = ow;
   sd->h = oh;
   _ob_resize(obj, ox, oy, ow, oh);
   evas_object_move(sd->clip, ox, oy);
   evas_object_resize(sd->clip, ow, oh);
}

static void
_smart_move(Evas_Object *obj, Evas_Coord x EINA_UNUSED, Evas_Coord y EINA_UNUSED)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_smart_changed(obj);
}

static void
_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   Evas_Event_Mouse_Down *ev = event;
   Video *sd = evas_object_smart_data_get(data);
   if (!sd) return;

   if (sd->down.down) return;
   if (ev->button != 1) return;
   sd->down.x = ev->canvas.x;
   sd->down.y = ev->canvas.y;
   sd->down.down = EINA_TRUE;
}

static void
_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   Evas_Event_Mouse_Down *ev = event;
   Video *sd = evas_object_smart_data_get(data);
   Evas_Coord dx, dy;
   if (!sd) return;

   if (!sd->down.down) return;
   sd->down.down = EINA_FALSE;
   dx = abs(ev->canvas.x - sd->down.x);
   dy = abs(ev->canvas.y - sd->down.y);
   if ((dx <= elm_config_finger_size_get()) &&
       (dy <= elm_config_finger_size_get()))
     evas_object_smart_callback_call(data, "clicked", NULL);
}

static void
_smart_init(void)
{
   static Evas_Smart_Class sc;

   evas_object_smart_clipped_smart_set(&_parent_sc);
   sc           = _parent_sc;
   sc.name      = "video";
   sc.version   = EVAS_SMART_CLASS_VERSION;
   sc.add       = _smart_add;
   sc.del       = _smart_del;
   sc.resize    = _smart_resize;
   sc.move      = _smart_move;
   sc.calculate = _smart_calculate;
   _smart = evas_smart_class_new(&sc);
}

Evas_Object *
video_add(Evas_Object *parent)
{
   Evas *e;
   Evas_Object *obj, *o;
   Video *sd;
// XXX: to do later
//   Config *config;

   EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
   e = evas_object_evas_get(parent);
   if (!e) return NULL;

   if (!_smart) _smart_init();
   obj = evas_object_smart_add(e, _smart);
   sd = evas_object_smart_data_get(obj);
   if (!sd) return obj;

   emotion_init();
   o = sd->o_vid = emotion_object_add(evas_object_evas_get(obj));
   emotion_object_keep_aspect_set(o, EMOTION_ASPECT_KEEP_NONE);
// XXX: to do later
//   config = config_get();
   evas_object_smart_callback_add(o, "frame_decode", _cb_vid_frame, obj);
   evas_object_smart_callback_add(o, "frame_resize", _cb_vid_resize, obj);
   evas_object_smart_callback_add(o, "decode_stop", _cb_vid_stop, obj);
   evas_object_smart_callback_add(o, "progress_change", _cb_vid_progress, obj);
   evas_object_smart_callback_add(o, "ref_change", _cb_vid_ref, obj);
   evas_object_smart_callback_add(o, "open_done", _cb_open_done, obj);
   evas_object_smart_callback_add(o, "position_update", _cb_position_update, obj);
   evas_object_smart_callback_add(o, "length_change", _cb_length_change, obj);
   evas_object_smart_callback_add(o, "title_change", _cb_title_change, obj);
   evas_object_smart_callback_add(o, "audio_level_change", _cb_audio_change, obj);
   evas_object_smart_callback_add(o, "channels_change", _cb_channels_change, obj);
   evas_object_smart_callback_add(o, "playback_started", _cb_play_start, obj);
   evas_object_smart_callback_add(o, "playback_finished", _cb_play_finish, obj);
   evas_object_smart_callback_add(o, "button_num_change", _cb_button_num_change, obj);
   evas_object_smart_callback_add(o, "button_change", _cb_button_change, obj);
   evas_object_smart_member_add(o, obj);
   evas_object_clip_set(o, sd->clip);

   sd->o_img = evas_object_image_filled_add(e);
   evas_object_smart_member_add(sd->o_img, obj);

   sd->o_event = evas_object_rectangle_add(e);
   evas_object_color_set(sd->o_event, 0, 0, 0, 0);
   evas_object_repeat_events_set(sd->o_event, EINA_TRUE);
   evas_object_smart_member_add(sd->o_event, obj);
   evas_object_clip_set(sd->o_event, sd->clip);
   evas_object_show(sd->o_event);
   evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, obj);
   evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, obj);
   return obj;
}

void
video_file_set(Evas_Object *obj, const char *file)
{
   char *realfile = NULL;
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_hide(sd->o_img);
   evas_object_hide(sd->o_vid);
   evas_object_hide(sd->clip);
   eina_stringshare_replace(&(sd->file), file);
   if (sd->file)
     {
        const char *colon = strchr(sd->file, ':');
        Eina_Bool is_url = EINA_FALSE;

        if ((colon) && (colon[1] == '/') && (colon[2] == '/'))
          is_url = EINA_TRUE;
        if (!strncasecmp(sd->file, "file:/", 6))
          {
             Efreet_Uri *uri = efreet_uri_decode(sd->file);
             if (uri)
               {
                  realfile = ecore_file_realpath(uri->path);
                  efreet_uri_free(uri);
               }
          }
        else if (is_url)
          realfile = strdup(sd->file);
        else
          realfile = ecore_file_realpath(sd->file);
        eina_stringshare_replace(&(sd->realfile), realfile);
        free(realfile);
        eina_stringshare_replace(&(sd->artfile), NULL);
        emotion_object_file_set(sd->o_vid, sd->file);
        video_position_set(obj, 0.0);
        if (sd->doart)
          {
             const char *extn = strchr(sd->file, '.');
             if (extn)
               {
                  if ((!strcasecmp(extn, ".mp3")) ||
                      (!strcasecmp(extn, ".m4a")) ||
                      (!strcasecmp(extn, ".oga")) ||
                      (!strcasecmp(extn, ".aac")) ||
                      (!strcasecmp(extn, ".flac")) ||
                      (!strcasecmp(extn, ".wav")))
                    {
                       _art_check(obj);
                    }
               }
          }
     }
   else
     {
        eina_stringshare_replace(&(sd->realfile), NULL);
        eina_stringshare_replace(&(sd->artfile), NULL);
     }
}

const char *
video_file_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return sd->file;
}

void
video_sub_file_set(Evas_Object *obj, const char *file)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_video_subtitle_file_set(sd->o_vid, file);
}

void
video_mute_set(Evas_Object *obj, Eina_Bool mute)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_audio_mute_set(sd->o_vid, mute);
   evas_object_smart_callback_call(obj, "audio", NULL);
}

Eina_Bool
video_mute_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return emotion_object_audio_mute_get(sd->o_vid);
}

void
video_play_set(Evas_Object *obj, Eina_Bool play)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;

   if (sd->volume)
     video_volume_set(obj, sd->volume);

   emotion_object_play_set(sd->o_vid, play);
}

Eina_Bool
video_play_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return emotion_object_play_get(sd->o_vid);
}

void
video_loop_set(Evas_Object *obj, Eina_Bool loop)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   sd->loop = loop;
}

Eina_Bool
video_loop_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return sd->loop;
}

void
video_art_set(Evas_Object *obj, Eina_Bool art)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   sd->doart = art;
   if (!sd->doart) evas_object_hide(sd->o_img);
}

Eina_Bool
video_art_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return sd->doart;
}

void
video_fill_set(Evas_Object *obj, Eina_Bool fill)
{
   Evas_Coord ox, oy, ow, oh;
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   sd->fill = fill;
   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
   _ob_resize(obj, ox, oy, ow, oh);
}

Eina_Bool
video_fill_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return sd->fill;
}

void
video_stop(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_hide(sd->o_vid);
   evas_object_hide(sd->clip);
   emotion_object_play_set(sd->o_vid, EINA_FALSE);
   emotion_object_position_set(sd->o_vid, 0.0);
}

void
video_position_set(Evas_Object *obj, double pos)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_position_set(sd->o_vid, pos);
}

double
video_position_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0.0;
   return emotion_object_position_get(sd->o_vid);
}

double
video_length_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0.0;
   return emotion_object_play_length_get(sd->o_vid);
}

void
video_ratio_size_get(Evas_Object *obj, int *w, int *h)
{
   Video *sd = evas_object_smart_data_get(obj);
   *w = 1;
   *h = 1;
   if (!sd) return;
   emotion_object_size_get(sd->o_vid, &(sd->iw), &(sd->ih));
   if ((sd->iw <= 0) || (sd->ih <= 0))
     {
        *w = 1;
        *h = 1;
     }
   else
     {
        double ratio;

        ratio = emotion_object_ratio_get(sd->o_vid);
        if (ratio > 0.0) sd->iw = (sd->ih * ratio) + 0.5;
        else ratio = (double)sd->iw / (double)sd->ih;
        *w = sd->iw;
        *h = sd->ih;
     }
}

void
video_eject(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_eject(sd->o_vid);
}

int
video_chapter_count(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_chapter_count(sd->o_vid);
}

void
video_chapter_set(Evas_Object *obj, int chapter)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_chapter_set(sd->o_vid, chapter);
}

int
video_chapter_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_chapter_get(sd->o_vid);
}

const char *
video_chapter_name_get(Evas_Object *obj, int chapter)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_chapter_name_get(sd->o_vid, chapter);
}

void
video_volume_set(Evas_Object *obj, double vol)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_audio_volume_set(sd->o_vid, vol);
   sd->volume = vol;
   evas_object_smart_callback_call(obj, "audio", NULL);
}

double
video_volume_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0.0;
   return emotion_object_audio_volume_get(sd->o_vid);
}

Eina_Bool
video_has_video_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return emotion_object_video_handled_get(sd->o_vid);
}

Eina_Bool
video_has_audio_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return emotion_object_audio_handled_get(sd->o_vid);
}

const char *
video_title_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_title_get(sd->o_vid);
}

int
video_audio_channel_count(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_audio_channel_count(sd->o_vid);
}

void
video_audio_channel_set(Evas_Object *obj, int chan)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_audio_channel_set(sd->o_vid, chan);
}

int
video_audio_channel_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_audio_channel_get(sd->o_vid);
}

const char *
video_audio_channel_name_get(Evas_Object *obj, int chan)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_audio_channel_name_get(sd->o_vid, chan);
}

int
video_video_channel_count(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_video_channel_count(sd->o_vid);
}

void
video_video_channel_set(Evas_Object *obj, int chan)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_video_channel_set(sd->o_vid, chan);
}

int
video_video_channel_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_video_channel_get(sd->o_vid);
}

const char *
video_video_channel_name_get(Evas_Object *obj, int chan)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_video_channel_name_get(sd->o_vid, chan);
}

int
video_spu_channel_count(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_spu_channel_count(sd->o_vid);
}

void
video_spu_channel_set(Evas_Object *obj, int chan)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_spu_channel_set(sd->o_vid, chan);
}

int
video_spu_channel_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_spu_channel_get(sd->o_vid);
}

const char *
video_spu_channel_name_get(Evas_Object *obj, int chan)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_spu_channel_name_get(sd->o_vid, chan);
}

int
video_spu_button_count(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_spu_button_count_get(sd->o_vid);
}

int
video_spu_button_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return 0;
   return emotion_object_spu_button_get(sd->o_vid);
}

void
video_event_send(Evas_Object *obj, Emotion_Event ev)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   emotion_object_event_simple_send(sd->o_vid, ev);
}

void
video_lowquality_set(Evas_Object *obj, Eina_Bool lowq)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   sd->lowqual = lowq;
   emotion_object_smooth_scale_set(sd->o_vid,
                                   (!sd->nosmooth) & (!sd->lowqual));
}

Eina_Bool
video_lowquality_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return EINA_FALSE;
   return sd->lowqual;
}

const char *
video_meta_title_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_TITLE);
}

const char *
video_meta_artist_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_ARTIST);
}

const char *
video_meta_album_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_ALBUM);
}

const char *
video_meta_year_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_YEAR);
}

Evas_Object *
video_meta_artwork_get(Evas_Object *obj, const char *path, int type)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_file_meta_artwork_get(sd->o_vid, path, type);
}

const char *
video_meta_genre_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_GENRE);
}

const char *
video_meta_comment_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_COMMENT);
}

void
video_file_autosub_set(Evas_Object *obj, const char *file, const char *sub)
{
   if ((file) && (!sub))
     {
        const char *subtypes[] =
          {
             ".srt",
             ".SRT",
             ".sub",
             ".SUB",
             NULL
          };
        char *sub = alloca(strlen(file) + 1 + 16);
        char *p;
        int i;
        Eina_Bool found = EINA_FALSE;

        // try last dont in filename first...
        strcpy(sub, file);
        p = strrchr(sub, '.');
        if (p)
          {
             for (i = 0; subtypes[i]; i++)
               {
                  strcpy(p, subtypes[i]);
                  if (ecore_file_exists(sub))
                    {
                       video_sub_file_set(obj, sub);
                       found = EINA_TRUE;
                       break;
                    }
               }
          }
        // now try first dot as a fallback.
        if (!found)
          {
             strcpy(sub, file);
             p = strchr(sub, '.');
             if (p)
               {
                  for (i = 0; subtypes[i]; i++)
                    {
                       strcpy(p, subtypes[i]);
                       if (ecore_file_exists(sub))
                         {
                            video_sub_file_set(obj, sub);
                            found = EINA_TRUE;
                            break;
                         }
                    }
               }
          }
        if (!found) video_sub_file_set(obj, NULL);
     }
   else
     {
        video_sub_file_set(obj, sub);
     }

   if (file)
     video_file_set(obj, file);
}

const char *
video_artfile_get(Evas_Object *obj)
{
   Video *sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return sd->artfile;
}

// emotion_object_seekable_get
// emotion_object_play_speed_set
// emotion_object_play_speed_get
// emotion_object_meta_info_get
// emotion_object_buffer_size_get
// emotion_object_video_mute_set
// emotion_object_video_mute_get
// emotion_object_spu_mute_set
// emotion_object_spu_mute_get
// emotion_object_progress_info_get
// emotion_object_progress_status_get
// emotion_object_ref_file_get
