/* Libvisual - The audio visualisation framework.
 * 
 * Copyright (C) 2004, 2005, 2006 Dennis Smit <ds@nerds-incorporated.org>
 *
 * Authors: Dennis Smit <ds@nerds-incorporated.org>
 *
 * $Id: lv_palette.c,v 1.21 2006/01/22 13:23:37 synap Exp $
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1
 * 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "lv_common.h"
#include "lv_palette.h"

static int palette_dtor (VisObject *object);

static int palette_dtor (VisObject *object)
{
	VisPalette *pal = VISUAL_PALETTE (object);

	if (pal->colors != NULL)
		visual_palette_free_colors (pal);

	pal->colors = NULL;

	return VISUAL_OK;
}

/**
 * @defgroup VisPalette VisPalette
 * @{
 */

/**
 * Creates a new VisPalette.
 *
 * @return A newly allocated VisPalette.
 */
VisPalette *visual_palette_new (int ncolors)
{
	VisPalette *pal;

	pal = visual_mem_new0 (VisPalette, 1);

	visual_palette_init (pal);

	/* Do the VisObject initialization */
	visual_object_set_allocated (VISUAL_OBJECT (pal), TRUE);
	visual_object_ref (VISUAL_OBJECT (pal));

	visual_palette_allocate_colors (pal, ncolors);

	return pal;
}

/**
 * Initializes a VisPalette, this should not be used to reset a VisPalette.
 * The resulting initialized VisPalette is a valid VisObject even if it was not allocated.
 * Keep in mind that VisPalette structures that were created by visual_palette_new() should not
 * be passed to visual_palette_init().
 *
 * @see visual_palette_new
 *
 * @param pal Pointer to the VisPalette which needs to be initialized.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PALETTE_NULL on failure.
 */
int visual_palette_init (VisPalette *pal)
{
	visual_log_return_val_if_fail (pal != NULL, -VISUAL_ERROR_PALETTE_NULL);

	/* Do the VisObject initialization */
	visual_object_clear (VISUAL_OBJECT (pal));
	visual_object_set_dtor (VISUAL_OBJECT (pal), palette_dtor);
	visual_object_set_allocated (VISUAL_OBJECT (pal), FALSE);

	/* Reset the VisPalette data */
	pal->ncolors = 0;
	pal->colors = NULL;

	return VISUAL_OK;
}

/**
 * Copies the colors from one VisPalette to another.
 *
 * @param dest Pointer to the destination VisPalette.
 * @param src Pointer to the source VisPalette from which colors are copied into the destination VisPalette.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PALETTE_NULL or -VISUAL_ERROR_PALETTE_SIZE on failure.
 */
int visual_palette_copy (VisPalette *dest, VisPalette *src)
{
	visual_log_return_val_if_fail (dest != NULL, -VISUAL_ERROR_PALETTE_NULL);
	visual_log_return_val_if_fail (src != NULL, -VISUAL_ERROR_PALETTE_NULL);
	visual_log_return_val_if_fail (dest->ncolors == src->ncolors, -VISUAL_ERROR_PALETTE_SIZE);

	visual_mem_copy (dest->colors, src->colors, sizeof (VisColor) * dest->ncolors);

	return VISUAL_OK;
}

/**
 * Allocate an amount of colors for a VisPalette.
 *
 * @param pal Pointer to the VisPalette for which colors are allocated.
 * @param ncolors The number of colors allocated for the VisPalette.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PALETTE_NULL on failure.
 */
int visual_palette_allocate_colors (VisPalette *pal, int ncolors)
{
	visual_log_return_val_if_fail (pal != NULL, -VISUAL_ERROR_PALETTE_NULL);

	pal->colors = visual_mem_new0 (VisColor, ncolors);
	pal->ncolors = ncolors;

	return VISUAL_OK;
}

/**
 * Frees allocated colors from a VisPalette.
 * 
 * @param pal Pointer to the VisPalette from which colors need to be freed.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PALETTE_NULL on failure.
 */
int visual_palette_free_colors (VisPalette *pal)
{
	visual_log_return_val_if_fail (pal != NULL, -VISUAL_ERROR_PALETTE_NULL);

	if (pal->colors != NULL)
		visual_mem_free (pal->colors);

	pal->colors = NULL;
	pal->ncolors = 0;

	return VISUAL_OK;
}

/**
 * This function is capable of morphing from one palette to another.
 *
 * @param dest Pointer to the destination VisPalette, this is where the result of the morph
 * 	  is put.
 * @param src1 Pointer to a VisPalette that acts as the first source for the morph.
 * @param src2 Pointer to a VisPalette that acts as the second source for the morph.
 * @param rate Value that sets the rate of the morph, which is valid between 0 and 1.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PALETTE_NULL or -VISUAL_ERROR_PALETTE_SIZE on failure.
 */
int visual_palette_blend (VisPalette *dest, VisPalette *src1, VisPalette *src2, float rate)
{
	int i;

	visual_log_return_val_if_fail (dest != NULL, -VISUAL_ERROR_PALETTE_NULL);
	visual_log_return_val_if_fail (src1 != NULL, -VISUAL_ERROR_PALETTE_NULL);
	visual_log_return_val_if_fail (src2 != NULL, -VISUAL_ERROR_PALETTE_NULL);

	if (src1->ncolors != src2->ncolors)
		return -VISUAL_ERROR_PALETTE_SIZE;

	if (dest->ncolors != src1->ncolors)
		return -VISUAL_ERROR_PALETTE_SIZE;

	for (i = 0; i < dest->ncolors; i++) {
		dest->colors[i].r = src1->colors[i].r + ((src2->colors[i].r - src1->colors[i].r) * rate);
		dest->colors[i].g = src1->colors[i].g + ((src2->colors[i].g - src1->colors[i].g) * rate);
		dest->colors[i].b = src1->colors[i].b + ((src2->colors[i].b - src1->colors[i].b) * rate);
	}

	return VISUAL_OK;
}

/**
 * Can be used to cycle through the colors of a VisPalette and blend between elements. The rate is from 0.0 to number of
 * VisColors in the VisPalette. The VisColor is newly allocated so you have to unref it. The last VisColor in the VisPalette is
 * morphed with the first.
 *
 * @param pal Pointer to the VisPalette in which the VisColors are cycled.
 * @param rate Selection of the VisColor from the VisPalette, goes from 0.0 to number of VisColors in the VisPalette
 * 	and morphs between colors if needed.
 *
 * @return A new VisColor, possibly a morph between two VisColors, NULL on failure.
 */
VisColor *visual_palette_color_cycle (VisPalette *pal, float rate)
{
	VisColor *color, *tmp1, *tmp2;
	int irate = (int) rate;
	unsigned char alpha;
	float rdiff = rate - irate;

	visual_log_return_val_if_fail (pal != NULL, NULL);

	irate = irate % pal->ncolors;
	alpha = rdiff * 255;

	color = visual_color_new ();

	/* If rate is exactly an item, return that item */
	if (rdiff == 0) {
		visual_color_copy (color, &pal->colors[irate]);

		return color;
	}

	tmp1 = &pal->colors[irate];

	if (irate == pal->ncolors - 1)
		tmp2 = &pal->colors[0];
	else
		tmp2 = &pal->colors[irate + 1];

	color->r = ((alpha * (tmp1->r - tmp2->r) >> 8) + tmp2->r);
	color->g = ((alpha * (tmp1->g - tmp2->g) >> 8) + tmp2->g);
	color->b = ((alpha * (tmp1->b - tmp2->b) >> 8) + tmp2->b);

	return color;
}

int visual_palette_find_color (VisPalette *pal, VisColor *color)
{
	int i;

	for (i = 0; i < pal->ncolors; i++) {
		if (visual_color_compare (&pal->colors[i], color) == TRUE)
			return i;
	}

	return -1;
}

/**
 * @}
 */

