/*-
 * 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"

/*
 * prepare desktop stuff for a shutdown/restart: simply
 * move the current workspace to 0,0; it doesn't matter which
 * desktop because 0,0 is always valid on any desktop.
 */
void desktop_shutdown() {
	screen_t *screen;

	TAILQ_FOREACH(screen, &screen_list, s_list)
		workspace_viewport_move(screen, screen->desktop,
			-screen->desktop->viewx, -screen->desktop->viewy);
}

/*
 * add desktops to a screen.  on error we return -1, leaving as many
 * complete desktops as we could add there, and freeing up partially
 * added one.
 */
int desktop_create(screen_t *screen, int width, int height, int count) {
	desktop_t *desktop;
	int spaces, n;

	/* add count desktops of dim width x height */
	while (count--) {
		desktop = calloc(1, sizeof(desktop_t));
		if (!desktop)
			return -1;

		desktop->num = screen->desktop_count++;
		desktop->width = width;
		desktop->height = height;
		desktop->workspaces = malloc(width * height * sizeof(workspace_t *));
		if (!desktop->workspaces)
			goto free1;
		for (n = 0; n < STACKLAYER_COUNT; n++)
			TAILQ_INIT(&desktop->stacking_list[n]);

		/* get all the workspaces for this desktop */
		for (spaces = 0; spaces < desktop->width * desktop->height; spaces++) {
			desktop->workspaces[spaces] = calloc(1, sizeof(workspace_t));
			if (!desktop->workspaces[spaces])
				goto free2;
			desktop->workspaces[spaces]->desktop = desktop;
			TAILQ_INIT(&desktop->workspaces[spaces]->w_foclist);
		}
		desktop->current_space = desktop->workspaces[0];

		TAILQ_INSERT_TAIL(&screen->s_desklist, desktop, d_list);
	}

	return 0;

free2:
	/* free and exit with error */
	for (n = 0; n < spaces; n++)
		free(desktop->workspaces[n]);
free1:
	free(desktop);
	return -1;
}

/* remove the desktops from a screen */
void desktop_remove(screen_t *screen) {
	desktop_t *desktop, *next;
	int i, numspaces;

	desktop = TAILQ_FIRST(&screen->s_desklist);
	while (desktop) {
		numspaces = desktop->width * desktop->height;
		for (i = 0; i < numspaces; i++)
			free(desktop->workspaces[i]);
		free(desktop->workspaces);
		
		next = TAILQ_NEXT(desktop, d_list);
		free(desktop);
		desktop = next;
	}
	TAILQ_INIT(&screen->s_desklist);
}

/*
 * perform neccesary actions when adding a client to a desktop
 * call _after_ workspace_add_client
 */
void desktop_add_client(client_t *client) {
	stacking_list_add(client);
}

/*
 * remove a client from the desktop it is on
 * call _before_ workspace_rm_client
 */
void desktop_rm_client(client_t *client) {
	stacking_list_rm(client);
}

/* switch the desktop for screen to desk number num */
void desktop_switch(screen_t *screen, int num) {
	client_t *focusthis = NULL;
	client_t *client;
	desktop_t *desktop, *olddesk;

	/* find the desktop to switch to */
	TAILQ_FOREACH(desktop, &screen->s_desklist, d_list)
		if (desktop->num == num)
			goto gotdesk;
	return;

gotdesk:
	if (screen->desktop == desktop)
		return;

	/* unmap clients on the current desktop, and map them for the new */
	LIST_FOREACH(client, &client_list, c_list)
		if (client->state == NormalState && client->workspace) {
			if (client->workspace->desktop == screen->desktop) {
				/*
				 * handle windows that are stuck to the glass.
				 * the idea is to preserve focus settings, so
				 * if a sticky window is focused it'll stay
				 * focused, and if it isn't focused it wont
				 * get the focus.  this means we don't need
				 * to check flags.nofocus, because if it's
				 * nofocus it will never have focus in the
				 * first place.
				 */
				if (client->flags.sticky) {
					if (screen->desktop->current_space->focused == client
							&& focusthis == NULL)
						focusthis = client;
					desktop_rm_client(client);
					workspace_rm_client(client);
					workspace_add_client(desktop->current_space, client);
					desktop_add_client(client);
				} else
					XUnmapWindow(display, client->frame);
			} else if (client->workspace->desktop == desktop)
				XMapWindow(display, client->frame);
		}

	/* set these up before doing focus stuff */
	olddesk = screen->desktop;
	screen->desktop = desktop;

	/* now set the focus */
	if (focusthis)
		focus_client(focusthis);
	else if (desktop->current_space->focused)
		focus_client(desktop->current_space->focused);
	else
		focus_none(screen);

	/* flush window stacking out to the server */
	stacking_flush(desktop);

	/* let plugins know about the desktop change */
	plugin_desktop_change(screen, olddesk);
}
