/*
 * Copyright © 2005 Novell, Inc.
 *
 * 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
 * of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include <stdlib.h>
#include <string.h>

#include <beryl.h>

#include <math.h>

#define FADE_SPEED_DEFAULT    7.0f
#define FADE_SPEED_MIN        0.1f
#define FADE_SPEED_MAX       10.0f
#define FADE_SPEED_PRECISION  0.1f

#define URGENT_SPEED_DEFAULT 15
#define URGENT_SPEED_MAX 50
#define URGENT_SPEED_MIN 5
#define URGENT_SPEED_O (fs->opt[FADE_SCREEN_OPTION_URGENT_SPEED].value.i*100)

#define URGENT_COUNT_DEFAULT 5
#define URGENT_COUNT_MIN 0
#define URGENT_COUNT_MAX 15
#define URGENT_COUNT_O fs->opt[FADE_SCREEN_OPTION_URGENT_COUNT].value.i

#define URGENT_CENTER_DEFAULT 70
#define URGENT_CENTER_MIN 0
#define URGENT_CENTER_MAX 100
#define URGENT_CENTER_O (fs->opt[FADE_SCREEN_OPTION_URGENT_CENTER].value.i/100.0)

#define URGENT_SWING_DEFAULT 20
#define URGENT_SWING_MIN 0
#define URGENT_SWING_MAX 100
#define URGENT_SWING_O (fs->opt[FADE_SCREEN_OPTION_URGENT_SWING].value.i/100.0)

#define URGENT_DEFAULT TRUE
#define URGENT_O fs->opt[FADE_SCREEN_OPTION_URGENT].value.b

#define FADE_VISUAL_BELL_DEFAULT FALSE

#define FADE_FULLSCREEN_VISUAL_BELL_DEFAULT FALSE

static int displayPrivateIndex;

typedef struct _FadeDisplay
{
	int screenPrivateIndex;
} FadeDisplay;

#define FADE_SCREEN_OPTION_FADE_SPEED          0
#define FADE_SCREEN_OPTION_NUM              1

typedef struct _FadeScreen
{
	int windowPrivateIndex;
	int fadeTime;

	int msec;

	CompOption opt[FADE_SCREEN_OPTION_NUM];

	PreparePaintScreenProc preparePaintScreen;
	PaintWindowProc paintWindow;

} FadeScreen;

typedef struct _FadeWindow
{
	GLushort opacity;
	GLushort brightness;
	GLushort saturation;

	int animStep;
	int count;

	int steps;
} FadeWindow;

/* Steps is in fade window to make sure painting only happens once per step */

#define GET_FADE_DISPLAY(d)                     \
    ((FadeDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define FADE_DISPLAY(d)               \
    FadeDisplay *fd = GET_FADE_DISPLAY (d)

#define GET_FADE_SCREEN(s, fd)                     \
    ((FadeScreen *) (s)->privates[(fd)->screenPrivateIndex].ptr)

#define FADE_SCREEN(s)                            \
    FadeScreen *fs = GET_FADE_SCREEN (s, GET_FADE_DISPLAY (s->display))

#define GET_FADE_WINDOW(w, fs)                         \
    ((FadeWindow *) (w)->privates[(fs)->windowPrivateIndex].ptr)

#define FADE_WINDOW(w)                         \
    FadeWindow *fw = GET_FADE_WINDOW  (w,             \
            GET_FADE_SCREEN  (w->screen,         \
                GET_FADE_DISPLAY (w->screen->display)))

#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))

static void fadeScreenInitOptions(FadeScreen * fs)
{
	CompOption *o;

	o = &fs->opt[FADE_SCREEN_OPTION_FADE_SPEED];
	o->advanced = False;
	o->name = "fade_speed";
	o->group = N_("Misc. options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Fade Speed");
	o->longDesc = N_("How fast the windows Fade in and out.");
	o->type = CompOptionTypeFloat;
	o->value.f = FADE_SPEED_DEFAULT;
	o->rest.f.min = FADE_SPEED_MIN;
	o->rest.f.max = FADE_SPEED_MAX;
	o->rest.f.precision = FADE_SPEED_PRECISION;
}


static CompOption *fadeGetScreenOptions(CompScreen * screen, int *count)
{
	if (screen)
	{
		FADE_SCREEN(screen);

		*count = NUM_OPTIONS(fs);
		return fs->opt;
	}
	else
	{
		FadeScreen *fs = malloc(sizeof(FadeScreen));

		fadeScreenInitOptions(fs);
		*count = NUM_OPTIONS(fs);
		return fs->opt;
	}
}

static Bool
fadeSetScreenOption(CompScreen * screen, char *name, CompOptionValue * value)
{
	CompOption *o;
	int index;

	FADE_SCREEN(screen);

	o = compFindOption(fs->opt, NUM_OPTIONS(fs), name, &index);
	if (!o)
		return FALSE;

	switch (index)
	{
	case FADE_SCREEN_OPTION_FADE_SPEED:
		if (compSetFloatOption(o, value))
		{
			fs->fadeTime = 1000.0f / o->value.f;
			return TRUE;
		}
		break;
	default:
		break;
	}

	return FALSE;
}


static void fadePreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
	CompWindow *w;
	int steps;

	FADE_SCREEN(s);

	fs->msec = msSinceLastPaint;
	steps = (msSinceLastPaint * OPAQUE) / fs->fadeTime;
	if (steps < 12)
		steps = 12;
	for (w = s->windows; w; w = w->next)
	{
		GET_FADE_WINDOW(w, fs)->steps = steps;
	}

	UNWRAP(fs, s, preparePaintScreen);
	(*s->preparePaintScreen) (s, msSinceLastPaint);
	WRAP(fs, s, preparePaintScreen, fadePreparePaintScreen);
}


static Bool
fadePaintWindow(CompWindow * w,
				const WindowPaintAttrib * attrib,
				Region region, unsigned int mask)
{
	CompScreen *s = w->screen;
	Bool status;

	FADE_SCREEN(s);
	FADE_WINDOW(w);

	if (!w->screen->canDoSlightlySaturated)
		fw->saturation = attrib->saturation;

	if (fw->opacity != attrib->opacity ||
		fw->brightness != attrib->brightness ||
		fw->saturation != attrib->saturation)
	{
		GLint opacity;
		GLint brightness;
		GLint saturation;

		opacity = fw->opacity;
		if (attrib->opacity > fw->opacity)
		{
			opacity = fw->opacity + fw->steps;
			if (opacity > attrib->opacity)
				opacity = attrib->opacity;
		}
		else if (attrib->opacity < fw->opacity)
		{
			opacity = fw->opacity - fw->steps;
			if (opacity < attrib->opacity)
				opacity = attrib->opacity;
		}

		brightness = fw->brightness;
		if (attrib->brightness > fw->brightness)
		{
			brightness = fw->brightness + (fw->steps / 12);
			if (brightness > attrib->brightness)
				brightness = attrib->brightness;
		}
		else if (attrib->brightness < fw->brightness)
		{
			brightness = fw->brightness - (fw->steps / 12);
			if (brightness < attrib->brightness)
				brightness = attrib->brightness;
		}

		saturation = fw->saturation;
		if (attrib->saturation > fw->saturation)
		{
			saturation = fw->saturation + (fw->steps / 6);
			if (saturation > attrib->saturation)
				saturation = attrib->saturation;
		}
		else if (attrib->saturation < fw->saturation)
		{
			saturation = fw->saturation - (fw->steps / 6);
			if (saturation < attrib->saturation)
				saturation = attrib->saturation;
		}

		WindowPaintAttrib fAttrib = *attrib;

		fAttrib.opacity = opacity;
		fAttrib.brightness = brightness;
		fAttrib.saturation = saturation;

		UNWRAP(fs, s, paintWindow);
		status = (*s->paintWindow) (w, &fAttrib, region, mask);
		WRAP(fs, s, paintWindow, fadePaintWindow);

		fw->opacity = opacity;
		fw->brightness = brightness;
		fw->saturation = saturation;
		fw->steps = 0;

		if (opacity != attrib->opacity ||
			brightness != attrib->brightness ||
			saturation != attrib->saturation)
			addWindowDamage(w);
	}
	else
	{
		UNWRAP(fs, s, paintWindow);
		status = (*s->paintWindow) (w, attrib, region, mask);
		WRAP(fs, s, paintWindow, fadePaintWindow);
	}

	return status;
}



static Bool fadeInitDisplay(CompPlugin * p, CompDisplay * d)
{
	FadeDisplay *fd;

	fd = malloc(sizeof(FadeDisplay));
	if (!fd)
		return FALSE;

	fd->screenPrivateIndex = allocateScreenPrivateIndex(d);
	if (fd->screenPrivateIndex < 0)
	{
		free(fd);
		return FALSE;
	}

	d->privates[displayPrivateIndex].ptr = fd;

	return TRUE;
}

static void fadeFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	FADE_DISPLAY(d);

	freeScreenPrivateIndex(d, fd->screenPrivateIndex);

	free(fd);
}

static Bool fadeInitScreen(CompPlugin * p, CompScreen * s)
{
	FadeScreen *fs;

	FADE_DISPLAY(s->display);

	fs = malloc(sizeof(FadeScreen));
	if (!fs)
		return FALSE;

	fs->windowPrivateIndex = allocateWindowPrivateIndex(s);
	if (fs->windowPrivateIndex < 0)
	{
		free(fs);
		return FALSE;
	}


	fs->fadeTime = 1000.0f / FADE_SPEED_DEFAULT;

	fadeScreenInitOptions(fs);

	WRAP(fs, s, preparePaintScreen, fadePreparePaintScreen);
	WRAP(fs, s, paintWindow, fadePaintWindow);

	s->privates[fd->screenPrivateIndex].ptr = fs;

	return TRUE;
}

static void fadeFiniScreen(CompPlugin * p, CompScreen * s)
{
	FADE_SCREEN(s);

	freeWindowPrivateIndex(s, fs->windowPrivateIndex);

	UNWRAP(fs, s, preparePaintScreen);
	UNWRAP(fs, s, paintWindow);

	free(fs);
}

static Bool fadeInitWindow(CompPlugin * p, CompWindow * w)
{
	FadeWindow *fw;

	FADE_SCREEN(w->screen);

	fw = malloc(sizeof(FadeWindow));
	if (!fw)
		return FALSE;

	fw->opacity = w->paint.opacity;
	fw->brightness = w->paint.brightness;
	fw->saturation = w->paint.saturation;

	w->privates[fs->windowPrivateIndex].ptr = fw;

	return TRUE;
}

static void fadeFiniWindow(CompPlugin * p, CompWindow * w)
{
	FADE_WINDOW(w);
	free(fw);
}

static Bool fadeInit(CompPlugin * p)
{
	displayPrivateIndex = allocateDisplayPrivateIndex();
	if (displayPrivateIndex < 0)
		return FALSE;

	return TRUE;
}

static void fadeFini(CompPlugin * p)
{
	if (displayPrivateIndex >= 0)
		freeDisplayPrivateIndex(displayPrivateIndex);
}

CompPluginDep fadeDeps[] = {
	{CompPluginRuleAfter, "animation"}
	,
	{CompPluginRuleAfter, "decoration"}
	,
	{CompPluginRuleAfter, "wobbly"}
	,
};

static CompPluginVTable fadeVTable = {
	"fade",
	N_("Fading Windows"),
	N_("Fade in windows when mapped and fade out windows when unmapped"),
	fadeInit,
	fadeFini,
	fadeInitDisplay,
	fadeFiniDisplay,
	fadeInitScreen,
	fadeFiniScreen,
	fadeInitWindow,
	fadeFiniWindow,
	0,							/* GetDisplayOptions */
	0,							/* SetDisplayOption */
	fadeGetScreenOptions,
	fadeSetScreenOption,
	fadeDeps,
	sizeof(fadeDeps) / sizeof(fadeDeps[0]),
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins",
	"effects",
	0,
	0,
	True,
};

CompPluginVTable *getCompPluginInfo(void)
{
	return &fadeVTable;
}


syntax highlighted by Code2HTML, v. 0.9.1