/**************************************************************

main_dlg.cpp (C-Munipack project)
Main dialog
Copyright (C) 2008 David Motl, dmotl@volny.cz

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 <stdlib.h>
#include <glib.h>
#include <math.h>
#include <string.h>

#include "main.h"
#include "colorchooser_dlg.h"
#include "cmpack_graph_view.h"
#include "cmpack_curve_plot.h"

// Number of palette entries
#define CUSTOM_PALETTE_WIDTH COLOR_CHOOSER_PALETTE_WIDTH
#define CUSTOM_PALETTE_HEIGHT COLOR_CHOOSER_PALETTE_HEIGHT

// Palette colors
static void default_color(gint i, GdkColor &color)
{
	color.red	= ((i / 3) % 4)*21845;
	color.green	= ((i / 12) % 4)*21845;
	color.blue	= (i % 3)*32767;
}

// Size request of each entry
#define CUSTOM_PALETTE_ENTRY_WIDTH   20
#define CUSTOM_PALETTE_ENTRY_HEIGHT  20 

// Conversion between 0->1 double and and guint16
#define SCALE(i) (i / 65535.0)
#define UNSCALE(d) ((guint16)(d * 65535 + 0.5))

// Intensity of a color
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11) 

//-------------------------   GRAPH TEST DIALOG   --------------------------------

//
// Constructor
//
CColorChooserDlg::CColorChooserDlg(GtkWindow *pParent, const gchar *title):m_pDlg(NULL)
{
	// Main window
	m_pDlg = gtk_dialog_new_with_buttons(title, pParent, 
		(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR),
		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
	gtk_dialog_widget_standard_tooltips(GTK_DIALOG(m_pDlg));
	gtk_window_set_position(GTK_WINDOW(m_pDlg), GTK_WIN_POS_CENTER);
	gtk_dialog_set_default_response(GTK_DIALOG(m_pDlg), GTK_RESPONSE_OK);
	gtk_window_set_resizable (GTK_WINDOW (m_pDlg), FALSE);
	//g_signal_connect(G_OBJECT(m_pDlg), "response", G_CALLBACK(response_dialog), this);

	/*!!!! Dialog icon
	gchar *icon = get_icon_file("photometry");
	gtk_window_set_icon(GTK_WINDOW(m_pDlg), gdk_pixbuf_new_from_file(icon, NULL));
	g_free(icon);
	!!!!*/

	// Dialog layout
	GtkWidget *vbox = gtk_vbox_new(FALSE, 4);
	gtk_widget_set_size_request(vbox, 300, -1);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(m_pDlg)->vbox), vbox, TRUE, TRUE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);

	// Set up the palette 
	GtkWidget *table = gtk_table_new (CUSTOM_PALETTE_HEIGHT, CUSTOM_PALETTE_WIDTH, TRUE);
	gtk_table_set_row_spacings (GTK_TABLE (table), 4);
	gtk_table_set_col_spacings (GTK_TABLE (table), 4);
	for (int i = 0; i < CUSTOM_PALETTE_WIDTH; i++) {
		for (int j = 0; j < CUSTOM_PALETTE_HEIGHT; j++) {
			GtkWidget *frame = gtk_frame_new (NULL);
			gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
			if (i!=CUSTOM_PALETTE_WIDTH-1 || j!=CUSTOM_PALETTE_HEIGHT-1) {
				custom_palette[i][j] = CreateEntry();
				gtk_widget_set_size_request (custom_palette[i][j], CUSTOM_PALETTE_ENTRY_WIDTH, CUSTOM_PALETTE_ENTRY_HEIGHT);
				gtk_container_add (GTK_CONTAINER (frame), custom_palette[i][j]);
				gtk_table_attach_defaults (GTK_TABLE (table), frame, i, i+1, j, j+1);
			} else {
				custom_palette[i][j] = NULL;
			}
		}	
	}
	update_palette();
	gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, FALSE, 0);

	// Selected color
	GtkWidget *frame = gtk_frame_new (NULL);
	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	gtk_widget_set_size_request (frame, -1, CUSTOM_PALETTE_ENTRY_HEIGHT);
	gtk_box_pack_start (GTK_BOX(vbox), frame, FALSE, FALSE, 0);

	GtkWidget *hbox = gtk_hbox_new(TRUE, 4);
	gtk_container_add (GTK_CONTAINER (frame), hbox);

	GtkWidget *label = gtk_label_new("Selected color:");
	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
	gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);

	cur_sample = gtk_drawing_area_new ();
	gtk_widget_set_can_focus (cur_sample, FALSE);
	gtk_widget_set_events (cur_sample, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | 
		GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
	gtk_box_pack_start (GTK_BOX(hbox), cur_sample, TRUE, TRUE, 0);
	g_signal_connect (cur_sample, "expose-event", G_CALLBACK (palette_expose), this);

	// Show window
	gtk_widget_show_all(GTK_DIALOG(m_pDlg)->vbox);
}


//
// Destructor
//
CColorChooserDlg::~CColorChooserDlg()
{
	gtk_widget_destroy(m_pDlg);
}


//
// Execute the dialog
//
bool CColorChooserDlg::Execute(const GdkColor &old_color, GdkColor &new_color)
{
	set_palette_color(cur_sample, old_color);
	for (int i = 0; i < CUSTOM_PALETTE_WIDTH; i++) {
		for (int j = 0; j < CUSTOM_PALETTE_HEIGHT; j++) {
			if (custom_palette[i][j]) {
				GdkColor color;
				get_palette_color(custom_palette[i][j], color);
				if (gdk_color_equal(&old_color, &color)) {
					SetSelectedEntry (i, j);
					break;
				}
			}
		}
	}

	if (gtk_dialog_run(GTK_DIALOG(m_pDlg)) == GTK_RESPONSE_ACCEPT) {
		get_palette_color(cur_sample, new_color);
		return true;
	} else {
		new_color = old_color;
		return false;
	}
}


//
// Create a palette entry
//
GtkWidget* CColorChooserDlg::CreateEntry()
{
	GtkWidget *retval = gtk_drawing_area_new ();
	gtk_widget_set_can_focus (retval, TRUE);

	gtk_widget_set_events (retval, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | 
		GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);

	g_signal_connect (retval, "expose-event", G_CALLBACK (palette_expose), this);
	g_signal_connect (retval, "button-press-event", G_CALLBACK (palette_press), this);
	g_signal_connect (retval, "button-release-event", G_CALLBACK (palette_release), this);
	g_signal_connect (retval, "enter-notify-event", G_CALLBACK (palette_enter), this);
	g_signal_connect (retval, "leave-notify-event", G_CALLBACK (palette_leave), this);
 
	return retval;
}


//
// Set focus on an entry
//
void CColorChooserDlg::SetSelectedEntry (int x, int y)
{
	if (custom_palette[x][y])
		gtk_widget_grab_focus(custom_palette[x][y]);
}
 
//
// Draw an entry
//
gboolean CColorChooserDlg::palette_expose (GtkWidget *drawing_area, GdkEventExpose *event, CColorChooserDlg *pMe)
{
	pMe->OnDrawEntry(drawing_area, event->area);
	return FALSE;
} 
void CColorChooserDlg::OnDrawEntry(GtkWidget *drawing_area, const GdkRectangle &area)
{
	if (drawing_area->window) {
		cairo_t *cr = gdk_cairo_create (drawing_area->window);
		gdk_cairo_set_source_color (cr, &drawing_area->style->bg[GTK_STATE_NORMAL]);
		gdk_cairo_rectangle (cr, &area);
		cairo_fill (cr);
		if (gtk_widget_has_focus (drawing_area)) {
			gint focus_width;
			set_focus_line_attributes (drawing_area, cr, &focus_width);
			cairo_rectangle (cr, focus_width / 2.0, focus_width / 2.0,
				drawing_area->allocation.width - focus_width,
				drawing_area->allocation.height - focus_width);
			cairo_stroke (cr);
		}
		cairo_destroy (cr);
	}
}

void CColorChooserDlg::set_focus_line_attributes (GtkWidget *drawing_area, cairo_t *cr, gint *focus_width)
{
	gchar* dash_list = NULL;
  
	gtk_widget_style_get (drawing_area, "focus-line-width", focus_width, 
		"focus-line-pattern", &dash_list, NULL);
      
	gdouble color[4];
	palette_get_color (drawing_area, color);

	if (INTENSITY (color[0], color[1], color[2]) > 0.5)
		cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
	else
		cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);

	cairo_set_line_width (cr, *focus_width);

	if (dash_list && dash_list[0]) {
		gint n_dashes = (gint)strlen(dash_list);
		gdouble *dashes = g_new (gdouble, n_dashes);
		gdouble total_length = 0;
		for (gint i = 0; i < n_dashes; i++) {
			dashes[i] = dash_list[i];
			total_length += dash_list[i];
		}

		/* The dash offset here aligns the pattern to integer pixels
		* by starting the dash at the right side of the left border
		* Negative dash offsets in cairo don't work
		* (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
		*/
		gdouble dash_offset = -1.0 * (*focus_width) / 2.0;
		while (dash_offset < 0)
			dash_offset += total_length;
		cairo_set_dash (cr, dashes, n_dashes, dash_offset);
		g_free (dashes);
	}
	g_free (dash_list);
}

void CColorChooserDlg::get_palette_color (GtkWidget *drawing_area, GdkColor &color)
{
	gdouble clr[4];
	palette_get_color(drawing_area, clr);
	color.red = UNSCALE(clr[0]);
	color.green = UNSCALE(clr[1]);
	color.blue = UNSCALE(clr[2]);
}

void CColorChooserDlg::palette_get_color (GtkWidget *drawing_area, gdouble *color)
{
	g_return_if_fail (color != NULL);
  
	gdouble *color_val = (gdouble*)g_object_get_data (G_OBJECT (drawing_area), "color_val");
	if (color_val == NULL) {
		/* Default to white for no good reason */
		color[0] = 1.0;
		color[1] = 1.0;
		color[2] = 1.0;
	} else {
		color[0] = color_val[0];
		color[1] = color_val[1];
		color[2] = color_val[2];
	}
	color[3] = 1.0;
} 

gboolean CColorChooserDlg::palette_enter (GtkWidget *drawing_area, GdkEventCrossing *event, CColorChooserDlg *pMe)
{
	g_object_set_data (G_OBJECT (drawing_area), "gtk-colorsel-have-pointer", GUINT_TO_POINTER (TRUE));
	return FALSE;
}

gboolean CColorChooserDlg::palette_leave (GtkWidget *drawing_area, GdkEventCrossing *event, CColorChooserDlg *pMe)
{
	g_object_set_data (G_OBJECT (drawing_area), "gtk-colorsel-have-pointer", NULL);
	return FALSE;
} 

gboolean CColorChooserDlg::palette_press(GtkWidget *drawing_area, GdkEventButton *event, CColorChooserDlg *pMe)
{
	gtk_widget_grab_focus (drawing_area);
	return FALSE;
}

gboolean CColorChooserDlg::palette_release(GtkWidget *drawing_area, GdkEventButton *event, CColorChooserDlg *pMe)
{
	gtk_widget_grab_focus (drawing_area);

	if (event->button == 1 && g_object_get_data (G_OBJECT (drawing_area), "gtk-colorsel-have-pointer") != NULL) {
		gdouble color[4];
		palette_get_color (drawing_area, color);
		palette_set_color (pMe->cur_sample, color);
	}
	return FALSE;
}

void CColorChooserDlg::update_palette(void)
{
	for (gint i = 0; i < CUSTOM_PALETTE_HEIGHT; i++) {
		for (gint j = 0; j < CUSTOM_PALETTE_WIDTH; j++) {
			if (custom_palette[j][i]) {
				GdkColor color;
				default_color(i + j * CUSTOM_PALETTE_HEIGHT, color);
				set_palette_color (custom_palette[j][i], color);
			}
		}
	}
}

void CColorChooserDlg::set_palette_color (GtkWidget *drawing_area, const GdkColor &color)
{
	gdouble col[3];
	col[0] = SCALE (color.red);
	col[1] = SCALE (color.green);
	col[2] = SCALE (color.blue);
	palette_set_color (drawing_area, col);
}

/* Changes the view color */
void CColorChooserDlg::palette_set_color(GtkWidget *drawing_area, gdouble *color)
{
	gdouble *new_color = g_new (double, 4);
	GdkColor gdk_color;
  
	gdk_color.red = UNSCALE (color[0]);
	gdk_color.green = UNSCALE (color[1]);
	gdk_color.blue = UNSCALE (color[2]);

	gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &gdk_color);
  
	new_color[0] = color[0];
	new_color[1] = color[1];
	new_color[2] = color[2];
	new_color[3] = 1.0;
  
	g_object_set_data_full (G_OBJECT (drawing_area), "color_val", new_color, (GDestroyNotify)g_free);
} 
