#include "cpumonitor.h"

typedef struct _Thread_Config Thread_Config;

struct _Thread_Config
{
   int                  interval;
   int                  num_cores;
   int                  percent;
   unsigned long        total;
   unsigned long        idle;
   Instance            *inst;
   E_Powersave_Sleeper *sleeper;
   Eina_List           *cores;
};

static void
_cpumonitor_face_update(Thread_Config *thc)
{
   Eina_List *l;
   CPU_Core *core;

   EINA_LIST_FOREACH(thc->cores, l, core)
     {
        Edje_Message_Int_Set *usage_msg;
        usage_msg = malloc(sizeof(Edje_Message_Int_Set) + 1 * sizeof(int));
        EINA_SAFETY_ON_NULL_RETURN(usage_msg);
        usage_msg->count = 1;
        usage_msg->val[0] = core->percent;
        edje_object_message_send(elm_layout_edje_get(core->layout), EDJE_MESSAGE_INT_SET, 1,
                                 usage_msg);
        E_FREE(usage_msg);
     }
   if (thc->inst->cfg->cpumonitor.popup)
     {
        elm_progressbar_value_set(thc->inst->cfg->cpumonitor.popup_pbar,
                                  (float)thc->percent / 100);
     }
}

static Evas_Object *
_cpumonitor_configure_cb(Evas_Object *g)
{
   Instance *inst = evas_object_data_get(g, "Instance");

   if (!sysinfo_config) return NULL;
   if (inst->cfg->cpumonitor.popup) return NULL;
   return cpumonitor_configure(inst);
}

static void
_cpumonitor_popup_dismissed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   Instance *inst = data;
   E_FREE_FUNC(obj, evas_object_del);
   inst->cfg->cpumonitor.popup = NULL;
}

static void
_cpumonitor_popup_deleted(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Instance *inst = data;
   inst->cfg->cpumonitor.popup = NULL;
}

static void
_cpumonitor_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Evas_Object *popup, *box, *pbar, *label;
   Evas_Event_Mouse_Up *ev = event_data;
   Instance *inst = data;
   char text[256];

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (ev->button == 1)
     {
        if (inst->cfg->cpumonitor.popup)
          {
             elm_ctxpopup_dismiss(inst->cfg->cpumonitor.popup);
             return;
          }
        popup = elm_ctxpopup_add(e_comp->elm);
        elm_object_style_set(popup, "noblock");
        evas_object_smart_callback_add(popup, "dismissed", _cpumonitor_popup_dismissed, inst);
        evas_object_event_callback_add(popup, EVAS_CALLBACK_DEL, _cpumonitor_popup_deleted, inst);

        box = elm_box_add(popup);
        E_EXPAND(box); E_FILL(box);
        elm_object_content_set(popup, box);
        evas_object_show(box);

        snprintf(text, sizeof(text), "<big><b>%s</b></big>", _("Total CPU Usage"));
        label = elm_label_add(box);
        E_EXPAND(label); E_ALIGN(label, 0.5, 0.5);
        elm_object_text_set(label, text);
        elm_box_pack_end(box, label);
        evas_object_show(label);

        pbar = elm_progressbar_add(box);
        E_EXPAND(pbar); E_FILL(pbar);
        elm_progressbar_span_size_set(pbar, 200 * e_scale);
        elm_progressbar_value_set(pbar, (float)inst->cfg->cpumonitor.percent / 100);
        elm_box_pack_end(box, pbar);
        evas_object_show(pbar);
        inst->cfg->cpumonitor.popup_pbar = pbar;

        e_gadget_util_ctxpopup_place(inst->o_main, popup,
                                     inst->cfg->cpumonitor.o_gadget);
        evas_object_show(popup);
        inst->cfg->cpumonitor.popup = popup;
     }
}

static void
_cpumonitor_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Evas_Coord w = 1, h = 1, sw, sh;
   Instance *inst = data;
   int num_cores = inst->cfg->cpumonitor.cores;

   if (!num_cores || !inst->o_main) return;

   edje_object_parts_extends_calc(elm_layout_edje_get(obj), 0, 0, &w, &h);
   evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE, _cpumonitor_resize_cb, inst);
   if (inst->cfg->esm == E_SYSINFO_MODULE_CPUMONITOR)
     {
        evas_object_geometry_get(inst->o_main, 0, 0, &sw, &sh);
     }
   else
     {
        sw = w;
        sh = h;
     }
   if (e_gadget_site_orient_get(e_gadget_site_get(inst->o_main)) == E_GADGET_SITE_ORIENT_VERTICAL)
     {
        w = sw;
        h *= num_cores;
     }
   else
     {
        w *= num_cores;
        h = sh;
     }
   if (w < 1) w = 1;
   if (h < 1) h = 1;
   evas_object_size_hint_aspect_set(inst->cfg->cpumonitor.o_gadget_box, EVAS_ASPECT_CONTROL_BOTH, w, h);
   if (inst->cfg->esm == E_SYSINFO_MODULE_CPUMONITOR)
     evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, w, h);
}

static void
_cpumonitor_cb_usage_check_main(void *data, Ecore_Thread *th)
{
   Thread_Config *thc = data;
   for (;; )
     {
        if (ecore_thread_check(th)) break;
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
        _cpumonitor_sysctl_getusage(&thc->total, &thc->idle, &thc->percent, thc->cores);
#else
        _cpumonitor_proc_getusage(&thc->total, &thc->idle, &thc->percent, thc->cores);
#endif
        ecore_thread_feedback(th, NULL);
        if (ecore_thread_check(th)) break;
        e_powersave_sleeper_sleep(thc->sleeper, thc->interval);
        if (ecore_thread_check(th)) break;
     }
}

static void
_cpumonitor_cb_usage_check_notify(void *data,
                                  Ecore_Thread *th EINA_UNUSED,
                                  void *msg EINA_UNUSED)
{
   Thread_Config *thc = data;

   if (!thc->inst || !thc->inst->cfg) return;
   if (thc->inst->cfg->esm != E_SYSINFO_MODULE_CPUMONITOR && thc->inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return;
   thc->inst->cfg->cpumonitor.percent = thc->percent;
   _cpumonitor_face_update(thc);
}

static void
_cpumonitor_cb_usage_check_end(void *data, Ecore_Thread *th EINA_UNUSED)
{
   Thread_Config *thc = data;
   CPU_Core *core;

   e_powersave_sleeper_free(thc->sleeper);
   EINA_LIST_FREE(thc->cores, core)
     E_FREE(core);
   E_FREE(thc);
}

Evas_Object *
_cpumonitor_add_layout(Instance *inst)
{
   Evas_Object *layout;
   int orient = e_gadget_site_orient_get(e_gadget_site_get(inst->o_main));

   layout = elm_layout_add(inst->cfg->cpumonitor.o_gadget_box);
   edje_object_update_hints_set(elm_layout_edje_get(layout), EINA_TRUE);
   if (orient == E_GADGET_SITE_ORIENT_VERTICAL)
     e_theme_edje_object_set(layout, "base/theme/gadget/cpumonitor",
                             "e/gadget/cpumonitor/core/main_vert");
   else
     e_theme_edje_object_set(layout, "base/theme/gadget/cpumonitor",
                             "e/gadget/cpumonitor/core/main");
   E_EXPAND(layout);
   E_FILL(layout);
   elm_box_pack_end(inst->cfg->cpumonitor.o_gadget_box, layout);
   evas_object_show(layout);

   return layout;
}

static void
_cpumonitor_del_layouts(Instance *inst)
{
   elm_box_clear(inst->cfg->cpumonitor.o_gadget_box);
}

static Eina_Bool
_screensaver_on(void *data)
{
   Instance *inst = data;

   if (!ecore_thread_check(inst->cfg->cpumonitor.usage_check_thread))
     {
        _cpumonitor_del_layouts(inst);
        ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread);
        inst->cfg->cpumonitor.usage_check_thread = NULL;
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_screensaver_off(void *data)
{
   Instance *inst = data;

   _cpumonitor_config_updated(inst);

   return ECORE_CALLBACK_RENEW;
}

void
_cpumonitor_config_updated(Instance *inst)
{
   Thread_Config *thc;
   CPU_Core *core;
   int i = 0;

   if (inst->cfg->id == -1)
     {
        int percent = 15;
        thc = E_NEW(Thread_Config, 1);
        if (thc)
          {
             thc->inst = inst;
             thc->total = 0;
             thc->idle = 0;
             thc->percent = 60;
             thc->num_cores = 4;
             inst->cfg->cpumonitor.cores = thc->num_cores;
             for (i = 0; i < 4; i++)
               {
                  core = E_NEW(CPU_Core, 1);
                  core->layout = _cpumonitor_add_layout(inst);
                  if (i == 0)
                    evas_object_event_callback_add(core->layout, EVAS_CALLBACK_RESIZE, _cpumonitor_resize_cb, inst);
                  core->percent = percent;
                  core->total = 0;
                  core->idle = 0;
                  thc->cores = eina_list_append(thc->cores, core);
                  percent += 15;
               }
             _cpumonitor_face_update(thc);
             EINA_LIST_FREE(thc->cores, core)
               E_FREE(core);
             E_FREE(thc);
          }
        return;
     }
   if (!ecore_thread_check(inst->cfg->cpumonitor.usage_check_thread))
     {
        _cpumonitor_del_layouts(inst);
        ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread);
        inst->cfg->cpumonitor.usage_check_thread = NULL;
     }
   thc = E_NEW(Thread_Config, 1);
   if (thc)
     {
        thc->inst = inst;
        thc->total = 0;
        thc->idle = 0;
        thc->percent = 0;
        thc->interval = inst->cfg->cpumonitor.poll_interval;
        thc->sleeper = e_powersave_sleeper_new();
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
        thc->num_cores = _cpumonitor_sysctl_getcores();
#else
        thc->num_cores = _cpumonitor_proc_getcores();
#endif
        inst->cfg->cpumonitor.cores = thc->num_cores;
        for (i = 0; i < thc->num_cores; i++)
          {
             core = E_NEW(CPU_Core, 1);
             core->layout = _cpumonitor_add_layout(inst);
             if (i == 0)
               evas_object_event_callback_add(core->layout, EVAS_CALLBACK_RESIZE, _cpumonitor_resize_cb, inst);
             core->percent = 0;
             core->total = 0;
             core->idle = 0;
             thc->cores = eina_list_append(thc->cores, core);
          }
        inst->cfg->cpumonitor.usage_check_thread =
          ecore_thread_feedback_run(_cpumonitor_cb_usage_check_main,
                                    _cpumonitor_cb_usage_check_notify,
                                    _cpumonitor_cb_usage_check_end,
                                    _cpumonitor_cb_usage_check_end, thc, EINA_TRUE);
     }
   e_config_save_queue();
}

static void
_cpumonitor_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Instance *inst = data;
   Ecore_Event_Handler *handler;

   if (inst->o_main != event_data) return;

   if (inst->cfg->cpumonitor.popup)
     E_FREE_FUNC(inst->cfg->cpumonitor.popup, evas_object_del);
   if (inst->cfg->cpumonitor.configure)
     E_FREE_FUNC(inst->cfg->cpumonitor.configure, evas_object_del);
   EINA_LIST_FREE(inst->cfg->cpumonitor.handlers, handler)
     ecore_event_handler_del(handler);
   evas_object_smart_callback_del_full(e_gadget_site_get(inst->o_main), "gadget_removed",
                                       _cpumonitor_removed_cb, inst);
   evas_object_event_callback_del_full(inst->o_main, EVAS_CALLBACK_DEL, sysinfo_cpumonitor_remove, data);
   if (!ecore_thread_check(inst->cfg->cpumonitor.usage_check_thread))
     {
        _cpumonitor_del_layouts(inst);
        ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread);
        inst->cfg->cpumonitor.usage_check_thread = NULL;
     }
   sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg);
   if (inst->cfg->id >= 0)
     sysinfo_instances = eina_list_remove(sysinfo_instances, inst);
   E_FREE(inst->cfg);
   E_FREE(inst);
}

void
sysinfo_cpumonitor_remove(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Instance *inst = data;
   Ecore_Event_Handler *handler;

   if (inst->cfg->cpumonitor.popup)
     E_FREE_FUNC(inst->cfg->cpumonitor.popup, evas_object_del);
   if (inst->cfg->cpumonitor.configure)
     E_FREE_FUNC(inst->cfg->cpumonitor.configure, evas_object_del);
   EINA_LIST_FREE(inst->cfg->cpumonitor.handlers, handler)
     ecore_event_handler_del(handler);
   if (!ecore_thread_check(inst->cfg->cpumonitor.usage_check_thread))
     {
        _cpumonitor_del_layouts(inst);
        ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread);
        inst->cfg->cpumonitor.usage_check_thread = NULL;
     }
}

static void
_cpumonitor_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Instance *inst = data;
   int orient = e_gadget_site_orient_get(e_gadget_site_get(inst->o_main));

   e_gadget_configure_cb_set(inst->o_main, _cpumonitor_configure_cb);

   inst->cfg->cpumonitor.o_gadget = elm_layout_add(inst->o_main);
   if (orient == E_GADGET_SITE_ORIENT_VERTICAL)
     e_theme_edje_object_set(inst->cfg->cpumonitor.o_gadget,
                             "base/theme/gadget/cpumonitor",
                             "e/gadget/cpumonitor/main_vert");
   else
     e_theme_edje_object_set(inst->cfg->cpumonitor.o_gadget,
                             "base/theme/gadget/cpumonitor",
                             "e/gadget/cpumonitor/main");
   E_EXPAND(inst->cfg->cpumonitor.o_gadget);
   E_FILL(inst->cfg->cpumonitor.o_gadget);
   elm_box_pack_end(inst->o_main, inst->cfg->cpumonitor.o_gadget);
   evas_object_event_callback_add(inst->cfg->cpumonitor.o_gadget, EVAS_CALLBACK_MOUSE_UP,
                                  _cpumonitor_mouse_up_cb, inst);
   evas_object_show(inst->cfg->cpumonitor.o_gadget);

   inst->cfg->cpumonitor.o_gadget_box = elm_box_add(inst->cfg->cpumonitor.o_gadget);
   elm_box_homogeneous_set(inst->cfg->cpumonitor.o_gadget_box, EINA_TRUE);
   if (orient == E_GADGET_SITE_ORIENT_VERTICAL)
     elm_box_horizontal_set(inst->cfg->cpumonitor.o_gadget_box, EINA_FALSE);
   else
     elm_box_horizontal_set(inst->cfg->cpumonitor.o_gadget_box, EINA_TRUE);
   E_EXPAND(inst->cfg->cpumonitor.o_gadget_box);
   E_FILL(inst->cfg->cpumonitor.o_gadget_box);
   elm_layout_content_set(inst->cfg->cpumonitor.o_gadget, "e.swallow.content", inst->cfg->cpumonitor.o_gadget_box);
   evas_object_show(inst->cfg->cpumonitor.o_gadget_box);

   evas_object_smart_callback_del_full(obj, "gadget_created", _cpumonitor_created_cb, data);

   E_LIST_HANDLER_APPEND(inst->cfg->cpumonitor.handlers, E_EVENT_SCREENSAVER_ON, _screensaver_on, inst);
   E_LIST_HANDLER_APPEND(inst->cfg->cpumonitor.handlers, E_EVENT_SCREENSAVER_OFF, _screensaver_off, inst);

   _cpumonitor_config_updated(inst);
}

Evas_Object *
sysinfo_cpumonitor_create(Evas_Object *parent, Instance *inst)
{
   inst->cfg->cpumonitor.percent = 0;
   inst->cfg->cpumonitor.popup = NULL;
   inst->cfg->cpumonitor.configure = NULL;
   inst->cfg->cpumonitor.o_gadget = elm_layout_add(parent);
   e_theme_edje_object_set(inst->cfg->cpumonitor.o_gadget,
                           "base/theme/gadget/cpumonitor",
                           "e/gadget/cpumonitor/main");
   E_EXPAND(inst->cfg->cpumonitor.o_gadget);
   E_FILL(inst->cfg->cpumonitor.o_gadget);
   evas_object_event_callback_add(inst->cfg->cpumonitor.o_gadget, EVAS_CALLBACK_MOUSE_UP,
                                  _cpumonitor_mouse_up_cb, inst);
   evas_object_show(inst->cfg->cpumonitor.o_gadget);

   inst->cfg->cpumonitor.o_gadget_box = elm_box_add(inst->cfg->cpumonitor.o_gadget);
   elm_box_homogeneous_set(inst->cfg->cpumonitor.o_gadget_box, EINA_TRUE);
   elm_box_horizontal_set(inst->cfg->cpumonitor.o_gadget_box, EINA_TRUE);
   E_EXPAND(inst->cfg->cpumonitor.o_gadget_box);
   E_FILL(inst->cfg->cpumonitor.o_gadget_box);
   elm_layout_content_set(inst->cfg->cpumonitor.o_gadget, "e.swallow.content", inst->cfg->cpumonitor.o_gadget_box);
   evas_object_show(inst->cfg->cpumonitor.o_gadget_box);

   E_LIST_HANDLER_APPEND(inst->cfg->cpumonitor.handlers, E_EVENT_SCREENSAVER_ON, _screensaver_on, inst);
   E_LIST_HANDLER_APPEND(inst->cfg->cpumonitor.handlers, E_EVENT_SCREENSAVER_OFF, _screensaver_off, inst);

   _cpumonitor_config_updated(inst);

   return inst->cfg->cpumonitor.o_gadget;
}

static Config_Item *
_conf_item_get(int *id)
{
   Config_Item *ci;
   Eina_List *l;

   if (*id > 0)
     {
        EINA_LIST_FOREACH(sysinfo_config->items, l, ci)
          if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_CPUMONITOR) return ci;
     }

   ci = E_NEW(Config_Item, 1);

   if (*id != -1)
     ci->id = eina_list_count(sysinfo_config->items) + 1;
   else
     ci->id = -1;

   ci->esm = E_SYSINFO_MODULE_CPUMONITOR;
   ci->cpumonitor.poll_interval = 32;
   ci->cpumonitor.popup = NULL;
   ci->cpumonitor.configure = NULL;
   sysinfo_config->items = eina_list_append(sysinfo_config->items, ci);

   return ci;
}

Evas_Object *
cpumonitor_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED)
{
   Instance *inst;

   inst = E_NEW(Instance, 1);
   inst->cfg = _conf_item_get(id);
   *id = inst->cfg->id;
   inst->cfg->cpumonitor.popup = NULL;
   inst->cfg->cpumonitor.configure = NULL;
   inst->o_main = elm_box_add(parent);
   evas_object_data_set(inst->o_main, "Instance", inst);
   evas_object_smart_callback_add(parent, "gadget_created", _cpumonitor_created_cb, inst);
   evas_object_smart_callback_add(parent, "gadget_removed", _cpumonitor_removed_cb, inst);
   evas_object_event_callback_add(inst->o_main, EVAS_CALLBACK_DEL, sysinfo_cpumonitor_remove, inst);
   evas_object_show(inst->o_main);

   if (inst->cfg->id < 0) return inst->o_main;

   sysinfo_instances =
     eina_list_append(sysinfo_instances, inst);

   return inst->o_main;
}

