#include "evas_common_soft16.h"

static Evas_Cache_Image *eci = NULL;
static int               reference = 0;

static Image_Entry      *_evas_common_soft16_image_new(void);
static void              _evas_common_soft16_image_delete(Image_Entry *ie);

static int               _evas_common_soft16_image_surface_alloc(Image_Entry *ie, unsigned int w, unsigned int h);
static void              _evas_common_soft16_image_surface_delete(Image_Entry *ie);
static DATA32		*_evas_common_soft16_image_surface_pixels(Image_Entry *ie);

static int               _evas_common_load_soft16_image_from_file(Image_Entry *ie);
static void              _evas_common_soft16_image_unload(Image_Entry *ie);

static void              _evas_common_soft16_image_dirty_region(Image_Entry *im, unsigned int x, unsigned int y, unsigned int w, unsigned int h);
static int               _evas_common_soft16_image_dirty(Image_Entry *ie_dst, const Image_Entry *ie_src);

static int               _evas_common_soft16_image_ram_usage(Image_Entry *ie);

static int               _evas_common_soft16_image_size_set(Image_Entry *ie_dst, const Image_Entry *ie_im, unsigned int w, unsigned int h);
static int               _evas_common_soft16_image_from_copied_data(Image_Entry* ie_dst, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace);
static int               _evas_common_soft16_image_from_data(Image_Entry* ie_dst, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace);
static int               _evas_common_soft16_image_colorspace_set(Image_Entry* ie_dst, int cspace);

static int               _evas_common_load_soft16_image_data_from_file(Image_Entry *ie);

/* static void */
/* _evas_common_soft16_image_debug(const char* context, Image_Entry *eim) */
/* { */
/*    DBG("[16] %p = [%s] {%s,%s} %i [%i|%i]", eim, context, eim->file, eim->key, eim->references, eim->w, eim->h); */
/* } */

static const Evas_Cache_Image_Func      _evas_common_soft16_image_func =
{
   _evas_common_soft16_image_new,
   _evas_common_soft16_image_delete,
   _evas_common_soft16_image_surface_alloc,
   _evas_common_soft16_image_surface_delete,
   _evas_common_soft16_image_surface_pixels,
   _evas_common_load_soft16_image_from_file,
   _evas_common_soft16_image_unload,
   _evas_common_soft16_image_dirty_region,
   _evas_common_soft16_image_dirty,
   _evas_common_soft16_image_size_set,
   _evas_common_soft16_image_from_copied_data,
   _evas_common_soft16_image_from_data,
   _evas_common_soft16_image_colorspace_set,
   _evas_common_load_soft16_image_data_from_file,
   _evas_common_soft16_image_ram_usage,
/*    _evas_common_soft16_image_debug */
   NULL
};

EAPI void
evas_common_soft16_image_init(void)
{
   if (!eci)
     eci = evas_cache_image_init(&_evas_common_soft16_image_func);
   reference++;
}

EAPI void
evas_common_soft16_image_shutdown(void)
{
   if (--reference == 0)
     {
// DISABLE for now - something wrong with cache shutdown freeing things
// still in use - rage_thumb segv's now.
// 
// actually - i think i see it. cache ref goes to 0 (and thus gets freed)
// because in eng_setup() when a buffer changes size it is FIRST freed
// THEN allocated again - thus brignhjing ref to 0 then back to 1 immediately
// where it should stay at 1. - see evas_engine.c in the buffer enigne for
// example. eng_output_free() is called BEFORE _output_setup(). although this
// is only a SIGNE of the problem. we can patch this up with either freeing
// after the setup (so we just pt a ref of 2 then back to 1), or just 
// evas_common_image_init() at the start and evas_common_image_shutdown()
// after it all. really ref 0 should only be reached when no more canvases
// with no more objects exist anywhere.

// ENABLE IT AGAIN, hope it is fixed. Gustavo @ January 22nd, 2009.
        evas_cache_image_shutdown(eci);
        eci = NULL;
     }
}

EAPI Evas_Cache_Image *
evas_common_soft16_image_cache_get(void)
{
   return eci;
}

static Image_Entry *
_evas_common_soft16_image_new(void)
{
   Soft16_Image *im;

   im = calloc(1, sizeof(Soft16_Image));
   if (!im) return NULL;

   im->stride = -1;

   return (Image_Entry *) im;
}

static void
_evas_common_soft16_image_delete(Image_Entry *ie)
{
   memset(ie, 0xFF, sizeof (Soft16_Image));
   free(ie);
}

static int
_evas_common_soft16_image_surface_alloc(Image_Entry *ie, unsigned int w, unsigned int h)
{
   Soft16_Image *im = (Soft16_Image *) ie;

   if (im->stride < 0) im->stride = _calc_stride(w);

   im->pixels = realloc(im->pixels, IMG_BYTE_SIZE(im->stride, h, ie->flags.alpha));
   if (!im->pixels) return -1;

   if (ie->flags.alpha)
     {
        im->alpha = (DATA8 *)(im->pixels + (im->stride * h));
        im->flags.free_alpha = 0;
     }
   im->flags.free_pixels = 1;

   return 0;
}

static void
_evas_common_soft16_image_surface_delete(Image_Entry *ie)
{
   Soft16_Image *im = (Soft16_Image *) ie;

   if (im->flags.free_pixels)
     free(im->pixels);
   im->pixels = NULL;
   im->flags.free_pixels = 0;

   if (im->flags.free_alpha)
     free(im->alpha);
   im->alpha = NULL;
   im->flags.free_alpha = 0;
}

static DATA32 *
_evas_common_soft16_image_surface_pixels(Image_Entry *ie __UNUSED__)
{
   abort();

   return NULL;
}

static int
_evas_common_load_soft16_image_from_file(Image_Entry *ie)
{
   Soft16_Image *sim = (Soft16_Image *) ie;
   RGBA_Image   *im;
   int           error = 0;

   im = (RGBA_Image *) evas_cache_image_request(evas_common_image_cache_get(), sim->cache_entry.file, sim->cache_entry.key, &sim->cache_entry.load_opts, &error);
   sim->source = im;
   if (!sim->source) return -1;

   sim->cache_entry.w = sim->source->cache_entry.w;
   sim->cache_entry.h = sim->source->cache_entry.h;
   ie->flags.alpha = im->cache_entry.flags.alpha;
   sim->cache_entry.info = im->cache_entry.info;
   if (sim->stride < 0) sim->stride = _calc_stride(sim->cache_entry.w);

   return 0;
}

static void
_evas_common_soft16_image_unload(Image_Entry *ie __UNUSED__)
{
}

static void
_evas_common_soft16_image_dirty_region(Image_Entry *im __UNUSED__, unsigned int x __UNUSED__, unsigned int y __UNUSED__, unsigned int w __UNUSED__, unsigned int h __UNUSED__)
{
}

static int
_evas_common_soft16_image_dirty(Image_Entry *ie_dst, const Image_Entry *ie_src)
{
   Soft16_Image *dst = (Soft16_Image *) ie_dst;
   Soft16_Image *src = (Soft16_Image *) ie_src;

   evas_cache_image_load_data(&src->cache_entry);
   evas_cache_image_surface_alloc(&dst->cache_entry,
                                  src->cache_entry.w, src->cache_entry.h);

/*    evas_common_blit_rectangle(src, dst, 0, 0, src->cache_entry.w, src->cache_entry.h, 0, 0); */

   return 0;
}

static int
_evas_common_soft16_image_ram_usage(Image_Entry *ie)
{
   Soft16_Image *im = (Soft16_Image *) ie;

   if (im->pixels && im->flags.free_pixels)
     return IMG_BYTE_SIZE(im->stride, im->cache_entry.h, ie->flags.alpha);
   return 0;
}

static int
_evas_common_soft16_image_size_set(Image_Entry *ie_dst, const Image_Entry *ie_im, unsigned int w __UNUSED__, unsigned int h __UNUSED__)
{
   Soft16_Image *dst = (Soft16_Image *) ie_dst;
   Soft16_Image *im = (Soft16_Image *) ie_im;

   dst->flags = im->flags;

   return 0;
}

static int
_evas_common_soft16_image_from_data(Image_Entry* ie_dst, unsigned int w, unsigned int h, DATA32 *image_data, int alpha, int cspace __UNUSED__)
{
   Soft16_Image *im = (Soft16_Image *) ie_dst;

   /* FIXME: handle colorspace */
   ie_dst->w = w;
   ie_dst->h = h;
   ie_dst->flags.alpha = alpha;

   im->flags.free_pixels = 0;
   im->flags.free_alpha = 0;
   if (im->stride < 0)
        im->stride = _calc_stride(w);

   /* FIXME: That's bad, the application must be aware of the engine internal. */
   im->pixels = (DATA16 *) image_data;
   if (ie_dst->flags.alpha)
     im->alpha = (DATA8 *)(im->pixels + (im->stride * h));

   return 0;
}

static int
_evas_common_soft16_image_from_copied_data(Image_Entry* ie_dst, unsigned int w __UNUSED__, unsigned int h, DATA32 *image_data, int alpha __UNUSED__, int cspace __UNUSED__)
{
   Soft16_Image *im = (Soft16_Image *) ie_dst;

   /* FIXME: handle colorspace */
   if (image_data)
     memcpy(im->pixels, image_data, IMG_BYTE_SIZE(im->stride, h, ie_dst->flags.alpha));
   else
     memset(im->pixels, 0, IMG_BYTE_SIZE(im->stride, h, ie_dst->flags.alpha));

   return 0;
}

static int
_evas_common_soft16_image_colorspace_set(Image_Entry* ie_dst __UNUSED__, int cspace __UNUSED__)
{
   /* FIXME: handle colorspace */
   return 0;
}

static int
_evas_common_load_soft16_image_data_from_file(Image_Entry *ie)
{
   Soft16_Image *im = (Soft16_Image *) ie;

   if (im->pixels) return 0;
   if (!im->source) return -1;

   evas_cache_image_load_data(&im->source->cache_entry);
   if (im->source->image.data)
     {
        DATA32 *sp;

        evas_cache_image_surface_alloc(&im->cache_entry,
                                       im->source->cache_entry.w,
                                       im->source->cache_entry.h);

        sp = im->source->image.data;
        if (im->alpha)
          evas_common_soft16_image_convert_from_rgba(im, sp);
        else
          evas_common_soft16_image_convert_from_rgb(im, sp);
     }
   evas_cache_image_drop(&im->source->cache_entry);
   im->cache_entry.info.module = NULL;
   im->cache_entry.info.loader = NULL;
   im->source = NULL;

   return 0;
}

/* Soft16_Image * */
/* evas_common_soft16_image_new(int w, int h, int stride, int have_alpha, DATA16 *pixels, */
/* 		 int copy) */
/* { */
/*    Soft16_Image *im; */

/*    if (stride < 0) stride = _calc_stride(w); */

/*    im = evas_common_soft16_image_alloc(w, h, stride, have_alpha, copy); */
/*    if (!im) return NULL; */

/*    if (pixels) */
/*      { */
/* 	if (copy) */
/* 	  memcpy(im->pixels, pixels, IMG_BYTE_SIZE(stride, h, have_alpha)); */
/* 	else */
/* 	  { */
/* 	     im->pixels = pixels; */
/* 	     if (have_alpha) im->alpha = (DATA8 *)(im->pixels + (stride * h)); */
/* 	  } */
/*      } */
/*    return im; */
/* } */

static inline void
_get_clip(const RGBA_Draw_Context *dc, const Soft16_Image *im,
	  Eina_Rectangle *clip)
{
   if (dc->clip.use)
     {
	EINA_RECTANGLE_SET(clip, dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h);
	if (clip->x < 0)
	  {
	     clip->w += clip->x;
	     clip->x = 0;
	  }
	if (clip->y < 0)
	  {
	     clip->h += clip->y;
	     clip->y = 0;
	  }
	if ((clip->x + clip->w) > (int)im->cache_entry.w) clip->w = im->cache_entry.w - clip->x;
	if ((clip->y + clip->h) > (int)im->cache_entry.h) clip->h = im->cache_entry.h - clip->y;
     }
   else
     {
	EINA_RECTANGLE_SET(clip, 0, 0, im->cache_entry.w, im->cache_entry.h);
     }
}

static inline int
_is_empty_rectangle(const Eina_Rectangle *r)
{
   return (r->w < 1) || (r->h < 1);
}

static inline void
_shrink(int *s_pos, int *s_size, int pos, int size)
{
   int d;

   d = (*s_pos) - pos;
   if (d < 0)
     {
	(*s_size) += d;
	(*s_pos) = pos;
     }

   d = size + pos - (*s_pos);
   if ((*s_size) > d)
     (*s_size) = d;
}

static int
_soft16_adjust_areas(Eina_Rectangle *src,
		     int src_max_x, int src_max_y,
		     Eina_Rectangle *dst,
		     int dst_max_x, int dst_max_y,
		     Eina_Rectangle *dst_clip)
{
   if (_is_empty_rectangle(src) ||
       _is_empty_rectangle(dst) ||
       _is_empty_rectangle(dst_clip))
     return 0;

   /* shrink clip */
   _shrink(&dst_clip->x, &dst_clip->w, dst->x, dst->w);
   _shrink(&dst_clip->y, &dst_clip->h, dst->y, dst->h);
   if (_is_empty_rectangle(dst_clip)) return 0;

   /* sanitise x */
   if (src->x < 0)
     {
	dst->x -= (src->x * dst->w) / src->w;
	dst->w += (src->x * dst->w) / src->w;
	src->w += src->x;
	src->x = 0;
     }
   if (src->x >= src_max_x) return 0;
   if ((src->x + src->w) > src_max_x)
     {
	dst->w = (dst->w * (src_max_x - src->x)) / (src->w);
	src->w = src_max_x - src->x;
     }
   if (dst->w <= 0) return 0;
   if (src->w <= 0) return 0;
   if (dst_clip->x < 0)
     {
	dst_clip->w += dst_clip->x;
	dst_clip->x = 0;
     }
   if (dst_clip->w <= 0) return 0;
   if (dst_clip->x >= dst_max_x) return 0;

   _shrink(&dst_clip->x, &dst_clip->w, 0, dst_max_x);
   if (dst_clip->w <= 0) return 0;

   /* sanitise y */
   if (src->y < 0)
     {
	dst->y -= (src->y * dst->h) / src->h;
	dst->h += (src->y * dst->h) / src->h;
	src->h += src->y;
	src->y = 0;
     }
   if (src->y >= src_max_y) return 0;
   if ((src->y + src->h) > src_max_y)
     {
	dst->h = (dst->h * (src_max_y - src->y)) / (src->h);
	src->h = src_max_y - src->y;
     }
   if (dst->h <= 0) return 0;
   if (src->h <= 0) return 0;
   if (dst_clip->y < 0)
     {
	dst_clip->h += dst_clip->y;
	dst_clip->y = 0;
     }
   if (dst_clip->h <= 0) return 0;
   if (dst_clip->y >= dst_max_y) return 0;

   _shrink(&dst_clip->y, &dst_clip->h, 0, dst_max_y);
   if (dst_clip->h <= 0) return 0;

   return 1;
}

static void
_soft16_image_draw_sampled_int(Soft16_Image *src, Soft16_Image *dst,
			       RGBA_Draw_Context *dc,
			       Eina_Rectangle sr, Eina_Rectangle dr)
{
   Eina_Rectangle cr;

   if (!(RECTS_INTERSECT(dr.x, dr.y, dr.w, dr.h, 0, 0, dst->cache_entry.w, dst->cache_entry.h)))
     return;
   if (!(RECTS_INTERSECT(sr.x, sr.y, sr.w, sr.h, 0, 0, src->cache_entry.w, src->cache_entry.h)))
     return;

   _get_clip(dc, dst, &cr);
   if (!_soft16_adjust_areas(&sr, src->cache_entry.w, src->cache_entry.h, &dr, dst->cache_entry.w, dst->cache_entry.h, &cr))
     return;

   if ((dr.w == sr.w) && (dr.h == sr.h))
     evas_common_soft16_image_draw_unscaled(src, dst, dc, sr, dr, cr);
   else
     evas_common_soft16_image_draw_scaled_sampled(src, dst, dc, sr, dr, cr);
}

EAPI void
evas_common_soft16_image_draw(Soft16_Image *src, Soft16_Image *dst,
		  RGBA_Draw_Context *dc,
		  int src_region_x, int src_region_y,
		  int src_region_w, int src_region_h,
		  int dst_region_x, int dst_region_y,
		  int dst_region_w, int dst_region_h,
		  int smooth __UNUSED__)
{
   Eina_Rectangle sr, dr;
   Cutout_Rects *rects;
   Cutout_Rect  *r;
   struct RGBA_Draw_Context_clip clip_bkp;
   int i;

   /* handle cutouts here! */
   EINA_RECTANGLE_SET(&dr, dst_region_x, dst_region_y, dst_region_w, dst_region_h);

   if (_is_empty_rectangle(&dr)) return;
   if (!(RECTS_INTERSECT(dr.x, dr.y, dr.w, dr.h, 0, 0, dst->cache_entry.w, dst->cache_entry.h)))
     return;

   EINA_RECTANGLE_SET(&sr, src_region_x, src_region_y, src_region_w, src_region_h);

   if (_is_empty_rectangle(&sr)) return;
   if (!(RECTS_INTERSECT(sr.x, sr.y, sr.w, sr.h, 0, 0, src->cache_entry.w, src->cache_entry.h)))
     return;

   /* no cutouts - cut right to the chase */
   if (!dc->cutout.rects)
     {
	_soft16_image_draw_sampled_int(src, dst, dc, sr, dr);
	return;
     }

   /* save out clip info */
   clip_bkp = dc->clip;
   evas_common_draw_context_clip_clip(dc, 0, 0, dst->cache_entry.w, dst->cache_entry.h);
   evas_common_draw_context_clip_clip(dc, dst_region_x, dst_region_y, dst_region_w, dst_region_h);
   /* our clip is 0 size.. abort */
   if ((dc->clip.w <= 0) || (dc->clip.h <= 0))
     {
	dc->clip = clip_bkp;
	return;
     }
   rects = evas_common_draw_context_apply_cutouts(dc);
   for (i = 0; i < rects->active; i++)
     {
	r = rects->rects + i;
	evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h);
	_soft16_image_draw_sampled_int(src, dst, dc, sr, dr);
     }
   evas_common_draw_context_apply_clear_cutouts(rects);
   dc->clip = clip_bkp;
}

EAPI Soft16_Image *
evas_common_soft16_image_alpha_set(Soft16_Image *im, int have_alpha)
{
   Soft16_Image   *new_im;

   if (im->cache_entry.flags.alpha == have_alpha) return im;

   new_im = (Soft16_Image *) evas_cache_image_alone(&im->cache_entry);

   new_im->cache_entry.flags.alpha = have_alpha;

   if (im->cache_entry.w > 0
       && im->cache_entry.h)
     new_im = (Soft16_Image *) evas_cache_image_size_set(&new_im->cache_entry, im->cache_entry.w, im->cache_entry.h);

   return new_im;
}

/* Soft16_Image * */
/* evas_common_soft16_image_size_set(Soft16_Image *old_im, int w, int h) */
/* { */
/*    Soft16_Image *new_im; */
/*    DATA16 *dp, *sp; */
/*    int i, cw, ch, ew; */

/*    if ((old_im->cache_entry.w == w) && (old_im->cache_entry.h == h)) return old_im; */

/*    new_im = evas_common_soft16_image_new(w, h, -1, old_im->flags.have_alpha, NULL, 1); */

/*    if (old_im->cache_entry.w < new_im->cache_entry.w) */
/*      cw = old_im->cache_entry.w; */
/*    else */
/*      cw = new_im->cache_entry.w; */

/*    ew = new_im->cache_entry.w - cw; */

/*    if (old_im->cache_entry.h < new_im->cache_entry.h) */
/*      ch = old_im->cache_entry.h; */
/*    else */
/*      ch = new_im->cache_entry.h; */

/*    dp = new_im->pixels; */
/*    sp = old_im->pixels; */
/*    for (i = 0; i < ch; i++) */
/*      { */
/* 	memcpy(dp, sp, cw * sizeof(DATA16)); */
/* 	if (ew > 0) memset(dp, 0, ew * sizeof(DATA16)); */

/* 	dp += new_im->stride; */
/* 	sp += old_im->stride; */
/*      } */

/*    if (old_im->flags.have_alpha) */
/*      { */
/* 	DATA8 *dp, *sp; */

/* 	dp = new_im->alpha; */
/* 	sp = old_im->alpha; */
/* 	for (i = 0; i < ch; i++) */
/* 	  { */
/* 	     memcpy(dp, sp, cw * sizeof(DATA8)); */
/* 	     if (ew > 0) memset(dp, 0, ew * sizeof(DATA8)); */

/* 	     dp += new_im->stride; */
/* 	     sp += old_im->stride; */
/* 	  } */
/*      } */

/*    evas_cache_image_drop(&old_im->cache_entry); */
/*    return new_im; */
/* } */
