/* 
Idea taken from EffecTV http://effectv.sourceforge.net/
Written by Kent Mein mein@cs.umn.edu
http://www.cs.umn.edu/~mein/blender
Date: Tue Apr 23 18:35:36 CDT 2002
*/

#include "plugin.h"
#define DEPTH 4

/* ******************** GLOBAL VARIABLES ***************** */
char name[24]= "Invisible";	

/* structure for buttons, 
 *  butcode      name           default  min  max  0
 */

VarStruct varstr[]= {
   { LABEL,	"In: 1 strip", 0.0, 0.0, 0.0, ""}, 
   { NUM|INT,	"Edge Radius",	    7.0,	1.0,	2000.0, "EdgeZone"}, 
   { NUM|FLO,	"Tolerance", 10.0,	0.0,	255.0, ""}, 
   { NUM|INT,	"Chance Blur", 75,	0.0,	100.0, ""}, 
   { NUM|FLO,	"Seed", 0.1,	0.0,	1.0, ""}, 
};

/* The cast struct is for input in the main doit function
   Varstr and Cast must have the same variables in the same order */ 

typedef struct Cast {
	int dummy;			/* because of the 'label' button */
	int radius;
	float tol; 
        int chance;
        float seed;
} Cast;


/* cfra: the current frame */

float cfra;

void plugin_seq_doit(Cast *, float, float, int, int, 
   ImBuf *, ImBuf *, ImBuf *, ImBuf *);

/* ******************** Fixed functions ***************** */

int plugin_seq_getversion(void) {
	return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but) {
}

void plugin_init() {
}

void plugin_getinfo(PluginInfo *info) {
	info->name= name;
	info->nvars= sizeof(varstr)/sizeof(VarStruct);
	info->cfra= &cfra;

	info->varstr= varstr;

	info->init= plugin_init;
	info->seq_doit= (SeqDoit) plugin_seq_doit;
	info->callback= plugin_but_changed;
}

int inrange(int x, int y, char *in, char *in2, Cast *cast,int xo) {
   int pos, total;

   pos = (y * xo + x) * DEPTH;
   total  = ABS(in[pos] - in2[pos]) + ABS(in[pos+1] - in2[pos+1]) +
      ABS(in[pos+2] - in2[pos+2]);
   if (total > (cast->tol)) return 1; 
   return 0;
}

/* returns a 1 if this is in the change area and close to and edge. */
int edge(int x, int y, char *in, char *in2, Cast *cast, int yo, int xo) {
   int r,g,minx,miny,maxx,maxy;

   maxx=x+cast->radius;
   if (maxx > xo) maxx = xo;
   maxy=y+cast->radius;
   if (maxy > yo) maxy = yo;
   minx=x-cast->radius;
   if (minx < 0) maxx = 0;
   miny=y-cast->radius;
   if (miny < 0) maxy = 0;

   if (inrange(x,y,in,in2,cast,xo)) {
      for (r=minx;r<maxx;r++) {
        for (g=miny;g<maxy;g++) {
           if (!inrange(r,g,in,in2,cast,xo)) return 1;
        }
      }
   }
   return 0;
}

/* does a little bluring of the pixels around it and returns a value*/
int get_color(int cindex, int x, int y, char *image, int xo, int yo,
   Cast *cast) {
   int r,g,minx,maxx,miny,maxy,tot,count;

   maxx=x+cast->radius;
   if (maxx > xo) maxx = xo;
   maxy=y+cast->radius;
   if (maxy > yo) maxy = yo;
   minx=x-cast->radius;
   if (minx < 0) maxx = 0;
   miny=y-cast->radius;
   if (miny < 0) maxy = 0;
   
   tot=0;
   count=0;
   for (r=minx;r<maxx;r++) {
      for (g=miny;g<maxy;g++) {
         tot += image[(g * xo + r) * DEPTH + cindex];
         count++;
      }
   }
   return FLOOR(tot/count);
}

void plugin_seq_doit(Cast *cast, float facf0, float facf1, int xo, int yo, 
	ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *outbuf, ImBuf *use) {
   int x, y, t, r;
   char *out=(char *)outbuf->rect;
   char *in=(char *)ibuf1->rect;
   char *in2=(char *)ibuf2->rect;

   srand(cast->seed);

   /* Step through the cubes */
   for(y=0;y<yo;y=y++) {
      for(x=0;x<xo;x=x++) {
          t=(y*xo + x) * DEPTH;
          if (edge(x,y,in,in2,cast,yo,xo) && ((rand()%100) < cast->chance)) {
             for(r=0;r<DEPTH;r++) out[t + r] = get_color(r,x,y,in,xo,yo,cast);
          } else {
             for(r=0;r<DEPTH;r++) out[t + r] = in[t + r];
          }
      }
   }
}
