#ifndef _plan9_
#include <malloc.h>
#include <string.h>
#include <math.h>
#include <config.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <stdlib.h>
#else
#include <u.h>
#include <libc.h>
#include <stdio.h>
#endif
#include <filter.h>
#include <fractal.h>
#include <xthread.h>
#define SIZE 64
#define SIZE2 8
struct blurdata
  {
    int bckuptime;
    int counter;
    struct palette *savedpalette, *palette;
    unsigned char (*table)[256];	/*Used by blur routines */
    int n;
  };
static int
requirement (struct filter *f, struct requirements *r)
{
  f->req = *r;
  r->nimages = 1;
  r->flags |= IMAGEDATA;
  r->supportedmask = C256 | TRUECOLOR24 | TRUECOLORS | HICOLOR | REALCOLOR | GRAYSCALE;
  return (f->next->action->requirement (f->next, r));
}
static void
blur8 (struct filter *f)
{
  struct image *desti = f->image;
  struct blurdata *s = f->data;
  unsigned char (*table)[256]=s->table;
  int i, i1, im, im1, ipl, ii;
  if (f->image->palette->type == C256)
    for (i = im = 0; i < SIZE; i++, im += 256 - s->n)
      {
	for (i1 = im1 = 0; i1 < SIZE2; i1++, im1 += s->n * (SIZE / SIZE2))
	  {
	    ipl = (im + im1 + 128) >> 8;
	    ii = i1 * (SIZE / SIZE2);
	    if (ipl == i && i != ii)
	      {
		if (i < ii)
		  ipl++;
		else
		  ipl--;
	      }
	    ii = desti->palette->pixels[i];
	    table[i1][ii] = desti->palette->pixels[ipl];
	  }
      }
  else
    for (i = im = desti->palette->start, im *= 256 - s->n; i < desti->palette->end; i++, im += 256 - s->n)
      {
	for (i1 = im1 = desti->palette->start, im1 *= s->n; i1 < desti->palette->end; i1++, im1 += s->n)
	  {
	    ipl = (im + im1 + 128) >> 8;
	    if (ipl == i && i != i1)
	      {
		if (i < i1)
		  ipl++;
		else
		  ipl--;
	      }
	    table[i1][i] = ipl;
	  }
      }
}
static void
blurtruecolor (struct filter *f)
{
  struct blurdata *s = f->data;
  int i, i1, im, im1;
  unsigned char (*table)[256]=s->table;
  for (i = im = 0; i < 256; i++, im += 256 - s->n)
    {
      for (i1 = im1 = 0; i1 < 256; i1++, im1 += s->n)
	table[i1][i] = (im + im1) >> 8;
    }
}
static int
initialize (struct filter *f, struct initdata *i)
{
  struct blurdata *s = f->data;
  int x;
  inhermisc (f, i);
  s->counter = 0;
  s->palette->size = SIZE2;
  for (x = 0; x < SIZE2; x++)
    s->palette->pixels[x] = x;
  if (datalost (f, i) || i->image->version != f->imageversion)
    {
      if (i->image->palette->type == C256)
	{
	  if (s->savedpalette == NULL)
	    s->savedpalette = clonepalette (i->image->palette);
	  mkblurpalette (i->image->palette);
	}
      else
	{
	  if (s->savedpalette != NULL)
	    {
	      restorepalette (i->image->palette, s->savedpalette);
	      destroypalette (s->savedpalette);
	      s->savedpalette = NULL;
	    }
	}
    }
  if (!inherimage (f, i, TOUCHIMAGE | IMAGEDATA, 0, 0, i->image->palette->type == C256 ? s->palette : NULL, 0, 0))
    return 0;
  if (f->image->palette->type == C256)
    {
      setfractalpalette (f, s->savedpalette);
    }
  if (i->image == NULL)
    {
      return 0;
    }
  clear_image (f->image);
  return (f->previous->action->initialize (f->previous, i));
}
static struct filter *
getinstance (struct filteraction *a)
{
  struct filter *f = createfilter (a);
  struct blurdata *i = calloc (sizeof (*i), 1);
  i->savedpalette = NULL;
  i->palette = createpalette (0, 256, SMALLITER, 0, 256, NULL, NULL, NULL, NULL);
  i->palette->size = SIZE2;
  i->palette->end = SIZE2;
  i->table=NULL;
  f->childimage = NULL;
  f->data = i;
  f->name = "Motionblur";
  return (f);
}
static void
destroyinstance (struct filter *f)
{
  struct blurdata *i = f->data;
  if (i->table!=NULL) free(i->table);
  if (i->savedpalette != NULL)
    destroypalette (i->savedpalette);
  destroypalette (i->palette);
  destroyinheredimage (f);
  free (f->data);
  free (f);
}

/* An part of blur function that should be done paraely */
static void
blur82 (void *data, struct taskinfo *task, int r1, int r2)
{
  struct filter *f = data;
  struct image *srci = f->childimage, *desti = f->image;
  struct blurdata *s = f->data;
  unsigned char (*table)[256];
  int i, im;
  unsigned char *src, *dest, *srcend;

  im = srci->width;
  table = s->table;
  for (i = r1; i < r2; i++)
    {
      src = srci->currlines[i];
      srcend = src + im;
      dest = desti->currlines[i];
      for (; src < srcend; src++, dest++)
	{
	  dest[0] = table[src[0]][dest[0]];
	}
    }
}

#ifdef SHICOLOR
static void
blurhi (void *data, struct taskinfo *task, int r1, int r2)
{
  struct filter *f = data;
  struct image *srci = f->childimage, *desti = f->image;
  struct blurdata *s = f->data;
  unsigned int n = s->n;
  int i;
  pixel16_t *src, *dest, *srcend;
  for (i = r1; i < r2; i++)
    {
      src = (pixel16_t *) srci->currlines[i];
      srcend = src + srci->width;
      dest = (pixel16_t *) desti->currlines[i];
      for (; src < srcend; src++, dest++)
	{
	  *dest = interhic (*src, *dest, n);
	}
    }
}
#endif
#ifdef SREALCOLOR
static void
blurreal (void *data, struct taskinfo *task, int r1, int r2)
{
  struct filter *f = data;
  struct image *srci = f->childimage, *desti = f->image;
  struct blurdata *s = f->data;
  unsigned int n = s->n;
  int i;
  pixel16_t *src, *dest, *srcend;
  for (i = r1; i < r2; i++)
    {
      src = (pixel16_t *) srci->currlines[i];
      srcend = src + srci->width;
      dest = (pixel16_t *) desti->currlines[i];
      for (; src < srcend; src++, dest++)
	{
	  *dest = interrealc (*src, *dest, n);
	}
    }
}
#endif
#ifdef STRUECOLOR24
static void
blur24 (void *data, struct taskinfo *task, int r1, int r2)
{
  struct filter *f = data;
  struct image *srci = f->childimage, *desti = f->image;
  struct blurdata *s = f->data;
  unsigned char (*table)[256] = s->table;
  unsigned char *src, *dest, *srcend;
  int i, im;
  im = srci->width * 3;
  for (i = r1; i < r2; i++)
    {
      src = srci->currlines[i];
      srcend = src + im;
      dest = desti->currlines[i];
      for (; src < srcend; src += 3, dest += 3)
	{
	  dest[0] = table[src[0]][dest[0]];
	  dest[1] = table[src[1]][dest[1]];
	  dest[2] = table[src[2]][dest[2]];
	}
    }
}
#endif
static void
blur32 (void *data, struct taskinfo *task, int r1, int r2)
{
  struct filter *f = data;
  struct image *srci = f->childimage, *desti = f->image;
  struct blurdata *s = f->data;
  unsigned char (*table)[256] = s->table;
  unsigned char *src, *dest, *srcend;
  char ledata[4];
  int i, im;
  im = srci->width * 4;
  switch(f->image->palette->type)
  {
    case TRUECOLOR:
      *(int *)ledata=0x00ffffff;
      break;
    case TRUECOLORMI:
      *(int *)ledata=0xffffff00;
      break;
  }
  for (i = r1; i < r2; i++)
    {
      src = srci->currlines[i];
      srcend = src + im;
      dest = desti->currlines[i];
      if(!ledata[3])
      for (; src < srcend; src += 4, dest += 4)
	{
	  dest[0] = table[src[0]][dest[0]];
	  dest[1] = table[src[1]][dest[1]];
	  dest[2] = table[src[2]][dest[2]];
	}
      else
      for (; src < srcend; src += 4, dest += 4)
	{
	  dest[1] = table[src[1]][dest[1]];
	  dest[2] = table[src[2]][dest[2]];
	  dest[3] = table[src[3]][dest[3]];
	}
    }
}
#define AMOUNT 0.005
#define DIV 1000.0
#define MAX 800*1000		/*after 800 frames should be OK */
static int
doit (struct filter *f, int flags, int time1)
{
  int val, n;
  int time = time1;
  struct blurdata *s = f->data;
  updateinheredimage (f);
  val = f->previous->action->doit (f->previous, flags, time);
  s->counter += time;
  if (val & CHANGED)
    s->counter = 0;
  n = (1 - pow (1.0 - AMOUNT, (time + s->bckuptime) / DIV)) * 256;
  if (s->counter >= 2 * MAX)
    {
      return val;
    }
  if (n < 10)
    {
      s->bckuptime += time;
      return val | ANIMATION;
    }
  s->bckuptime = 0;
  if (s->counter >= MAX)
    n = 256, s->counter = 2 * MAX;
  if(s->n!=n) {
  s->n = n;
  switch(f->image->bytesperpixel)
  {
    case 1:
      if(s->table==NULL) s->table=malloc(256*256);
      blur8(f);
      break;
    case 3:
    case 4:
      if(s->table==NULL) s->table=malloc(256*256);
      blurtruecolor(f);
      break;
    default:
      if(s->table!=NULL) free(s->table);
  }
  }
  switch (f->image->palette->type)
    {
    case C256:
    case GRAYSCALE:
      xth_function (blur82, f, f->image->height);
      break;
#ifdef SHICOLOR
    case HICOLOR:
      xth_function (blurhi, f, f->image->height);
      break;
#endif
#ifdef SREALCOLOR
    case REALCOLOR:
      xth_function (blurreal, f, f->image->height);
      break;
#endif
    case TRUECOLOR:
    case TRUECOLORMI:
      xth_function (blur32, f, f->image->height);
      break;
#ifdef STRUECOLOR24
    case TRUECOLOR24:
      xth_function (blur24, f, f->image->height);
      break;
#endif
    }
  xth_sync ();
  if (s->counter == 2 * MAX)
    {
      return val | CHANGED;
    }
  return val | CHANGED | ANIMATION;
}
static void
myremovefilter (struct filter *f)
{
  struct blurdata *s = f->data;
  if (s->savedpalette != NULL)
    {
      restorepalette (f->image->palette, s->savedpalette);
      destroypalette (s->savedpalette);
      s->savedpalette = NULL;
    }
}

struct filteraction blur_filter =
{
  "Motionblur",
  "blur",
  0,
  getinstance,
  destroyinstance,
  doit,
  requirement,
  initialize,
  convertupgeneric,
  convertdowngeneric,
  myremovefilter
};
