/*
 * Dynamic Noise Reduction (based on the VirtualDub filter by Steven Don)
 *
 * Copyright (c) 2005 Peter Schlaile 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "math.h"
#include "plugin.h"
#include <stdio.h>

char name[]= "Dynamic Noise Reduction";

VarStruct varstr[]= {
	{ NUMSLI|INT, "Level:", 10.0,	0.0,	15.0, "Level"}, 
};

typedef struct Cast {
	int level;
} Cast;

float cfra;
void * plugin_private_data;

struct my_data {
	unsigned char lookup_table[65536];
	int last_level;
	float last_cfra;
	int last_width;
	int last_height;
	unsigned char * last_frame;
};

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

int plugin_seq_getversion(void) { return B_PLUGIN_VERSION;}

static void precalculate(unsigned char * table, int level)
{
	int ap_, bp;

	for (ap_ = 0; ap_ < 256; ap_++) {
		for (bp = 0; bp < 256; bp++) {
			int ap = ap_;
			int diff = ap - bp;
			if (diff < 0) {
				diff = -diff;
			}
			if (diff < level) {
				if (diff > (level >> 1)) {
					ap = (ap + ap + bp)/3;
				} else {
					ap = bp;
				}
			}
		
			*table++ = ap;
                }
	}
}

void plugin_but_changed(int but) { }
void plugin_init() { }

void * plugin_seq_alloc_private_data()
{
	struct my_data * result = (struct my_data*) calloc(
		sizeof(struct my_data), 1);
	result->last_cfra = -1;
	return result;
}

void plugin_seq_free_private_data(void * data)
{
	struct my_data * d = (struct my_data*) data;
	if (d->last_frame) {
		free(d->last_frame);
	}
	free(d);
}

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;
}

static void doit(unsigned char * src_, unsigned char * dst_,
		 unsigned char * table, int width, int height)
{
	int count = width * height;
	unsigned char * src = src_;
	unsigned char * dst = dst_;

	while (count--) {
		*dst = table[(*src++ << 8) | *dst]; dst++;
		*dst = table[(*src++ << 8) | *dst]; dst++;
		*dst = table[(*src++ << 8) | *dst]; dst++;
		*dst++ = *src++;
	}

	memcpy(src_, dst_, width * height * 4);
}

void plugin_seq_doit(Cast *cast, float facf0, float facf1, int width, 
	int height, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *out, ImBuf *use) {

	struct my_data * d = (struct my_data*) plugin_private_data;

	if (!ibuf1) return;

	if (cast->level != d->last_level) {
		precalculate(d->lookup_table, cast->level);
		d->last_level = cast->level;
	}

	if (width != d->last_width || height != d->last_height
	    || cfra != d->last_cfra + 1) {
		free(d->last_frame);
		d->last_frame = (unsigned char*) calloc(width * height, 4);
		
		d->last_width = width;
		d->last_height = height;
	}

	memcpy(out->rect, ibuf1->rect, width * height * 4);

	doit((unsigned char*) out->rect, 
	     d->last_frame, d->lookup_table, width, height);
	
	d->last_cfra = cfra;
}
