
/*
 * 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 "bcdisplayinfo.h"
#include "clip.h"
#include "bccmodels.h"
#include "filexml.h"
#include "aggregated.h"
#include "language.h"
#include "interpolate.h"

#include <stdio.h>
#include <string.h>


REGISTER_PLUGIN(InterpolatePixelsMain)






InterpolatePixelsOffset::InterpolatePixelsOffset(InterpolatePixelsWindow *window,
	int x,
	int y,
	int *output)
 : BC_ISlider(x, y, 0, xS(50), yS(50), 0, 1, *output, 0)
{
	this->window = window;
	this->output = output;
}

InterpolatePixelsOffset::~InterpolatePixelsOffset()
{
}

int InterpolatePixelsOffset::handle_event()
{
	*output = get_value();
	window->client->send_configure_change();
	return 1;
}






InterpolatePixelsWindow::InterpolatePixelsWindow(InterpolatePixelsMain *client)
 : PluginClientWindow(client,
	xS(200), yS(70), xS(200), yS(70), 0)
{
	this->client = client;
}

InterpolatePixelsWindow::~InterpolatePixelsWindow()
{
}

void InterpolatePixelsWindow::create_objects()
{
	int xs5 = xS(5), xs10 = xS(10);
	int ys5 = yS(5), ys10 = yS(10);
	int x = xs10, y = ys10;

	BC_Title *title;
	add_tool(title = new BC_Title(x, y, _("X Offset:")));
	add_tool(x_offset = new InterpolatePixelsOffset(this,
		x + title->get_w() + xs5,
		y,
		&client->config.x));
	y += MAX(x_offset->get_h(), title->get_h()) + ys5;
	add_tool(title = new BC_Title(x, y, _("Y Offset:")));
	add_tool(y_offset = new InterpolatePixelsOffset(this,
		x + title->get_w() + xs5,
		y,
		&client->config.y));
	y += MAX(y_offset->get_h(), title->get_h()) + ys5;

	show_window();
}















InterpolatePixelsConfig::InterpolatePixelsConfig()
{
	x = 0;
	y = 0;
}

int InterpolatePixelsConfig::equivalent(InterpolatePixelsConfig &that)
{
	return x == that.x && y == that.y;
}

void InterpolatePixelsConfig::copy_from(InterpolatePixelsConfig &that)
{
	x = that.x;
	y = that.y;
}

void InterpolatePixelsConfig::interpolate(InterpolatePixelsConfig &prev,
    InterpolatePixelsConfig &next,
    int64_t prev_position,
    int64_t next_position,
    int64_t current_position)
{
    this->x = prev.x;
    this->y = prev.y;
}






InterpolatePixelsMain::InterpolatePixelsMain(PluginServer *server)
 : PluginVClient(server)
{
	out_temp = out_frame = 0;
	engine = 0;
}

InterpolatePixelsMain::~InterpolatePixelsMain()
{
	delete out_temp;
	delete engine;
}

const char* InterpolatePixelsMain::plugin_title() { return N_("Interpolate Bayer"); }
int InterpolatePixelsMain::is_realtime() { return 1; }


NEW_WINDOW_MACRO(InterpolatePixelsMain, InterpolatePixelsWindow)


void InterpolatePixelsMain::update_gui()
{
	if(thread)
	{
		int changed = load_configuration();
		if(changed)
		{
			thread->window->lock_window("InterpolatePixelsMain::update_gui");
			((InterpolatePixelsWindow*)thread->window)->x_offset->update(config.x);
			((InterpolatePixelsWindow*)thread->window)->y_offset->update(config.y);
			thread->window->unlock_window();
		}
	}
}


LOAD_CONFIGURATION_MACRO(InterpolatePixelsMain, InterpolatePixelsConfig)


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

// cause data to be stored directly in text
	output.set_shared_output(keyframe->xbuf);
	output.tag.set_title("INTERPOLATEPIXELS");
	output.tag.set_property("X", config.x);
	output.tag.set_property("Y", config.y);
	output.append_tag();
	output.tag.set_title("/INTERPOLATEPIXELS");
	output.append_tag();
	output.append_newline();
	output.terminate_string();
}

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

	input.set_shared_input(keyframe->xbuf);

	int result = 0;

	while(!result)
	{
		result = input.read_tag();

		if(!result)
		{
			if(input.tag.title_is("INTERPOLATEPIXELS"))
			{
				config.x = input.tag.get_property("X", config.x);
				config.y = input.tag.get_property("Y", config.y);
			}
		}
	}
}

int InterpolatePixelsMain::process_buffer(VFrame *frame,
	int64_t start_position,
	double frame_rate)
{
	load_configuration();

// Set aggregation parameters
	frame->get_params()->update("INTERPOLATEPIXELS_X", config.x);
	frame->get_params()->update("INTERPOLATEPIXELS_Y", config.y);

	read_frame(frame,
		0,
		start_position,
		frame_rate,
		get_use_opengl());
//frame->dump_params();

	if(get_use_opengl())
	{
// Aggregate with gamma
		if(next_effect_is(_("Gamma")) ||
			next_effect_is(_("Histogram")) ||
			next_effect_is(_("Color Balance")))
			return 0;


		return run_opengl();
	}

	int color_model = frame->get_color_model();
	int active_model = BC_CModels::has_alpha(color_model) ?
		BC_RGBA_FLOAT : BC_RGB_FLOAT;
	new_temp(frame->get_w(), frame->get_h(), active_model);
	get_temp()->transfer_from(frame);

	out_frame = get_output();
	color_model = out_frame->get_color_model();
	active_model = BC_CModels::has_alpha(color_model) ?
		BC_RGBA_FLOAT : BC_RGB_FLOAT;
	if( active_model != color_model ) {
		int w = out_frame->get_w(), h = out_frame->get_h();
		if( out_temp && ( out_temp->get_color_model() != active_model ||
			     out_temp->get_w() != w || out_temp->get_w() != h ) ) {
			delete out_temp;  out_temp = 0;
		}
		if( !out_temp )
			out_temp = new VFrame(w, h, active_model, 0);
		out_frame = out_temp;
	}

	if(!engine)
		engine = new InterpolatePixelsEngine(this);
	engine->process_packages();

	if( out_frame != get_output() ) {
		if( BC_CModels::has_alpha(out_frame->get_color_model()) ) {
			unsigned char **out_rows = out_frame->get_rows();
			int w = out_frame->get_w(), h = out_frame->get_h();
			if( BC_CModels::has_alpha(get_temp()->get_color_model()) ) {
				unsigned char **in_rows = get_temp()->get_rows();
				for( int y=0; y<h; ++y ) {
					float *inp = (float *)in_rows[y];
					float *outp = (float *)out_rows[y];
					for( int x=0; x<w; ++x,inp+=4,outp+=4 ) outp[3] = inp[3];
				}
			}
			else {
				for( int y=0; y<h; ++y ) {
					float *outp = (float *)out_rows[y];
					for( int x=0; x<w; ++x,outp+=4 ) outp[3] = 1;
				}
			}
		}
		get_output()->transfer_from(out_frame);
	}

	return 0;
}

// The pattern is
//   G B
//   R G
// And is offset to recreate the 4 possibilities.
// The color values are interpolated in the most basic way.
// No adaptive algorithm is used.

int InterpolatePixelsMain::handle_opengl()
{
//printf("InterpolatePixelsMain::handle_opengl\n");
#ifdef HAVE_GL


	get_output()->to_texture();
	get_output()->enable_opengl();

        const char *shader_stack[16];
        memset(shader_stack,0, sizeof(shader_stack));
        int current_shader = 0;

	INTERPOLATE_COMPILE(shader_stack, current_shader);

	shader_stack[current_shader] = 0;
	unsigned int shader = VFrame::make_shader(shader_stack);
	if( shader > 0 ) {
		glUseProgram(shader);
		glUniform1i(glGetUniformLocation(shader, "tex"), 0);
		INTERPOLATE_UNIFORMS(shader);
	}

	get_output()->init_screen();
	get_output()->bind_texture(0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);


	get_output()->draw_texture();
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glUseProgram(0);
	get_output()->set_opengl_state(VFrame::SCREEN);

#endif
	return 0;
}













InterpolatePixelsPackage::InterpolatePixelsPackage()
 : LoadPackage()
{

}






InterpolatePixelsUnit::InterpolatePixelsUnit(InterpolatePixelsEngine *server, InterpolatePixelsMain *plugin)
 : LoadClient(server)
{
	this->plugin = plugin;
	this->server = server;
}

void InterpolatePixelsUnit::process_package(LoadPackage *package)
{
	InterpolatePixelsPackage *pkg = (InterpolatePixelsPackage*)package;
	int h = plugin->get_temp()->get_h();
	int w = plugin->get_temp()->get_w();
	int pattern_offset_x = plugin->config.x;
	int pattern_offset_y = plugin->config.y;
	int y1 = pkg->y1;
	int y2 = pkg->y2;
	int components = BC_CModels::components(plugin->out_frame->get_color_model());
	float color_matrix[9];
	memcpy(color_matrix, server->color_matrix, sizeof(color_matrix));

// printf("InterpolatePixelsUnit::process_package %d color_matrix=", __LINE__);
// for(int i = 0; i < 9; i++)
// {
// 	printf("%f ", color_matrix[i]);
// }
// printf("\n");

	y1 = MAX(y1, 1);
	y2 = MIN(y2, h - 1);

// Only supports float because it's useless in any other colormodel.
	for(int i = y1; i < y2; i++)
	{
		int pattern_coord_y = (i - pattern_offset_y) % 2;
		float *prev_row = (float*)plugin->get_temp()->get_rows()[i - 1];
		float *current_row = (float*)plugin->get_temp()->get_rows()[i];
		float *next_row = (float*)plugin->get_temp()->get_rows()[i + 1];
		float *out_row = (float*)plugin->out_frame->get_rows()[i];

		prev_row += components;
		current_row += components;
		next_row += components;
		out_row += components;
		float r;
		float g;
		float b;
#undef RED
#define RED 0
#undef GREEN
#define GREEN 1
#undef BLUE
#define BLUE 2
		if(pattern_coord_y == 0)
		{
			for(int j = 1; j < w - 1; j++)
			{
				int pattern_coord_x = (j - pattern_offset_x) % 2;
// Top left pixel
				if(pattern_coord_x == 0)
				{
					r = (prev_row[RED] + next_row[RED]) / 2;
					g = current_row[GREEN];
					b = (current_row[-components + BLUE] + current_row[components + BLUE]) / 2;
				}
				else
// Top right pixel
				{
					r = (prev_row[-components + RED] +
						prev_row[components + RED] +
						next_row[-components + RED] +
						next_row[components + RED]) / 4;
					g = (current_row[-components + GREEN] +
						prev_row[GREEN] +
						current_row[components + GREEN] +
						next_row[GREEN]) / 4;
					b = current_row[BLUE];
				}

// 				out_row[0] = r * color_matrix[0] + g * color_matrix[1] + b * color_matrix[2];
// 				out_row[1] = r * color_matrix[3] + g * color_matrix[4] + b * color_matrix[5];
// 				out_row[2] = r * color_matrix[6] + g * color_matrix[7] + b * color_matrix[8];

				out_row[0] = r;
				out_row[1] = g;
				out_row[2] = b;

				prev_row += components;
				current_row += components;
				next_row += components;
				out_row += components;
			}
		}
		else
		{
			for(int j = 1; j < w - 1; j++)
			{
				int pattern_coord_x = (j - pattern_offset_x) % 2;
// Bottom left pixel
				if(pattern_coord_x == 0)
				{
					r = current_row[RED];
					g = (current_row[-components + GREEN] +
						prev_row[GREEN] +
						current_row[components + GREEN] +
						next_row[GREEN]) / 4;
					b = (prev_row[-components + BLUE] +
						prev_row[components + BLUE] +
						next_row[-components + BLUE] +
						next_row[components + BLUE]) / 4;
				}
				else
// Bottom right pixel
				{
					r = (current_row[-components + RED] + current_row[components + RED]) / 2;
					g = current_row[GREEN];
					b = (prev_row[BLUE] + next_row[BLUE]) / 2;
				}

//				out_row[0] = r * color_matrix[0] + g * color_matrix[1] + b * color_matrix[2];
//				out_row[1] = r * color_matrix[3] + g * color_matrix[4] + b * color_matrix[5];
//				out_row[2] = r * color_matrix[6] + g * color_matrix[7] + b * color_matrix[8];

				out_row[0] = r;
				out_row[1] = g;
				out_row[2] = b;

				prev_row += components;
				current_row += components;
				next_row += components;
				out_row += components;
			}
		}
	}
}




InterpolatePixelsEngine::InterpolatePixelsEngine(InterpolatePixelsMain *plugin)
 : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
{
	this->plugin = plugin;
}

void InterpolatePixelsEngine::init_packages()
{
	for( int i=0; i<9; ++i ) color_matrix[i] = 0;
	for( int i=0; i<3; ++i ) color_matrix[i*3 + i] = 1;
	char string[BCTEXTLEN];
	string[0] = 0;
	plugin->get_output()->get_params()->get("DCRAW_MATRIX", string);

// printf("InterpolatePixelsEngine::init_packages %d\n", __LINE__);
// plugin->get_output()->dump_params();

	sscanf(string,
		"%f %f %f %f %f %f %f %f %f",
		&color_matrix[0],
		&color_matrix[1],
		&color_matrix[2],
		&color_matrix[3],
		&color_matrix[4],
		&color_matrix[5],
		&color_matrix[6],
		&color_matrix[7],
		&color_matrix[8]);
	for(int i = 0; i < get_total_packages(); i++)
	{
		InterpolatePixelsPackage *package = (InterpolatePixelsPackage*)get_package(i);
		package->y1 = plugin->get_temp()->get_h() * i / get_total_packages();
		package->y2 = plugin->get_temp()->get_h() * (i + 1) / get_total_packages();
	}
}


LoadClient* InterpolatePixelsEngine::new_client()
{
	return new InterpolatePixelsUnit(this, plugin);
}


LoadPackage* InterpolatePixelsEngine::new_package()
{
	return new InterpolatePixelsPackage;
}


