/*** Notes on margins ****

We assume all struts overlap either completely or not at all, otherwise
we would need a complicated recursive algorithm to work out brickwork-like
structures. The original code appeared to add all struts on the same edge
together, but from playing with Gnome it appears that this was wrong and it's
the responsibility of an application setting a strut to add it to any existing
strut. This also makes the wm's job easier.

*****/


#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xmd.h>
#include <ft2build.h>
#include <X11/Xft/Xft.h>
#include "main.h"
#include "hints.h"
#include "margins.h"

#define DBUG_VERBOSE(x)			// DBUG(x)

/* Each data item points to a client's margins field: a set of 12 unsigned
 * longs as per _NET_WM_STRUT_PARTIAL */
static GList *global_margins = NULL;

void getNetWMStrut(Window win, gulong * window_margins)
{
	gulong items_read;
	gulong *data = NULL;

	DBUG_VERBOSE("entering getNetWMStrut");

	memset(window_margins, 0, 12 * sizeof(gulong));
	data = getPropDataWithItems(win, intern_atoms[NET_WM_STRUT_PARTIAL], XA_CARDINAL, &items_read);
	if (data && items_read >= 12)
		memcpy(window_margins, data, 12 * sizeof(gulong));
	else if (data)
	{
		XFree(data);
		data = NULL;
	}
	if (!data)
	{
		data = getPropDataWithItems(win, intern_atoms[NET_WM_STRUT], XA_CARDINAL, &items_read);
		if (data && items_read >= 4)
		{
			memcpy(window_margins, data, 4 * sizeof(gulong));
			if (data[MARGIN_LEFT])
				window_margins[MARGIN_LEFT_END_Y] = display_height;

			if (data[MARGIN_RIGHT])
				window_margins[MARGIN_RIGHT_END_Y] = display_height;

			if (data[MARGIN_BOTTOM])
				window_margins[MARGIN_BOTTOM_END_X] = display_width;

			if (data[MARGIN_TOP])
				window_margins[MARGIN_TOP_END_X] = display_width;
		}
	}
	if (data)
	{
		global_margins = g_list_append(global_margins, window_margins);
		XFree(data);
	}
}

void delNetWMStrut(Client * c)
{
	DBUG_VERBOSE("entering delNetWMStrut");
	global_margins = g_list_remove(global_margins, c->margins);
}

inline static int ranges_overlap(int start0, int end0, int start1, int end1)
{
	return (start0 > start1 && start0 < end1) ||
		(end0 > start1 && end0 < end1) || (start0 <= start1 && end0 >= end1);
}

static int margin_overlaps_range(int side, int start, int end, gulong * margin)
{
	if (margin[side])
	{
		int start1, end1;
		switch (side)
		{
			case MARGIN_BOTTOM:
				start1 = margin[MARGIN_BOTTOM_START_X];
				end1 = margin[MARGIN_BOTTOM_END_X];
			break;
			case MARGIN_TOP:
				start1 = margin[MARGIN_TOP_START_X];
				end1 = margin[MARGIN_TOP_END_X];
			break;
			case MARGIN_LEFT:
				start1 = margin[MARGIN_LEFT_START_Y];
				end1 = margin[MARGIN_LEFT_END_Y];
			break;
			case MARGIN_RIGHT:
				start1 = margin[MARGIN_RIGHT_START_Y];
				end1 = margin[MARGIN_RIGHT_END_Y];
			break;
		}
		return ranges_overlap(start, end, start1, end1);
	}
	return 0;
}

/*
static int margins_overlap(int side, gulong *m1, gulong *m2)
{
    if (m1[side] && m2[side])
    {
	switch (side)
	{
	    case MARGIN_BOTTOM:
		return ranges_overlap(
		    m1[MARGIN_BOTTOM_START_X],
		    m1[MARGIN_BOTTOM_END_X],
		    m2[MARGIN_BOTTOM_START_X],
		    m2[MARGIN_BOTTOM_END_X]);
	    case MARGIN_TOP:
		return ranges_overlap(
		    m1[MARGIN_TOP_START_X],
		    m1[MARGIN_TOP_END_X],
		    m2[MARGIN_TOP_START_X],
		    m2[MARGIN_TOP_END_X]);
	    case MARGIN_LEFT:
		return ranges_overlap(
		    m1[MARGIN_LEFT_START_Y],
		    m1[MARGIN_LEFT_END_Y],
		    m2[MARGIN_LEFT_START_Y],
		    m2[MARGIN_LEFT_END_Y]);
	    case MARGIN_RIGHT:
		return ranges_overlap(
		    m1[MARGIN_RIGHT_START_Y],
		    m1[MARGIN_RIGHT_END_Y],
		    m2[MARGIN_RIGHT_START_Y],
		    m2[MARGIN_RIGHT_END_Y]);
	}
    }
    return 0;
}
*/
GList *global_margins_first(void)
{
	return global_margins;
}

/* See notes at top of file */
int getMargin(int side, int start, int end)
{
	GList *mlink;

	/* Final width of margin - return value */
	gulong final_width = 0;

	for (mlink = global_margins_first(); mlink; mlink = g_list_next(mlink))
	{
		if (margin_overlaps_range(side, start, end, mlink->data))
		{
			gulong this_width = ((gulong *) mlink->data)[side];

			if (this_width > final_width)
				final_width = this_width;
		}
	}
	return (int) final_width;
}

int getMarginWithFrame(Client * c, int side, int start, int end)
{

	switch (side)
	{
		case MARGIN_LEFT:
		case MARGIN_RIGHT:
			start -= frameTop(c);
			end += frameBottom(c);
			break;
		case MARGIN_TOP:
		case MARGIN_BOTTOM:
			start -= frameLeft(c);
			end += frameRight(c);
			break;
	}
	return getMargin(side, start, end);
}

