// Tint2 : Separator plugin
// Author: Oskari Rauta

#include <string.h>
#include <stdio.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <pango/pangocairo.h>
#include <stdlib.h>
#include <math.h>

#include "window.h"
#include "server.h"
#include "panel.h"
#include "common.h"
#include "separator.h"

int separator_compute_desired_size(void *obj);

Separator *create_separator()
{
	Separator *separator = (Separator *)calloc(1, sizeof(Separator));
	separator->color.rgb[0] = 0.5;
	separator->color.rgb[1] = 0.5;
	separator->color.rgb[2] = 0.5;
	separator->color.alpha = 0.9;
	separator->style = SEPARATOR_DOTS;
	separator->thickness = 3;
	separator->area.paddingxlr = 1;
	return separator;
}

void destroy_separator(void *obj)
{
	Separator *separator = (Separator *)obj;
	remove_area(&separator->area);
	free_area(&separator->area);
	free_and_null(separator);
}

gpointer copy_separator(gconstpointer arg, gpointer data)
{
	Separator *old = (Separator *)arg;
	Separator *copy = (Separator *)calloc(1, sizeof(Separator));
	memcpy(copy, old, sizeof(Separator));
	return copy;
}

void init_separator()
{
	GList *to_remove = panel_config.separator_list;
	for (int k = 0; k < strlen(panel_items_order) && to_remove; k++) {
		if (panel_items_order[k] == ':') {
			to_remove = to_remove->next;
		}
	}

	if (to_remove) {
		if (to_remove == panel_config.separator_list) {
			g_list_free_full(to_remove, destroy_separator);
			panel_config.separator_list = NULL;
		} else {
			// Cut panel_config.separator_list
			if (to_remove->prev)
				to_remove->prev->next = NULL;
			to_remove->prev = NULL;
			// Remove all elements of to_remove and to_remove itself
			g_list_free_full(to_remove, destroy_separator);
		}
	}
}

void init_separator_panel(void *p)
{
	Panel *panel = (Panel *)p;

	// Make sure this is only done once if there are multiple items
	if (panel->separator_list)
		return;

	// panel->separator_list is now a copy of the pointer panel_config.separator_list
	// We make it a deep copy
	panel->separator_list = g_list_copy_deep(panel_config.separator_list, copy_separator, NULL);

	for (GList *l = panel->separator_list; l; l = l->next) {
		Separator *separator = (Separator *)l->data;
		if (!separator->area.bg)
			separator->area.bg = &g_array_index(backgrounds, Background, 0);
		separator->area.parent = p;
		separator->area.panel = p;
		snprintf(separator->area.name, sizeof(separator->area.name), "separator");
		separator->area.size_mode = LAYOUT_FIXED;
		separator->area.resize_needed = 1;
		separator->area.on_screen = TRUE;
		separator->area._resize = resize_separator;
		separator->area._compute_desired_size = separator_compute_desired_size;
		separator->area._draw_foreground = draw_separator;
		instantiate_area_gradients(&separator->area);
	}
}

void cleanup_separator()
{
	// Cleanup frontends
	for (int i = 0; i < num_panels; i++) {
		g_list_free_full(panels[i].separator_list, destroy_separator);
		panels[i].separator_list = NULL;
	}

	// Cleanup backends
	g_list_free_full(panel_config.separator_list, destroy_separator);
	panel_config.separator_list = NULL;
}

int separator_compute_desired_size(void *obj)
{
	Separator *separator = (Separator *)obj;
	if (!separator->area.on_screen)
		return 0;

	if (panel_horizontal)
		return separator->thickness + 2 * separator->area.paddingxlr + left_right_border_width(&separator->area);
	else
		return separator->thickness + 2 * separator->area.paddingxlr + top_bottom_border_width(&separator->area);
}

gboolean resize_separator(void *obj)
{
	Separator *separator = (Separator *)obj;
	if (!separator->area.on_screen)
		return FALSE;

	if (panel_horizontal) {
		separator->area.width =
		    separator->thickness + 2 * separator->area.paddingxlr + left_right_border_width(&separator->area);
		separator->length =
		    separator->area.height - 2 * separator->area.paddingy - top_bottom_border_width(&separator->area);
	} else {
		separator->area.height =
		    separator->thickness + 2 * separator->area.paddingxlr + top_bottom_border_width(&separator->area);
		separator->length =
		    separator->area.width - 2 * separator->area.paddingy - left_right_border_width(&separator->area);
	}

	schedule_redraw(&separator->area);
	schedule_panel_redraw();
	return TRUE;
}

void draw_separator_line(void *obj, cairo_t *c);
void draw_separator_dots(void *obj, cairo_t *c);

void draw_separator(void *obj, cairo_t *c)
{
	Separator *separator = (Separator *)obj;

	if (separator->style == SEPARATOR_EMPTY)
		return;
	else if (separator->style == SEPARATOR_LINE)
		draw_separator_line(separator, c);
	else if (separator->style == SEPARATOR_DOTS)
		draw_separator_dots(separator, c);
}

void draw_separator_line(void *obj, cairo_t *c)
{
	Separator *separator = (Separator *)obj;

	if (separator->thickness <= 0)
		return;

	cairo_set_source_rgba(c,
	                      separator->color.rgb[0],
	                      separator->color.rgb[1],
	                      separator->color.rgb[2],
	                      separator->color.alpha);
	cairo_set_line_width(c, separator->thickness);
	cairo_set_line_cap(c, CAIRO_LINE_CAP_ROUND);
	if (panel_horizontal) {
		cairo_move_to(c, separator->area.width / 2.0, separator->area.height / 2.0 - separator->length / 2.0);
		cairo_line_to(c, separator->area.width / 2.0, separator->area.height / 2.0 + separator->length / 2.0);
	} else {
		cairo_move_to(c, separator->area.width / 2.0 - separator->length / 2.0, separator->area.height / 2.0);
		cairo_line_to(c, separator->area.width / 2.0 + separator->length / 2.0, separator->area.height / 2.0);
	}
	cairo_stroke(c);
}

void draw_separator_dots(void *obj, cairo_t *c)
{
	const double PI = 3.14159265359;
	Separator *separator = (Separator *)obj;
	if (separator->thickness <= 0)
		return;

	cairo_set_source_rgba(c,
	                      separator->color.rgb[0],
	                      separator->color.rgb[1],
	                      separator->color.rgb[2],
	                      separator->color.alpha);
	cairo_set_line_width(c, 0);

	int num_circles = separator->length / (1.618 * separator->thickness - 1);
	double spacing = (separator->length - num_circles * separator->thickness) / MAX(1.0, num_circles - 1.0);
	if (spacing > separator->thickness)
		num_circles++;
	spacing = (separator->length - num_circles * separator->thickness) / MAX(1.0, num_circles - 1.0);
	double offset = (panel_horizontal ? separator->area.height : separator->area.width) / 2.0 - separator->length / 2.0;
	if (num_circles == 1)
		offset += spacing / 2.0;
	for (int i = 0; i < num_circles; i++) {
		if (panel_horizontal) {
			cairo_arc(c,
					  separator->area.width / 2.0,
					  offset + separator->thickness / 2.0,
					  separator->thickness / 2.0,
					  0,
					  2 * PI);
		} else {
			cairo_arc(c,
					  offset + separator->thickness / 2.0,
					  separator->area.height / 2.0,
					  separator->thickness / 2.0,
					  0,
					  2 * PI);
		}
		cairo_stroke_preserve(c);
		cairo_fill(c);
		offset += separator->thickness + spacing;
	}
}
