/* sortix.c
 * Sortix display(1) support
 * (c) 2021 Juhani 'nortti' Krekelä
 * This file is a part of the Links program, released under GPL.
 */

#include "cfg.h"

#ifdef GRDRV_SORTIX

#include <err.h> //debg
#include <errno.h>
#include <stdint.h>

#include <display.h>
#include <framebuffer.h>

#include "links.h"

static int default_window_width = 600;
static int default_window_height = 500;

static struct display_connection* connection;
static struct display_event_handlers event_handlers;

struct graphics_driver sortix_driver;

static void sortix_process_events(void *data)
{
	(void) data;
	warnx("sortix_process_events");//debg
	while (display_poll_event(connection, &event_handlers) == 0);
}

static unsigned char *sortix_init_driver(unsigned char *param, unsigned char *display)
{
	if (param && *param)
		errx(1, "param %s", param);
	if (display && *display)
		errx(1, "display %s", display);

	connection = display_connect_default();
	if (!connection && errno == ECONNREFUSED)
		return "TODO: display_spawn";

	if (!connection)
		return "TODO: Proper error reporting";

	sortix_driver.get_color = get_color_fn(sortix_driver.depth);

	set_handlers(display_connection_fd(connection), sortix_process_events, NULL, NULL);

	return NULL;
}

struct window_data
{
	uint32_t window_id;
	struct framebuffer fb;
};

static struct graphics_device *sortix_init_device(void)
{
	struct graphics_device *dev = mem_calloc(sizeof(struct graphics_device));

	dev->size.x1 = 0;
	dev->size.y1 = 0;
	dev->size.x2 = default_window_width;
	dev->size.y2 = default_window_height;

	dev->clip = dev->size;

	struct window_data *window_data = mem_calloc(sizeof(struct window_data));
	dev->driver_data = window_data;
	window_data->window_id = 0;

	// TODO: Overflow when multiplying
	window_data->fb.buffer = mem_calloc(dev->size.x2 * dev->size.y2 * sizeof(uint32_t));
	window_data->fb.xres = dev->size.x2;
	window_data->fb.yres = dev->size.y2;
	window_data->fb.pitch = dev->size.x2;

	display_create_window(connection, window_data->window_id);
	display_resize_window(connection, window_data->window_id, dev->size.x2, dev->size.y2);
	display_show_window(connection, window_data->window_id);

	return dev;
}

static void sortix_shutdown_device(struct graphics_device *dev)
{
	struct window_data *window_data = dev->driver_data;
	display_destroy_window(connection, window_data->window_id);
	free(window_data);
}

static void sortix_shutdown_driver(void)
{
	display_disconnect(connection);
}

static void sortix_after_fork(void)
{
	if (connection)
		display_disconnect(connection);
}

static unsigned char *sortix_get_driver_param(void)
{
	return NULL; //TODO: driver parameters?
}

static int sortix_get_empty_bitmap(struct bitmap *dest)
{
	// TODO: Overflow when multiplying
	dest->data = mem_alloc_mayfail((size_t)dest->x * (size_t)dest->y * sizeof(uint32_t));
	if (!dest->data)
		return -1;
	// TODO: Overflow when multiplying
	dest->skip = (ssize_t)dest->x * sizeof(uint32_t);
	dest->flags = 0;
	return 0;
}

static void sortix_register_bitmap(struct bitmap *bmp)
{
}

static void *sortix_prepare_strip(struct bitmap *bmp, int top, int lines)
{
	if (!bmp->data)
		return NULL;
	// TODO: Overflow when multiplying
	return ((unsigned char *)bmp->data) + bmp->skip * top;
}

static void sortix_commit_strip(struct bitmap *bmp, int top, int lines)
{
}

static void sortix_unregister_bitmap(struct bitmap *bmp)
{
	if (bmp->data)
		mem_free(bmp->data);
}

static void sortix_draw_bitmap(struct graphics_device *dev, struct bitmap *bmp, int x1, int y1)
{
	struct window_data *window_data = dev->driver_data;

	size_t width = bmp->x;
	size_t height = bmp->y;
	if (!width || !height)
		return;
	// TODO: Overflow when multiplying
	uint32_t *buffer = mem_alloc(width * height * sizeof(uint32_t));

	for (size_t y = 0; y < height; y++) {
		for (size_t x = 0; x < width; x++) {
			void *pixel_data = ((unsigned char*)bmp->data) + y * bmp->skip + x * sizeof(uint32_t);
			uint32_t pixel;
			memcpy(&pixel, pixel_data, sizeof(uint32_t));
			// Set alpha (see sortix_fill_area() for more details)
			pixel |= 0xff000000;
			buffer[y * width + x] = pixel;
		}
	}
	display_render_window(connection, window_data->window_id, x1, y1, width, height, buffer);
	free(buffer);
}

static void sortix_fill_area(struct graphics_device *dev, int x1, int y1, int x2, int y2, long color)
{
	struct window_data *window_data = dev->driver_data;

	size_t width = x2 - x1;
	size_t height = y2 - y1;
	if (!width || !height)
		return;
	// TODO: Overflow when multiplying
	uint32_t *buffer = mem_alloc(width * height * sizeof(uint32_t));

	// Links uses a pixel format where the top byte is clear
	// Sortix stores alpha there, so set it to 255
	uint32_t pixel = color | 0xff000000;

	for (size_t y = 0; y < height; y++) {
		for (size_t x = 0; x < width; x++)
			buffer[y * width + x] = pixel;
	}
	display_render_window(connection, window_data->window_id, x1, y1, width, height, buffer);
	free(buffer);
}

static void sortix_draw_hline(struct graphics_device *dev, int x1, int y, int x2, long color)
{
	sortix_fill_area(dev, x1, y, x2, y + 1, color);
}

static void sortix_draw_vline(struct graphics_device *dev, int x, int y1, int y2, long color)
{
	sortix_fill_area(dev, x, y1, x + 1, y2, color);
}

static void sortix_set_title(struct graphics_device *dev, unsigned char *title)
{
	struct window_data *window_data = dev->driver_data;
	display_title_window(connection, window_data->window_id, title);
}

struct graphics_driver sortix_driver =
{
	(unsigned char *)"sortix",
	sortix_init_driver,
	sortix_init_device,
	sortix_shutdown_device,
	sortix_shutdown_driver,
	NULL, // emergency_shutdown
	sortix_after_fork,
	sortix_get_driver_param,
	NULL, // get_af_unix_name
	NULL, // get_margin
	NULL, // set_margin
	sortix_get_empty_bitmap,
	sortix_register_bitmap,
	sortix_prepare_strip,
	sortix_commit_strip,
	sortix_unregister_bitmap,
	sortix_draw_bitmap,
	NULL, // get_color, set in sortix_init_driver()
	sortix_fill_area,
	sortix_draw_hline,
	sortix_draw_vline,
	NULL, // scroll
	NULL, // set_clip_area
	NULL, // flush
	NULL, // block
	NULL, // unblock
	NULL, // set_palette
	NULL, // get_real_colors
	sortix_set_title,
	NULL, // exec
	NULL, // set_clipboard_text
	NULL, // get_clipboard_text
	(24 << 3) | 4, // depth: 24bpp, 4 bytes per pixel
	0, 0, // size (x, y), unused
	GD_DONT_USE_SCROLL, //flags
	NULL, // param
};

#endif /* GRDRV_SORTIX */
