/* quickpalette.c
 * This file is part of Amsam photo cropping program
 * Copyright 2021, 2023, 2024, 2025, 2026 Nandakumar Edamana
 * Distributed under GNU General Public License, version 3
 */ 

#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>

#include "quickpalette.h"


void quick_palette_construct(QuickPalette *this)
{
	image_view_construct((ImageView *) this);
	this->_ngg_vtab_image_view.destruct = (void (*)(ImageView *this)) quick_palette__nggnonvirt_destruct;
	this->firsttime = false;
	this->selclr = linear_pixel_default();
	this->curh = 0;
	this->curw = 0;
	this->oncolorset_data = NULL;
	this->oncolorset_callback = NULL;
	this->autoresize = false;

	this->selclr_spectrum = LINEAR_PIXEL_RED;

	g_signal_connect((GtkWidget *) (this->da), "size-allocate", G_CALLBACK(quick_palette_on_da_sizeallocate), this);
	gtk_widget_set_size_request((GtkWidget *) (this->da), 240, 48);

	gtk_widget_add_events((GtkWidget *) (this->da), GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK);

	g_signal_connect((GtkWidget *) (this->da), "event", G_CALLBACK(quick_palette_on_da_event), this);
}

void quick_palette_on_da_event(GtkWidget *widget, GdkEvent *event, void *user_data)
{
	if (!(event->type == GDK_BUTTON_PRESS || event->type == GDK_MOTION_NOTIFY)) {
		return;
	}

	_ngg_tuple_ngg_coords_from_event _ngg_tmp_0 = ngg_coords_from_event(event);
	double y = _ngg_tmp_0.m1;
	double x = _ngg_tmp_0.m0;

	_ngg_tuple_QuickPalette__get_linear_color_at _ngg_tmp_1 = quick_palette_get_linear_color_at((QuickPalette *) user_data, (int) x, (int) y, event->type == GDK_MOTION_NOTIFY);
	_Bool in_spectrum = _ngg_tmp_1.m2;
	_Bool valid = _ngg_tmp_1.m1;
	LinearPixel color = _ngg_tmp_1.m0;
	if (!valid) {
		return;
	}

	if (in_spectrum && !linear_pixels_match(color, ((QuickPalette *) user_data)->selclr_spectrum)) {
		assert(((QuickPalette *) user_data)->img);
		fill_spectrum(((QuickPalette *) user_data)->img, color, false);
		image_view_queue_draw((ImageView *) ((QuickPalette *) user_data));
		((QuickPalette *) user_data)->selclr_spectrum = color;
	}

	if (((QuickPalette *) user_data)->oncolorset_callback) {
		void (*cbknn)(QuickPalette *widget, LinearPixel eventcolor_linear, void *user_data);
		cbknn = (void (*)(QuickPalette *widget, LinearPixel eventcolor_linear, void *user_data)) (((QuickPalette *) user_data)->oncolorset_callback);
		if (!linear_pixels_match(color, ((QuickPalette *) user_data)->selclr) || ((QuickPalette *) user_data)->firsttime) {
			assert(1 == color.rgb[3]);
			((QuickPalette *) user_data)->firsttime = false;
			cbknn((QuickPalette *) user_data, color, ((QuickPalette *) user_data)->oncolorset_data);
		}
	}

	((QuickPalette *) user_data)->selclr = color;
}

void quick_palette_on_da_sizeallocate(GtkDrawingArea *widget, GdkRectangle *nggevent_allocation, void *user_data)
{
	int neww = nggevent_allocation->width;
	int newh = nggevent_allocation->height;

	neww -= neww % imax2(6, NSHADES);

	newh -= newh % 4;

	neww += 1;
	newh += 1;

	if (neww <= 0 || newh <= 0) {
		return;
	}

	if (neww == ((QuickPalette *) user_data)->curw && newh == ((QuickPalette *) user_data)->curh) {
		return;
	}

	Image *img2 = (Image *) malloc(sizeof(Image));
	if (img2 == NULL) {
		perror(NULL);
		exit(EXIT_FAILURE);
	}

	image_construct(img2, neww, newh, 255);

	fill_spectrum(img2, ((QuickPalette *) user_data)->selclr_spectrum, true);
	image_view_set_image((ImageView *) ((QuickPalette *) user_data), img2);

	((QuickPalette *) user_data)->curw = neww;
	((QuickPalette *) user_data)->curh = newh;
}

void quick_palette_set_hexpand(QuickPalette *this, _Bool expand)
{
	gtk_widget_set_hexpand((GtkWidget *) (this->da), expand);
}

_ngg_tuple_QuickPalette__get_linear_color_at quick_palette_get_linear_color_at(QuickPalette *this, int x, int y, _Bool dragging)
{
	assert(this->img);
	Image *imgnn = this->img;

	if (!dragging) {
		if (((x < 0 || y < 0) || x >= imgnn->width) || y >= imgnn->height) {
			return (_ngg_tuple_QuickPalette__get_linear_color_at) { LINEAR_PIXEL_BLACK, false, false };
		}
	} else {
		x = imax2(x, 0);
		x = imin2(x, imgnn->width - 1);
		y = imax2(y, 0);
		y = imin2(y, imgnn->height - 1);
	}

	int spectrumh = imgnn->height / 4;

	return (_ngg_tuple_QuickPalette__get_linear_color_at) { nan_img_get_linear_pixel(imgnn, x, y), true, y < spectrumh };
}

void quick_palette__nggnonvirt_destruct(QuickPalette *this)
{
	image_view__nggnonvirt_destruct((ImageView *) this);
}

void fill_spectrum(Image *img, LinearPixel basecolor, _Bool redraw_spectrum)
{
	int segwidth = img->width / 6;

	int shadewidth = img->width / NSHADES;

	int spectrumh = img->height / 4;

	VaraRect region = image_get_region(img);
	if (!redraw_spectrum) {
		region.y = spectrumh;
		region.h -= spectrumh;
	}

	image_fill_region(img, LINEAR_PIXEL_WHITE, region);

	_Bool steppy = true;

	for (int gradtype = 1; gradtype < 4; gradtype += 1) {
		Pixel _tmp_1;
		if (gradtype == 1) {
			_tmp_1 = PIXEL_BLACK;
		} else {
			_tmp_1 = (gradtype == 2)? PIXEL_GRAY: PIXEL_WHITE;
		}

		Pixel fromclr = _tmp_1;

		Pixel curclr = fromclr;

		for (int x = 0; x < img->width; x += 1) {
			double xfac = (double) x / img->width;

			if (!steppy || 0 == x % shadewidth) {
				for (int i = 0; i < 3; i += 1) {
					curclr.rgb[i] = fromclr.rgb[i] + xfac * (basecolor.rgb[i] - fromclr.rgb[i]);
				}
			}

			int yoffset = spectrumh * gradtype;
			for (int y = yoffset; y < yoffset + spectrumh; y += 1) {
				nan_img_set_linear_pixel(img, x, y, srgbpixel2linear(curclr));
			}
		}
	}

	for (int yy = spectrumh; yy < img->height; yy += 1) {
		if (0 == yy % spectrumh) {
			for (int x = 0; x < img->width; x += 1) {
				nan_img_set_linear_pixel(img, x, yy, LINEAR_PIXEL_BLACK);
			}
		} else {
			for (int x = 0; x < img->width; x += shadewidth) {
				nan_img_set_linear_pixel(img, x, yy, LINEAR_PIXEL_BLACK);
			}
		}
	}

	if (!redraw_spectrum) {
		return;
	}

	int steps = 4;
	for (int gx = 0; gx <= segwidth; gx += 1) {
		double g = (double) ((gx >> steps) << steps) / segwidth;

		for (int y = 0; y < spectrumh; y += 1) {
			nan_img_set_linear_pixel(img, gx, y, srgbpixel2linear((Pixel) { { 1, g, 0, 1 } }));
		}
	}

	for (int rx = 0; rx <= segwidth; rx += 1) {
		double r = 1 - (double) ((rx >> steps) << steps) / segwidth;

		for (int y = 0; y < spectrumh; y += 1) {
			nan_img_set_linear_pixel(img, segwidth + rx, y, srgbpixel2linear((Pixel) { { r, 1, 0, 1 } }));
		}
	}

	for (int bx = 0; bx <= segwidth; bx += 1) {
		double b = (double) ((bx >> steps) << steps) / segwidth;

		for (int y = 0; y < spectrumh; y += 1) {
			nan_img_set_linear_pixel(img, 2 * segwidth + bx, y, srgbpixel2linear((Pixel) { { 0, 1, b, 1 } }));
		}
	}

	for (int gx = 0; gx <= segwidth; gx += 1) {
		double g = 1 - (double) ((gx >> steps) << steps) / segwidth;

		for (int y = 0; y < spectrumh; y += 1) {
			nan_img_set_linear_pixel(img, 3 * segwidth + gx, y, srgbpixel2linear((Pixel) { { 0, g, 1, 1 } }));
		}
	}

	for (int rx = 0; rx <= segwidth; rx += 1) {
		double r = (double) ((rx >> steps) << steps) / segwidth;

		for (int y = 0; y < spectrumh; y += 1) {
			nan_img_set_linear_pixel(img, 4 * segwidth + rx, y, srgbpixel2linear((Pixel) { { r, 0, 1, 1 } }));
		}
	}

	for (int bx = 0; bx <= segwidth; bx += 1) {
		double b = 1 - (double) ((bx >> steps) << steps) / segwidth;

		for (int y = 0; y < spectrumh; y += 1) {
			nan_img_set_linear_pixel(img, 5 * segwidth + bx, y, srgbpixel2linear((Pixel) { { 1, 0, b, 1 } }));
		}
	}
}

_ngg_tuple_QuickPalette__get_linear_color_at _ngg_tuple_QuickPalette__get_linear_color_at_default()
{
	_ngg_tuple_QuickPalette__get_linear_color_at s;
	s.m0 = linear_pixel_default();
	s.m1 = false;
	s.m2 = false;
	return s;
}
