
/*
 * CINELERRA
 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "clip.h"
#include "bchash.h"
#include "denoisemjpeg.h"
#include "filexml.h"
#include "guicast.h"
#include "keyframe.h"
#include "language.h"
#include "vframe.h"


#include <stdint.h>
#include <string.h>



REGISTER_PLUGIN(DenoiseMJPEG)



DenoiseMJPEGConfig::DenoiseMJPEGConfig()
{
	radius = 8;
	threshold = 5;
	threshold2 = 4;
	sharpness = 125;
	lcontrast = 100;
	ccontrast = 100;
	deinterlace = 0;
	mode = 0;
	delay = 3;
}

int DenoiseMJPEGConfig::equivalent(DenoiseMJPEGConfig &that)
{
	return
		that.radius == radius &&
		that.threshold == threshold &&
		that.threshold2 == threshold2 &&
		that.sharpness == sharpness &&
		that.lcontrast == lcontrast &&
		that.ccontrast == ccontrast &&
		that.deinterlace == deinterlace &&
		that.mode == mode &&
		that.delay == delay;
}

void DenoiseMJPEGConfig::copy_from(DenoiseMJPEGConfig &that)
{
	radius = that.radius;
	threshold = that.threshold;
	threshold2 = that.threshold2;
	sharpness = that.sharpness;
	lcontrast = that.lcontrast;
	ccontrast = that.ccontrast;
	deinterlace = that.deinterlace;
	mode = that.mode;
	delay = that.delay;
}

void DenoiseMJPEGConfig::interpolate(DenoiseMJPEGConfig &prev,
	DenoiseMJPEGConfig &next,
	long prev_frame,
	long next_frame,
	long current_frame)
{
	double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
	double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);

	this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale);
	this->threshold = (int)(prev.threshold * prev_scale + next.threshold * next_scale);
	this->threshold2 = (int)(prev.threshold2 * prev_scale + next.threshold2 * next_scale);
	this->sharpness = (int)(prev.sharpness * prev_scale + next.sharpness * next_scale);
	this->lcontrast = (int)(prev.lcontrast * prev_scale + next.lcontrast * next_scale);
	this->ccontrast = (int)(prev.ccontrast * prev_scale + next.ccontrast * next_scale);
	this->deinterlace = prev.deinterlace;
	this->mode = prev.mode;
	this->delay = (int)(prev.delay * prev_scale + next.delay * next_scale);
}






DenoiseMJPEGRadius::DenoiseMJPEGRadius(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.radius,
	8,
	24)
{
	this->plugin = plugin;
}

int DenoiseMJPEGRadius::handle_event()
{
	int result = get_value();
	plugin->config.radius = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGThresh::DenoiseMJPEGThresh(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.threshold,
	0,
	255)
{
	this->plugin = plugin;
}

int DenoiseMJPEGThresh::handle_event()
{
	int result = get_value();
	plugin->config.threshold = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGThresh2::DenoiseMJPEGThresh2(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.threshold2,
	0,
	255)
{
	this->plugin = plugin;
}

int DenoiseMJPEGThresh2::handle_event()
{
	int result = get_value();
	plugin->config.threshold2 = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGSharp::DenoiseMJPEGSharp(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.sharpness,
	0,
	255)
{
	this->plugin = plugin;
}

int DenoiseMJPEGSharp::handle_event()
{
	int result = get_value();
	plugin->config.sharpness = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGLContrast::DenoiseMJPEGLContrast(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.lcontrast,
	0,
	255)
{
	this->plugin = plugin;
}

int DenoiseMJPEGLContrast::handle_event()
{
	int result = get_value();
	plugin->config.lcontrast = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGCContrast::DenoiseMJPEGCContrast(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.ccontrast,
	0,
	255)
{
	this->plugin = plugin;
}

int DenoiseMJPEGCContrast::handle_event()
{
	int result = get_value();
	plugin->config.ccontrast = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGDeinterlace::DenoiseMJPEGDeinterlace(DenoiseMJPEG *plugin, int x, int y)
 : BC_CheckBox(x,
 	y,
	plugin->config.deinterlace,
	_("Deinterlace"))
{
	this->plugin = plugin;
}

int DenoiseMJPEGDeinterlace::handle_event()
{
	int result = get_value();
	plugin->config.deinterlace = result;
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGModeProgressive::DenoiseMJPEGModeProgressive(DenoiseMJPEG *plugin, DenoiseMJPEGWindow *gui, int x, int y)
 : BC_Radial(x,
 	y,
	plugin->config.mode == 0,
	_("Progressive"))
{
	this->plugin = plugin;
	this->gui = gui;
}

int DenoiseMJPEGModeProgressive::handle_event()
{
	int result = get_value();
	if(result) gui->update_mode(0);
	plugin->send_configure_change();
	return 1;
}


DenoiseMJPEGModeInterlaced::DenoiseMJPEGModeInterlaced(DenoiseMJPEG *plugin, DenoiseMJPEGWindow *gui, int x, int y)
 : BC_Radial(x,
 	y,
	plugin->config.mode == 1,
	_("Interlaced"))
{
	this->plugin = plugin;
	this->gui = gui;
}

int DenoiseMJPEGModeInterlaced::handle_event()
{
	int result = get_value();
	if(result) gui->update_mode(1);
	plugin->send_configure_change();
	return 1;
}


DenoiseMJPEGModeFast::DenoiseMJPEGModeFast(DenoiseMJPEG *plugin, DenoiseMJPEGWindow *gui, int x, int y)
 : BC_Radial(x,
 	y,
	plugin->config.mode == 2,
	_("Fast"))
{
	this->plugin = plugin;
	this->gui = gui;
}

int DenoiseMJPEGModeFast::handle_event()
{
	int result = get_value();
	if(result) gui->update_mode(2);
	plugin->send_configure_change();
	return 1;
}






DenoiseMJPEGDelay::DenoiseMJPEGDelay(DenoiseMJPEG *plugin, int x, int y)
 : BC_IPot(x,
 	y,
	plugin->config.delay,
	1,
	8)
{
	this->plugin = plugin;
}

int DenoiseMJPEGDelay::handle_event()
{
	int result = get_value();
	plugin->config.delay = result;
	plugin->send_configure_change();
	return 1;
}











DenoiseMJPEGWindow::DenoiseMJPEGWindow(DenoiseMJPEG *plugin)
 : PluginClientWindow(plugin, xS(250), yS(350), 0, 0, 1)
{
	this->plugin = plugin;
}


void DenoiseMJPEGWindow::create_objects()
{
	int xs10 = xS(10), xs25 = xS(25), xs30 = xS(30), xs140 = xS(140), xs180 = xS(180);
	int ys10 = yS(10), ys20 = yS(20);
	int x1 = xs10, y1 = ys20, x2 = xs140, x3 = xs180, y2 = ys10, margin = xs30, margin2 = xs25;
	add_subwindow(new BC_Title(x1, y1, _("Search radius:")));
	add_subwindow(radius = new DenoiseMJPEGRadius(plugin, x2, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Pass 1 threshold:")));
	add_subwindow(threshold1 = new DenoiseMJPEGThresh(plugin, x3, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Pass 2 threshold:")));
	add_subwindow(threshold2 = new DenoiseMJPEGThresh2(plugin, x2, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Sharpness:")));
	add_subwindow(sharpness = new DenoiseMJPEGSharp(plugin, x3, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Luma contrast:")));
	add_subwindow(lcontrast = new DenoiseMJPEGLContrast(plugin, x2, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Chroma contrast:")));
	add_subwindow(ccontrast = new DenoiseMJPEGCContrast(plugin, x3, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Delay frames:")));
	add_subwindow(delay = new DenoiseMJPEGDelay(plugin, x2, y2));
	y1 += margin;
	y2 += margin;
	add_subwindow(new BC_Title(x1, y1, _("Mode:")));
	add_subwindow(interlaced = new DenoiseMJPEGModeInterlaced(plugin, this, x2, y1));
	y1 += margin2;
	y2 += margin2;
	add_subwindow(progressive = new DenoiseMJPEGModeProgressive(plugin, this, x2, y1));
	y1 += margin2;
	y2 += margin2;
	add_subwindow(fast = new DenoiseMJPEGModeFast(plugin, this, x2, y1));
	y1 += margin;
	y2 += margin;
	add_subwindow(deinterlace = new DenoiseMJPEGDeinterlace(plugin, x1, y1));

	show_window();
	flush();
}

void DenoiseMJPEGWindow::update_mode(int value)
{
	plugin->config.mode = value;
	progressive->update(value == 0);
	interlaced->update(value == 1);
	fast->update(value == 2);
}

int DenoiseMJPEGWindow::close_event()
{
	set_done(1);
	return 1;
}




DenoiseMJPEG::DenoiseMJPEG(PluginServer *server)
 : PluginVClient(server)
{

	accumulation = 0;
}


DenoiseMJPEG::~DenoiseMJPEG()
{


	if(accumulation) delete [] accumulation;
}

int DenoiseMJPEG::process_realtime(VFrame *input, VFrame *output)
{
	load_configuration();
	return 0;
}

const char* DenoiseMJPEG::plugin_title() { return N_("Denoise video2"); }
int DenoiseMJPEG::is_realtime() { return 1; }

void DenoiseMJPEG::update_gui()
{
	if(thread)
	{
		load_configuration();
		DenoiseMJPEGWindow *window = (DenoiseMJPEGWindow *)thread->window;
		window->lock_window();
		window->delay->update(config.delay);
		window->threshold1->update(config.threshold);
		window->unlock_window();
	}
}


NEW_WINDOW_MACRO(DenoiseMJPEG, DenoiseMJPEGWindow)
LOAD_CONFIGURATION_MACRO(DenoiseMJPEG, DenoiseMJPEGConfig)


void DenoiseMJPEG::save_data(KeyFrame *keyframe)
{
	FileXML output;

// cause data to be stored directly in text
	output.set_shared_output(keyframe->xbuf);
	output.tag.set_title("DENOISE_VIDEO2");
	output.tag.set_property("RADIUS", config.radius);
	output.tag.set_property("THRESHOLD", config.threshold);
	output.tag.set_property("THRESHOLD2", config.threshold2);
	output.tag.set_property("SHARPNESS", config.sharpness);
	output.tag.set_property("LCONTRAST", config.lcontrast);
	output.tag.set_property("CCONTRAST", config.ccontrast);
	output.tag.set_property("DEINTERLACE", config.deinterlace);
	output.tag.set_property("MODE", config.mode);
	output.tag.set_property("DELAY", config.delay);
	output.append_tag();
	output.tag.set_title("/DENOISE_VIDEO2");
	output.append_tag();
	output.append_newline();
	output.terminate_string();
}

void DenoiseMJPEG::read_data(KeyFrame *keyframe)
{
	FileXML input;

	input.set_shared_input(keyframe->xbuf);

	while(!input.read_tag())
	{
		if(input.tag.title_is("DENOISE_VIDEO2"))
		{
			config.radius = input.tag.get_property("RADIUS", config.radius);
			config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
			config.threshold2 = input.tag.get_property("THRESHOLD2", config.threshold2);
			config.sharpness = input.tag.get_property("SHARPNESS", config.sharpness);
			config.lcontrast = input.tag.get_property("LCONTRAST", config.lcontrast);
			config.ccontrast = input.tag.get_property("CCONTRAST", config.ccontrast);
			config.deinterlace = input.tag.get_property("DEINTERLACE", config.deinterlace);
			config.mode = input.tag.get_property("MODE", config.mode);
			config.delay = input.tag.get_property("DELAY", config.delay);
		}
	}
}




