/* 
 * Oroborus Window Manager
 *
 * Copyright (C) 2001 Ken Lynch
 * Copyright (C) 2002 Stefan Pfetzing
 *
 * OroboROX Window Manager
 * 
 * Copyright (C) 2004 Guido Schimmels
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 */

#include "config.h"
#include <glib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xmd.h>
#include <ft2build.h>
#include <X11/Xft/Xft.h>

#include "keyboard.h"
#include "pixmap.h"
#include "hints.h"
#include "margins.h"
#include "client.h"
#include "mouse.h"
#include "hints.h"
#include "settings.h"
#include "stacking.h"
#include "main.h"
#include "workspaces.h"
#include "events.h"
#include "focus.h"
#include "xerror.h"

extern Window last_focus[9];

long workspace;
int workspace_count, wrap_workspaces, cycle_workspaces;

int showing_desktop = FALSE;


Bool workspaces_match(Client * c, int ws)
{
	return (c->win_workspace == ws) || (c->win_workspace == -1) || (c->state & STATE_STICKY);
}

void setNetWorkarea(void)
{
	struct workarea {
		gulong left, top, right, bottom;
	} *workarea;
	int i;
	GList *mlink;
	int width, height;

	if (workspace_count <= 0)
		return;

	width = display_width;
	height = display_height;

	workarea = g_new(struct workarea, workspace_count);
	workarea[0].left = workarea[0].top = 0;
	workarea[0].right = width;
	workarea[0].bottom = height;

	for (mlink = global_margins_first(); mlink; mlink = g_list_next(mlink))
	{
		gulong *margin = mlink->data;

		if (workarea[0].left < margin[MARGIN_LEFT])
			workarea[0].left = margin[MARGIN_LEFT];
		if (workarea[0].top < margin[MARGIN_TOP])
			workarea[0].left = margin[MARGIN_TOP];
		if (workarea[0].right > width - margin[MARGIN_RIGHT])
			workarea[0].right = width - margin[MARGIN_RIGHT];
		if (workarea[0].bottom > height - margin[MARGIN_BOTTOM])
			workarea[0].bottom = height - margin[MARGIN_BOTTOM];
	}

	for (i = 1; i < workspace_count; i++)
		memcpy(workarea + i, workarea, 4 * sizeof(gulong));

	XChangeProperty(dpy, XDefaultRootWindow(dpy),
					intern_atoms[NET_WORKAREA], XA_CARDINAL, 32,
					PropModeReplace, (unsigned char *) workarea, 4 * workspace_count);

	g_free(workarea);
}

void client_set_workspace(Client * c, int ws)
{
	g_return_if_fail(c);

	dbg("%s: %s\n",__func__, c->class.res_name);

	if (c->win_workspace == ws)
		return;

	if (ws > 8)
		ws = 0;
	if (ws < 0)
		ws = -1;

	set_property_card32(c->window, intern_atoms[NET_WM_DESKTOP], ws);
	c->win_workspace = ws;
	if (get_wm_state(c->window) == NormalState)
	{
		if ((c->state & STATE_STICKY) || (ws == workspace))
			clientShow(c, FALSE);
		else
			clientHide(c, FALSE);
	}
}

static int validated_workspace(int ws)
{
	if (wrap_workspaces)
	{
		if (ws < 0)
			ws = workspace_count - 1;
		if (ws >= workspace_count)
			ws = 0;
	}

	if ((ws >= workspace_count) || (ws == workspace))
		ws = -1;

	return ws;
}

#ifdef VERBOSE
void show_hidden_clients(void)
{
	dbg("Show hidden clients:\n");

	GList *l;
	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;

		if (c->state & STATE_HIDDEN)
			dbg("%s/%s\n", c->class.res_name, c->class.res_class);
	}
}
#endif

static Window goto_workspace(int ws)
{
	dbg("goto_to_workspace()");

	Window focuswin = root;
	Window focusws = last_focus[ws];
	
	// we can't know which window ends up under the pointer,
	// until all windows have been remapped, so return root
	// window fallback and figure out later
	if (focus_policy == FOCUS_UNIX)
		focusws = root; 
		
	GList *l;

	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;

		if ((c->win_workspace != -1) && !(c->state & STATE_STICKY))
		{
			if (c->win_workspace == ws)
			{
				if (!(c->state & STATE_HIDDEN))
				{
					{
						//if (map_state(c->window) == IsUnmapped)
						{
							XMapWindow(dpy, c->frame);
							XMapWindow(dpy, c->window);
						}
						if (focusws == c->window)
						{
							set_input_focus(c->window);
							focuswin = get_input_focus();
						}
						frameDraw(c);
					}
				}
			}
			else
			{	/* unmap all clients on other workspaces */

				{
					if (map_state(c->window) != IsUnmapped)
					{
						c->ignore_unmap++;
						XUnmapWindow(dpy, c->window);
						XUnmapWindow(dpy, c->frame);	
					}
					c->decor_state = INACTIVE;	
				}
				
			}
		}
	}
	set_property_card32(root, intern_atoms[NET_CURRENT_DESKTOP], workspace = ws);
	if (showing_desktop)
		unshow_desktop();

	return focuswin;
}

void switch_to_workspace(int ws)
{
	DBUG("switch_to_workspace()");

	ws = validated_workspace(ws);

	if (ws >= 0)
	{
		cancel_pending_raise();
		if (goto_workspace(ws) == root)
			focus_workspace();
	}	
}

void show_desktop(void)
{
	GList *l;

	set_property_card32(root, intern_atoms[NET_SHOWING_DESKTOP], 1);
	showing_desktop = TRUE;
	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;
		if (client_is_viewable_or_obscured(c)  && 
			!(c->type & (WINDOW_DESKTOP|WINDOW_DOCK)))
		{
			c->ignore_unmap+=1;
			c->decor_state = INACTIVE;
			XUnmapWindow(dpy, c->window);
			XUnmapWindow(dpy, c->frame);
		}
	}

	set_input_focus(root);
}

void unshow_desktop(void)
{
	GList *l;

	set_property_card32(root, intern_atoms[NET_SHOWING_DESKTOP], 0);
	showing_desktop = FALSE;
	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;

		if (client_is_viewable_or_obscured(c))
		{
			XMapWindow(dpy, c->window);
			XMapWindow(dpy, c->frame);
			// do *not* decrease ignore_unmap here!!
		}
	}

	focus_workspace();
}

void move_client_to_workspace(Client * c, int ws)
{
	DBUG("move_client_to_workspace()");

	ws = validated_workspace(ws);
	if (ws >= 0)
	{
		goto_workspace(ws);
		c->win_workspace = ws;
		set_property_card32(c->window, intern_atoms[NET_WM_DESKTOP], ws);
		XMapWindow(dpy, c->window);
		XMapWindow(dpy, c->frame);
		apply_focus_policy(c);
	}
}

void workspaceSetCount(int count)
{
	DBUG("workspaceSetCount()");

	if (count < 1)
		count = 1;
	if (count == workspace_count)
		return;

	set_property_card32(root, intern_atoms[NET_NUMBER_OF_DESKTOPS], count);
	workspace_count = count;

	GList *l;

	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;

		if (c->win_workspace > count - 1)
			client_set_workspace(c, count - 1);
	}

	if (workspace > count - 1)
		switch_to_workspace(count - 1);

	setNetWorkarea();
}

