// 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;
    Panel *panel = (Panel*)separator->area.panel;
    if (!separator->area.on_screen)
        return 0;

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

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

    if (panel_horizontal) {
        separator->area.width =
            separator->thickness + 2 * separator->area.paddingxlr * panel->scale + left_right_border_width(&separator->area);
        separator->length =
            separator->area.height - 2 * separator->area.paddingy * panel->scale - top_bottom_border_width(&separator->area);
    } else {
        separator->area.height =
            separator->thickness + 2 * separator->area.paddingxlr * panel->scale + top_bottom_border_width(&separator->area);
        separator->length =
            separator->area.width - 2 * separator->area.paddingy * panel->scale - 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;
    }
}
