/*
 * Animation plugin for compiz/beryl
 *
 * animation.c
 *
 * Copyright : (C) 2006 Erkin Bahceci
 * E-mail    : erkinbah@gmail.com
 *
 * Based on Wobbly and Minimize plugins by
 *           : David Reveman
 * E-mail    : davidr@novell.com>
 *
 * 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.
 */

#include "animation-internal.h"

// =================  Option Related Functions  =================

CompOptionValue *
animGetOptVal(AnimScreen *as,
	      AnimWindow *aw,
	      int optionId)
{
    OptionSet *os =
	&as->eventOptionSets[aw->curWindowEvent]->sets[aw->curAnimSelectionRow];
    IdValuePair *pair = os->pairs;

    int i;
    for (i = 0; i < os->nPairs; i++, pair++)
	if (pair->id == optionId)
	    return &pair->value;
    return &as->opt[optionId].value;
}

inline Bool
animGetB(AnimScreen *as,
	 AnimWindow *aw,
	 int optionId)
{
    return animGetOptVal(as, aw, optionId)->b;
}

inline int
animGetI(AnimScreen *as,
	 AnimWindow *aw,
	 int optionId)
{
    return animGetOptVal(as, aw, optionId)->i;
}

inline float
animGetF(AnimScreen *as,
	 AnimWindow *aw,
	 int optionId)
{
    return animGetOptVal(as, aw, optionId)->f;
}

inline char *
animGetS(AnimScreen *as,
	 AnimWindow *aw,
	 int optionId)
{
    return animGetOptVal(as, aw, optionId)->s;
}

inline unsigned short *
animGetC(AnimScreen *as,
	 AnimWindow *aw,
	 int optionId)
{
    return animGetOptVal(as, aw, optionId)->c;
}

static
void freeSingleEventOptionSets(OptionSets *oss)
{
    int j;
    for (j = 0; j < oss->nSets; j++)
	if (oss->sets[j].pairs)
	    free(oss->sets[j].pairs);
    free (oss->sets);
    oss->sets = NULL;
}

void
freeAllOptionSets(OptionSets **eventsOss)
{
    int i;
    for (i = 0; i < NUM_EVENTS; i++)
    {
	OptionSets *oss = eventsOss[i];
	if (!oss->sets)
	    continue;
	freeSingleEventOptionSets(oss);
    }
    free (eventsOss[WindowEventOpen]);
    free (eventsOss[WindowEventClose]);
    free (eventsOss[WindowEventMinimize]);
    free (eventsOss[WindowEventFocus]);
    free (eventsOss[WindowEventShade]);

    for (i = 0; i < NUM_EVENTS; i++)
	eventsOss[i] = NULL;
}

static void
updateOptionSet(CompScreen *s, OptionSet *os, char *optNamesValuesOrig)
{
    ANIM_SCREEN(s);
    int len = strlen(optNamesValuesOrig);
    char *optNamesValues = calloc(len + 1, 1);

    // Find the first substring with no spaces in it
    sscanf(optNamesValuesOrig, " %s ", optNamesValues);
    if (strlen(optNamesValues) == 0)
    {
	free(optNamesValues);
	return;
    }
    // Backup original, since strtok is destructive
    strcpy(optNamesValues, optNamesValuesOrig);

    char *name;
    char *nameTrimmed = calloc(len + 1, 1);
    char *valueStr = NULL;
    char *betweenPairs = ",";
    char *betweenOptVal = "=";

    // Count number of pairs
    char *pairToken = optNamesValuesOrig;
    int nPairs = 1;
	
    while ((pairToken = strchr(pairToken, betweenPairs[0])))
    {
	pairToken++; // skip delimiter
	nPairs++;
    }

    if (os->pairs)
	free(os->pairs);
    os->pairs = calloc(nPairs, sizeof(IdValuePair));
    if (!os->pairs)
    {
	os->nPairs = 0;
	free(optNamesValues);
	free(nameTrimmed);
	compLogMessage (s->display, "animation", CompLogLevelError,
			"Not enough memory");
	return;
    }
    os->nPairs = nPairs;

    // Tokenize pairs
    name = strtok(optNamesValues, betweenOptVal);
    int errorNo = -1;
    int i;
    for (i = 0; name && i < nPairs; i++)
    {
	errorNo = 0;
	if (strchr(name, betweenPairs[0])) // handle "a, b=4" case
	{
	    errorNo = 1;
	    break;
	}

	sscanf(name, " %s ", nameTrimmed);
	if (strlen(nameTrimmed) == 0)
	{
	    errorNo = 2;
	    break;
	}
	valueStr = strtok(NULL, betweenPairs);
	if (!valueStr)
	{
	    errorNo = 3;
	    break;
	}

	CompOption *o;
	int j;

	// Skip non-effect options
	for (j = 0; j < ANIM_SCREEN_OPTION_NUM; j++)
	{
	    o = &as->opt[j];
	    if (strcasecmp(nameTrimmed, o->name) == 0)
		break;
	}
	if (j == ANIM_SCREEN_OPTION_NUM) // no match
	{
	    errorNo = 4;
	    break;
	}
	else if (j < NUM_NONEFFECT_OPTIONS)
	{
	    errorNo = 5;
	    break;
	}

	CompOptionValue v;

	os->pairs[i].id = j;
	int valueRead = -1;
	switch (o->type)
	{
	case CompOptionTypeBool:
	    valueRead = sscanf(valueStr, " %d ", &os->pairs[i].value.b);
	    break;
	case CompOptionTypeInt:
	    valueRead = sscanf(valueStr, " %d ", &v.i);
	    if (valueRead > 0)
	    {
		// Store option's original value
		int backup = o->value.i;
		if (compSetIntOption (o, &v))
		    os->pairs[i].value = v;
		else
		    errorNo = 7;
		// Restore value
		o->value.i = backup;
	    }
	    break;
	case CompOptionTypeFloat:
	    valueRead = sscanf(valueStr, " %f ", &v.f);
	    if (valueRead > 0)
	    {
		// Store option's original value
		float backup = o->value.f;
		if (compSetFloatOption (o, &v))
		    os->pairs[i].value = v;
		else
		    errorNo = 7;
		// Restore value
		o->value.f = backup;
	    }
	    break;
	case CompOptionTypeString:
	    v.s = calloc (strlen(valueStr) + 1, 1); // TODO: not freed
	    if (!v.s)
	    {
		compLogMessage (s->display, "animation", CompLogLevelError,
				"Not enough memory");
		return;
	    }
	    strcpy(v.s, valueStr);
	    valueRead = 1;
	    break;
	case CompOptionTypeColor:
	{
	    unsigned int c[4];
	    valueRead = sscanf (valueStr, " #%2x%2x%2x%2x ",
				&c[0], &c[1], &c[2], &c[3]);
	    if (valueRead == 4)
	    {
		CompOptionValue * pv = &os->pairs[i].value;
		int j;
		for (j = 0; j < 4; j++)
		    pv->c[j] = c[j] << 8 | c[j];
	    }
	    else
		errorNo = 6;
	    break;
	}
	default:
	    break;
	}
	if (valueRead == 0)
	    errorNo = 6;
	if (errorNo > 0)
	    break;
	// If valueRead is -1 here, then it must be a
	// non-(int/float/string) option, which is not supported yet.
	// Such an option doesn't currently exist anyway.

	errorNo = -1;
	name = strtok(NULL, betweenOptVal);
    }

    if (i < nPairs)
    {
	switch (errorNo)
	{
	case -1:
	case 2:
	    compLogMessage (s->display, "animation", CompLogLevelError,
			    "Option name missing in \"%s\"",
			    optNamesValuesOrig);
	    break;
	case 1:
	case 3:
	    compLogMessage (s->display, "animation", CompLogLevelError,
			    "Option value missing in \"%s\"",
			    optNamesValuesOrig);
	    break;
	case 4:
	    compLogMessage (s->display, "animation", CompLogLevelError,
			    "Unknown option \"%s\" in \"%s\"",
			    nameTrimmed, optNamesValuesOrig);
	    break;
	case 5:
	    compLogMessage (s->display, "animation", CompLogLevelError,
			    "Option \"%s\" cannot be changed in option strings.",
			    nameTrimmed);
	    break;
	case 6:
	    compLogMessage (s->display, "animation", CompLogLevelError,
			    "Invalid value \"%s\" in \"%s\"",
			    valueStr, optNamesValuesOrig);
	    break;
	case 7:
	    compLogMessage (s->display, "animation", CompLogLevelError,
			    "Value \"%s\" out of range in \"%s\"",
			    valueStr, optNamesValuesOrig);
	    break;
	default:
	    break;
	}
	free(os->pairs);
	os->pairs = 0;
	os->nPairs = 0;
    }
    free(optNamesValues);
    free(nameTrimmed);
}

void
updateOptionSets(CompScreen *s,
		 OptionSets *oss,
		 CompListValue *listVal)
{
    int n = listVal->nValue;

    if (oss->sets)
	freeSingleEventOptionSets(oss);

    oss->sets = calloc(n, sizeof(OptionSet));
    if (!oss->sets)
    {
	compLogMessage (s->display, "animation", CompLogLevelError,
			"Not enough memory");
	return;
    }
    oss->nSets = n;

    int i;
    for (i = 0; i < n; i++)
	updateOptionSet(s, &oss->sets[i], listVal->value[i].s);
}


syntax highlighted by Code2HTML, v. 0.9.1