#include "e.h"

Evas_Smart *_e_spectrum_smart = NULL;

typedef struct _E_Spectrum E_Spectrum;

struct _E_Spectrum
{
   Evas_Object      *o_spectrum;
   Evas_Object      *o_event;
   Evas_Object      *o_cursor;

   int               iw, ih; /* square image width/height */
   E_Color_Component mode;

   E_Color          *cv;
   Ecore_Timer      *draw_timer;
};

static Eina_Bool _e_spectrum_redraw(void *d);

static void
_e_spectrum_smart_add(Evas_Object *o)
{
   E_Spectrum *sp;
   sp = E_NEW(E_Spectrum, 1);

   if (!sp) return;

   evas_object_smart_data_set(o, sp);

   sp->mode = E_COLOR_COMPONENT_R;

   sp->o_spectrum = evas_object_image_add(evas_object_evas_get(o));
   sp->iw = sp->ih = 255;
   evas_object_image_size_set(sp->o_spectrum, sp->iw, sp->ih);
   evas_object_image_alpha_set(sp->o_spectrum, 1);

   evas_object_smart_member_add(sp->o_spectrum, o);
}

static void
_e_spectrum_smart_del(Evas_Object *o)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   evas_object_del(sp->o_spectrum);
   evas_object_del(sp->o_event);
   evas_object_del(sp->o_cursor);

   if (sp->draw_timer) ecore_timer_del(sp->draw_timer);

   E_FREE(sp);
}

static void
_e_spectrum_smart_show(Evas_Object *o)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   evas_object_show(sp->o_spectrum);
   evas_object_show(sp->o_event);
   evas_object_show(sp->o_cursor);
}

static void
_e_spectrum_smart_hide(Evas_Object *o)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   evas_object_hide(sp->o_spectrum);
   evas_object_hide(sp->o_event);
   evas_object_hide(sp->o_cursor);
}

static void
_e_spectrum_smart_move(Evas_Object *o, Evas_Coord x, Evas_Coord y)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   evas_object_move(sp->o_spectrum, x, y);
   evas_object_move(sp->o_event, x, y);
   evas_object_move(sp->o_cursor, x, y);
}

static void
_e_spectrum_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   evas_object_image_fill_set(sp->o_spectrum, 0, 0, w, h);

   evas_object_resize(sp->o_spectrum, w, h);
   evas_object_resize(sp->o_event, w, h);
   evas_object_resize(sp->o_cursor, w, h);
}

static void
_e_spectrum_smart_color_set(Evas_Object *o, int r, int g, int b, int a)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;
   evas_object_color_set(sp->o_spectrum, r, g, b, a);
   evas_object_color_set(sp->o_event, r, g, b, a);
   evas_object_color_set(sp->o_cursor, r, g, b, a);
}

static void
_e_spectrum_smart_clip_set(Evas_Object *o, Evas_Object *o_clip)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;
   evas_object_clip_set(sp->o_spectrum, o_clip);
   evas_object_clip_set(sp->o_event, o_clip);
   evas_object_clip_set(sp->o_cursor, o_clip);
}

static void
_e_spectrum_smart_clip_unset(Evas_Object *o)
{
   E_Spectrum *sp;

   sp = evas_object_smart_data_get(o);
   if (!sp) return;
   evas_object_clip_unset(sp->o_spectrum);
   evas_object_clip_unset(sp->o_event);
   evas_object_clip_unset(sp->o_cursor);
}

static void
_e_spectrum_smart_init(void)
{
   if (_e_spectrum_smart) return;
   {
      static const Evas_Smart_Class sc =
      {
         "e_spectrum",
         EVAS_SMART_CLASS_VERSION,
         _e_spectrum_smart_add,
         _e_spectrum_smart_del,
         _e_spectrum_smart_move,
         _e_spectrum_smart_resize,
         _e_spectrum_smart_show,
         _e_spectrum_smart_hide,
         _e_spectrum_smart_color_set,
         _e_spectrum_smart_clip_set,
         _e_spectrum_smart_clip_unset,
         NULL,
         NULL,
         NULL,
         NULL,
         NULL,
         NULL,
         NULL
      };
      _e_spectrum_smart = evas_smart_class_new(&sc);
   }
}

void
_e_spectrum_color_calc(E_Spectrum *sp, float vx, float vy, float vz, int *r, int *g, int *b)
{
   switch (sp->mode)
     {
      case E_COLOR_COMPONENT_R:
        *r = 255 * vz;
        *g = 255 * vy;
        *b = 255 * vx;
        break;

      case E_COLOR_COMPONENT_G:
        *r = 255 * vx;
        *g = 255 * vz;
        *b = 255 * vy;
        break;

      case E_COLOR_COMPONENT_B:
        *r = 255 * vy;
        *g = 255 * vx;
        *b = 255 * vz;
        break;

      case E_COLOR_COMPONENT_H:
        evas_color_hsv_to_rgb(vz * 360.0, vy, vx, r, g, b);
        break;

      case E_COLOR_COMPONENT_S:
        evas_color_hsv_to_rgb(vx * 360.0, vz, vy, r, g, b);
        break;

      case E_COLOR_COMPONENT_V:
        evas_color_hsv_to_rgb(vy * 360.0, vx, vz, r, g, b);
        break;

      default:
        break;
     }
}

void
_e_spectrum_2d_color_at(E_Spectrum *sp, int x, int y, int *r, int *g, int *b)
{
   int rr, gg, bb;
   float h, s, v;

   if (!sp || !sp->cv) return;

   switch (sp->mode)
     {
      case E_COLOR_COMPONENT_R:
        rr = sp->cv->r;
        gg = (1 - (y / (double)(sp->ih))) * 255;
        bb = (x / (double)(sp->iw)) * 255;
        break;

      case E_COLOR_COMPONENT_G:
        rr = (x / (double)(sp->iw)) * 255;
        gg = sp->cv->g;
        bb = (1 - (y / (double)(sp->ih))) * 255;
        break;

      case E_COLOR_COMPONENT_B:
        rr = (1 - (y / (double)(sp->ih))) * 255;
        gg = (x / (double)(sp->iw)) * 255;
        bb = sp->cv->b;
        break;

      case E_COLOR_COMPONENT_H:
        h = sp->cv->h;
        s = 1 - (y / (double)(sp->ih));
        v = x / (double)(sp->iw);
        evas_color_hsv_to_rgb(h, s, v, &rr, &gg, &bb);
        break;

      case E_COLOR_COMPONENT_S:
        s = sp->cv->s;
        v = 1 - (y / (double)(sp->ih));
        h = x / (double)(sp->iw) * 360;
        evas_color_hsv_to_rgb(h, s, v, &rr, &gg, &bb);
        break;

      case E_COLOR_COMPONENT_V:
        v = sp->cv->v;
        h = (1 - (y / (double)(sp->ih))) * 360;
        s = x / (double)(sp->iw);
        evas_color_hsv_to_rgb(h, s, v, &rr, &gg, &bb);
        break;

      case E_COLOR_COMPONENT_MAX:
      default:
        return;
     }

   if (r) *r = rr;
   if (g) *g = gg;
   if (b) *b = bb;
}

static Eina_Bool
_e_spectrum_redraw(void *d)
{
   E_Spectrum *sp = d;
   int *data;
   int i, j;
   int r, g, b;
   float vx, vy, vz = 0;

   data = evas_object_image_data_get(sp->o_spectrum, 1);
   if (!data)
     {
        sp->draw_timer = NULL;
        return ECORE_CALLBACK_CANCEL;
     }

   switch (sp->mode)
     {
      case E_COLOR_COMPONENT_R:
        vz = (float)sp->cv->r / 255;
        break;

      case E_COLOR_COMPONENT_G:
        vz = (float)sp->cv->g / 255;
        break;

      case E_COLOR_COMPONENT_B:
        vz = (float)sp->cv->b / 255;
        break;

      case E_COLOR_COMPONENT_H:
        vz = sp->cv->h / 360;
        break;

      case E_COLOR_COMPONENT_S:
        vz = sp->cv->s;
        break;

      case E_COLOR_COMPONENT_V:
        vz = sp->cv->v;
        break;

      case E_COLOR_COMPONENT_MAX:
        break;
     }

   for (i = 0; i < sp->ih; i++)
     {
        vy = (float)i / sp->ih;
        for (j = 0; j < sp->iw; j++)
          {
             vx = (float)j / sp->iw;
             _e_spectrum_color_calc(sp, vx, vy, vz, &r, &g, &b);
             data[(i * sp->iw) + j] =
               (sp->cv->a << 24) |
               (((r * sp->cv->a) / 255) << 16) |
               (((g * sp->cv->a) / 255) << 8) |
               (((b * sp->cv->a) / 255));
          }
     }

   evas_object_image_data_set(sp->o_spectrum, data);
   evas_object_image_data_update_add(sp->o_spectrum, 0, 0, sp->iw, sp->ih);
   sp->draw_timer = NULL;
   return ECORE_CALLBACK_CANCEL;
}

static void
_e_spectrum_update(E_Spectrum *sp)
{
   if (!sp || !sp->cv) return;

   if (sp->draw_timer) ecore_timer_del(sp->draw_timer);
   sp->draw_timer = ecore_timer_loop_add(.001, _e_spectrum_redraw, sp);
}

Evas_Object *
e_spectrum_add(Evas *e)
{
   _e_spectrum_smart_init();
   return evas_object_smart_add(e, _e_spectrum_smart);
}

void
e_spectrum_mode_set(Evas_Object *o, E_Color_Component mode)
{
   E_Spectrum *sp;

   if (evas_object_smart_smart_get(o) != _e_spectrum_smart) SMARTERRNR();
   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   if (sp->mode == mode) return;
   sp->mode = mode;
   _e_spectrum_update(sp);
}

E_Color_Component
e_spectrum_mode_get(Evas_Object *o)
{
   E_Spectrum *sp;

   if (evas_object_smart_smart_get(o) != _e_spectrum_smart) SMARTERR(0);
   sp = evas_object_smart_data_get(o);
   if (!sp) return -1;

   return sp->mode;
}

void
e_spectrum_color_value_set(Evas_Object *o, E_Color *cv)
{
   E_Spectrum *sp;

   if (evas_object_smart_smart_get(o) != _e_spectrum_smart) SMARTERRNR();
   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   sp->cv = cv;
   _e_spectrum_update(sp);
}

void
e_spectrum_update(Evas_Object *o)
{
   E_Spectrum *sp;

   if (evas_object_smart_smart_get(o) != _e_spectrum_smart) SMARTERRNR();
   sp = evas_object_smart_data_get(o);
   if (!sp) return;

   _e_spectrum_update(sp);
}

