/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "wm.h"

/* default dgroup, in case a dgroup isn't made in the rcfile */
dgroup_t dgroup_empty = { "EMPTY" /* compiler 0s the rest */ };

/* the list of decoration groups */
static SLIST_HEAD(, dgroup) dgroup_list	= { &dgroup_empty };

/* free all dgroup allocated memory */
void dgroup_shutdown() {
	dgroup_t *dgroup, *next;

	/*
	 * free the entire list of dgroup_t's; decor_t's are
	 * freed elsewhere (decor_shutdown).  we loop till we
	 * get to dgroup_empty, which is statically allocated
	 * and doesn't need to be freed.  dgroup_empty is always
	 * on the very end of dgroup_list.
	 */
	dgroup = SLIST_FIRST(&dgroup_list);
	while (dgroup != &dgroup_empty) {
		free(dgroup->ident);
		free(dgroup->decor);
	
		next = SLIST_NEXT(dgroup, dg_list);
		free(dgroup);
		dgroup = next;
	}
}

/* add a decoration group */
dgroup_t *dgroup_add(char *ident) {
	dgroup_t *dgroup;

	dgroup = calloc(1, sizeof(dgroup_t));
	if (!dgroup)
		return NULL;
	dgroup->ident = ident;
	SLIST_INSERT_HEAD(&dgroup_list, dgroup, dg_list);

	return dgroup;
}

/* find a dgroup by identifier; used in the rcfile parser */
dgroup_t *dgroup_ident(char *ident) {
	dgroup_t *dgroup;

	SLIST_FOREACH(dgroup, &dgroup_list, dg_list)
		if (strcmp(dgroup->ident, ident) == 0)
			return dgroup;
	return NULL;
}

/* add a decoration unit to a dgroup */
int dgroup_add_decor(dgroup_t *dgroup, decor_t *decor) {
	decor_t **tmp;

	dgroup->decor_count++;

	tmp = realloc(dgroup->decor, dgroup->decor_count * sizeof(decor_t *));
	if (!tmp)
		return -1;
	dgroup->decor = tmp;
	dgroup->decor[dgroup->decor_count - 1] = decor;

	return 0;
}

/* learn spacing information for all our decoration groups */
void dgroup_learn_space() {
	dgroup_t *dgroup;
	int i;

	SLIST_FOREACH(dgroup, &dgroup_list, dg_list)
		for (i = 0; i < dgroup->decor_count; i++) {
			if (dgroup->decor[i]->edge == DE_TOP) {
				if (dgroup->top_space < dgroup->decor[i]->pixmap->height)
					dgroup->top_space = dgroup->decor[i]->pixmap->height;
			} else if (dgroup->decor[i]->edge == DE_LEFT) {
				if (dgroup->left_space < dgroup->decor[i]->pixmap->width)
					dgroup->left_space = dgroup->decor[i]->pixmap->width;
			} else if (dgroup->decor[i]->edge == DE_RIGHT) {
				if (dgroup->right_space < dgroup->decor[i]->pixmap->width)
					dgroup->right_space = dgroup->decor[i]->pixmap->width;
			} else if (dgroup->decor[i]->edge == DE_BOTTOM) {
				if (dgroup->bottom_space < dgroup->decor[i]->pixmap->height)
					dgroup->bottom_space = dgroup->decor[i]->pixmap->height;
			}
		}
}

/* switch the dgroup used by a client */
void dgroup_switch(client_t *client, dgroup_t *dgroup) {
	XRectangle rect;

	decor_undecor(client);
	if (!dgroup) {
		if (SLIST_NEXT(client->dgroup, dg_list))
			client->dgroup = SLIST_NEXT(client->dgroup, dg_list);
		else
			client->dgroup = SLIST_FIRST(&dgroup_list);
	} else
		client->dgroup = dgroup;

	/*
	 * the meat of client_sizeframe, we don't actually call it to aviod having
	 * to unneccesarily reshape the window's decor twice.
	 */
	XMoveResizeWindow(display, client->frame, client->x, client->y,
		client->width + client->dgroup->left_space
		+ client->dgroup->right_space,
		client->height + client->dgroup->top_space
		+ client->dgroup->bottom_space);
	XMoveWindow(display, client->window, client->dgroup->left_space,
		client->dgroup->top_space);

	/* fill in any old shaping */
	rect.x = 0;
	rect.y = 0;
	rect.width = client->width + client->dgroup->left_space
		+ client->dgroup->right_space;
	rect.height = client->height + client->dgroup->top_space
		+ client->dgroup->bottom_space;
	XShapeCombineRectangles(display, client->frame, ShapeBounding,
		0, 0, &rect, 1, ShapeSet, Unsorted);

	/* make the new decoration */
	decor_decorate(client);
	if (client->flags.shaped)
		client_shape(client);

	/* if the decor should be focused...focus it */
	if (client_focused == client)
		decor_focus(client);
}
