
#include "livido.h"

// used for printf 
#include <stdio.h>       




// Functions that are supposed to be used from host, since
// plugins already know them 

/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//////////////// FUNCTIONS TO COUNT LIST MEMBERS ////////////////
/// OF CHANNEL TEMPLATES, CHANNELS AND PARAMETER DESCRIPTIONS ///
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////

// Passing it pointer to begining of channel templates
// It returns their total number
int lvd_num_channel_templates(livido_channel_template_t *chan) {
	int c = 0;
	while (chan[c].palettes != NULL) 
		c++;
	return c; 
}

// Passing it pointer to begining of channels
// It returns their total number
int lvd_num_channels(livido_channel_t *chan) {
	int c = 0;
	while (chan[c].based_on_template != NULL) c++;
	return c; 
}

// Passing it pointer to begining of parameter descriptions
// It returns their total number
int lvd_num_parameters(livido_parameter_template_t *params) {
	int c = 0;
	while (params[c].name != NULL) c++;
	return c; 
}

// Passing it pointer to begining of the stringlist choices
// It returns their total number of choices
int lvd_num_stringlist(lvd_type_string *st) {
	int c = 0;
	while (st[c][0] != 0) c++;
	return c; 
}




/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//////////////// ALLOCATING OF PARAMETER BLOCK //////////////////
///////////////// (USEFUL FOR COMPLEX HOSTS)  ///////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////



// Allocates a parameter set to be used when dealing with class
void *lvd_malloc_in_params(const livido_instance_template_t *insttmpl) 
{
	return (malloc(insttmpl->params_in_size));
}

void lvd_free_params(void *params) 
{
	free(params);
}

/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
///////////// TYPECHECKED ACCESS TO PARAMETERS //////////////////
////////////////////// (USEFUL FOR HOSTS)  //////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////


#define TYPECAST_ERROR \
	{ \
		printf("LIVIDO Typecasting error, probably crushing!\n"); \
		return NULL; \
	}
	
// get the reference to the bool parameter, typechecked! (returns NULL on error)
lvd_type_switch *lvd_param_switch(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_SWITCH)
		TYPECAST_ERROR;
	return (LVD_GET_SWITCH(insttmpl, param_set, number));		
}

lvd_type_number *lvd_param_number(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_NUMBER)
		TYPECAST_ERROR;
	return (LVD_GET_NUMBER(insttmpl, param_set, number));		
}

lvd_type_rgba *lvd_param_rgba(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_RGBA)
		TYPECAST_ERROR;
	return (LVD_GET_RGBA(insttmpl, param_set, number));		
}


lvd_type_coord2d *lvd_param_coord2d(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_COORD2D)
		TYPECAST_ERROR;
	return (LVD_GET_COORD2D(insttmpl, param_set, number));		
}

lvd_type_string *lvd_param_string(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_STRING)
		TYPECAST_ERROR;
	return (LVD_GET_STRING(insttmpl, param_set, number));		
}

lvd_type_stringlist *lvd_param_stringlist(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_STRINGLIST)
		TYPECAST_ERROR;
	return (LVD_GET_STRINGLIST(insttmpl, param_set, number));		
}

lvd_type_pointer *lvd_param_pointer(const livido_instance_template_t *insttmpl, void *param_set, int number)
{
	if (insttmpl->in_parameter_templates[number].type != LVD_PARAM_POINTER)
		TYPECAST_ERROR;
	return (LVD_GET_POINTER(insttmpl, param_set, number));		
}




/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
///////// AUTOMATIC SETTING OF WHOLE PARAMETER SET TO ///////////
////////////////// DEFULT, MINIMUM AND MAXIMUM //////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////


// set the parameter set to default values 
// This actually works the same way as 	LIVIDO_COPY_DEFAULT_PARAMETERS macro,
// but does it as a function
void lvd_params_to_def(const livido_instance_template_t *insttmpl, void *parameter_set)
{ 
	LIVIDO_COPY_DEFAULT_PARAMETERS(insttmpl, parameter_set);
}

// set the parameter set to minimum values (or default if not numerical)
void lvd_params_to_min(const livido_instance_template_t *insttmpl, void *parameter_set)
{
	memcpy(parameter_set, insttmpl->params_in_min, insttmpl->params_in_size);
}

void lvd_params_to_min_num(const livido_instance_template_t *insttmpl, void *parameter_set, int num)
{
	int offset = insttmpl->in_parameter_templates[num].offset;
	int size = insttmpl->in_parameter_templates[num].size;
	memcpy((char *)parameter_set + offset, (char *) insttmpl->params_in_min + offset, size);
}

// set the parameter set to maximum values (or default if not numerical)
void lvd_params_to_max(const livido_instance_template_t *insttmpl, void *parameter_set)
{ 
	memcpy(parameter_set, insttmpl->params_in_max, insttmpl->params_in_size);
}

void lvd_params_to_max_num(const livido_instance_template_t *insttmpl, void *parameter_set, int num)
{
	int offset = insttmpl->in_parameter_templates[num].offset;
	int size = insttmpl->in_parameter_templates[num].size;
	memcpy((char *)parameter_set + offset, (char *) insttmpl->params_in_max + offset, size);
}

/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
////////////////// AUTOMATIC CHECKING FUNCTION //////////////////
//////////////////// (USEFUL FOR PLUGINS) ///////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////



// Passing to it input and output channels and same_ parameter
// Returns a pointer to the channel that same parameter refers to or NULL if none
livido_channel_t *lvd_get_same_channel(	livido_channel_t *in,
					livido_channel_t *out,
					int same)
{ 
	if (same > 0) 
		return (&(in[same - 1]));
	else if (same < 0)
		return (&(out[-same - 1]));
	else 
		return (NULL);
}

int lvd_is_among_palettes(livido_palette_t pal, livido_palette_t *pals) 
{
	for ( ;*pals != LVD_PALETTE_END; pals++) 
		if (*pals == pal) return 1;		
	return 0;
}


int lvd_check_channel_layout(const livido_instance_template_t *cl,
				livido_instance_t *inst)
{
	if (inst->my_template != cl)
		return 1; // host hasn't set the right class!

	livido_channel_template_t *temp_in = cl->in_channel_templates;
	livido_channel_template_t *temp_out = cl->out_channel_templates;
	livido_channel_t *chan_in = inst->in_channels;
	livido_channel_t *chan_out = inst->out_channels;
	int c;
	// First check if the match is 1:1
	if (lvd_num_channel_templates(temp_in) != lvd_num_channels(chan_in))
		return 1; // Input channel numbers don't match	
	if (lvd_num_channel_templates(temp_out) != lvd_num_channels(chan_out))
		return 1; // Output channel numbers don't match	
	// Now check if based-on variables match

	// Ok lets first check input channels
	for (c = 0; chan_in[c].based_on_template != NULL; c++)
	{	if (chan_in[c].based_on_template != temp_in + c)
			return 1; // nonaligned input channels!
		if (temp_in[c].flags & LIVIDO_CHNL_OPTIONS_DISABLED)
		{
			if (!(chan_in[c].flags & LIVIDO_CHNLTMPL_OPTIONAL))
				continue; // if channel is not used, don't check anymore
			else 
				return 1; // channel disabled but not optional
		}
		if (!lvd_is_among_palettes(chan_in[c].palette, temp_in[c].palettes))
			return 1; // palettes!
		if ((temp_in[c].width && temp_in[c].width != chan_in[c].width) ||
		    (temp_in[c].height && temp_in[c].height != chan_in[c].height))
			return 1; // channel size does not conform to template
		livido_channel_t *same;
		same = lvd_get_same_channel(chan_in, chan_out, temp_in[c].same_as);
		if (same && 
		   (temp_in[c].flags && LIVIDO_CHNLTMPL_SAME_AS_PALETTE) && 
		   (chan_in[c].palette != same->palette))
			return 1; // Palette does not conform to dependand channel
		if (same && 
		   (temp_in[c].flags && LIVIDO_CHNLTMPL_SAME_AS_SIZE) && 
		   ((chan_in[c].width != same->width) || (chan_in[c].height != same->height)))
			return 1; // Size does not conform to dependand channel
		if (!LIVIDO_PALETTE_IS_PLANAR(chan_in[c].palette))
		{	// check that rowstride is sane
			if (chan_in[c].rowstrides[0] < (LIVIDO_PALETTE_BITS(chan_in[c].palette) * chan_in[c].width + 7) / 8) 
				return 1;
		}
	}
	
	// and now output channels
	for (c = 0; chan_out[c].based_on_template != NULL; c++)
	{
		if (chan_out[c].based_on_template != temp_out + c)
			return 1; // nonaligned output channels!
		if (temp_out[c].flags & LIVIDO_CHNL_OPTIONS_DISABLED)
		{
			if (!(chan_out[c].flags & LIVIDO_CHNLTMPL_OPTIONAL))
				continue; // if channel is not used, don't check anymore
			else 
				return 1; // channel disabled but not optional
		}
		if (!lvd_is_among_palettes(chan_out[c].palette, temp_out[c].palettes))
			return 1; // palettes
		if ((temp_out[c].width && temp_out[c].width != chan_out[c].width) ||
		    (temp_out[c].height && temp_out[c].height != chan_out[c].height))
			return 1; // channel size does not conform to template
		livido_channel_t *same;
		same = lvd_get_same_channel(chan_out, chan_out, temp_out[c].same_as);
		if (same && 
		   (temp_out[c].flags && LIVIDO_CHNLTMPL_SAME_AS_PALETTE) && 
		   (chan_out[c].palette != same->palette))
			return 1; // Palette does not conform to dependand channel
		if (same && 
		   (temp_out[c].flags && LIVIDO_CHNLTMPL_SAME_AS_SIZE) && 
		   ((chan_out[c].width != same->width) || (chan_out[c].height != same->height)))
			return 1; // Size does not conform to dependand channel
		if (!LIVIDO_PALETTE_IS_PLANAR(chan_out[c].palette))
		{	// check that rowstride is sane
			if (chan_out[c].rowstrides[0] < (LIVIDO_PALETTE_BITS(chan_out[c].palette) * chan_out[c].width + 7) / 8) 
				return 1;
		}
	}
	// ADDON: sane u_shift and v_shift checking for appropriate modes
	// ADDON: checking of rowstides sanity for planar modes	
	return 0;
}


#define LVD_STRATEGY_BAIL_OUT 0		// when checking function finds a mistake, it returns error
#define LVD_STRATEGY_FIX_IT   1         // checkin function clams the values to the min/max

int lvd_check_parameter_boundaries( const livido_instance_template_t *lc,
				       void *params,
				       int strategy)
{
	livido_parameter_template_t *param = lc->in_parameter_templates;
	int param_num = 0;
	while (param->name != NULL)
	{
		switch (param->type)
		{
			case LVD_PARAM_NUMBER:
			{	
				lvd_type_number *value = lvd_param_number(lc, params, param_num);
				lvd_type_number min = *lvd_param_number(lc, lc->params_in_min, param_num);
				lvd_type_number max = *lvd_param_number(lc, lc->params_in_max, param_num);
				if (*value < min)
				{
					if (strategy == LVD_STRATEGY_BAIL_OUT)
						return 1;
					else 
						*value = min;
				} else
				if (*value > max)
				{
					if (strategy == LVD_STRATEGY_BAIL_OUT)
						return 1;
					else 
						*value = max;
				}
				break;
			}
		}
		param_num++;
		param++;
	}
	return 0;
}  


/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
///////////////////// GET ENGLISH NAME FUNCTIONS ////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////



// Passing it a livido palette number
// It returns the pointer to the english name of the palette
char *lvd_get_palette_name(livido_palette_t pal) 
{
	switch (pal)
	{
		case LVD_PALETTE_RGB888:	return "RGB888"; break;
		case LVD_PALETTE_BGR888:	return "BGR888"; break;
		case LVD_PALETTE_RGBA8888:	return "RGBA8888"; break;
		case LVD_PALETTE_RGBX8888:	return "RGBX8888"; break;
		case LVD_PALETTE_RGB161616:	return "RGB161616"; break;
		case LVD_PALETTE_RGBA16161616:	return "RGBA16161616"; break;
		case LVD_PALETTE_YUV888:	return "YUV888"; break;
		case LVD_PALETTE_YUV161616:	return "YUV161616"; break;
		case LVD_PALETTE_YUVA8888:	return "YUVA8888"; break;
		case LVD_PALETTE_YUVA16161616:	return "YUVA16161616"; break;
		case LVD_PALETTE_YUV422P:	return "YUV422P"; break;
		case LVD_PALETTE_YUV420P:	return "YUV420P"; break;
		case LVD_PALETTE_YUV444P:	return "YUV444P"; break;
		case LVD_PALETTE_A1:		return "A1"; break;
		case LVD_PALETTE_A2:		return "A2"; break;
		case LVD_PALETTE_A4:		return "A4"; break;
		case LVD_PALETTE_A8:		return "A8"; break;
		case LVD_PALETTE_A16:		return "A16"; break;
		case LVD_PALETTE_AFLOAT:	return "AFLOAT"; break;
		case LVD_PALETTE_YUYV8888:	return "YUYV8888"; break;
		case LVD_PALETTE_UYVY8888:	return "UYVY8888"; break;
		case LVD_PALETTE_RGB565:	return "RGB565"; break;
		default: return "Unknown";
	}	
}

// Passing it a livido type number
// It returns the pointer to the english name of the type
char *lvd_get_type_name(int type)
{
	switch (type)
	{	case LVD_PARAM_SWITCH:		return "Switch"; break;
		case LVD_PARAM_NUMBER:		return "Number"; break;
		case LVD_PARAM_RGBA:		return "RGBA color quadraplet"; break;
		case LVD_PARAM_COORD2D: 	return "2D double coordinate"; break;
		case LVD_PARAM_STRING:		return "String"; break;
		case LVD_PARAM_STRINGLIST:	return "Predefined indexed strings"; break;
		case LVD_PARAM_POINTER:		return "Data pointer"; break;
		default: return "Unknown data type"; 
	}
}

void lvd_double_to_text(double value, int decimals, lvd_type_string text)
{
	char formatstr[16];
	sprintf(formatstr, "%%.%if", decimals);
	sprintf(text, formatstr, value); 
}

void lvd_value_to_text(const livido_instance_template_t *insttmpl, void *param_set, int number, lvd_type_string text)
{
	void *param_data = (void *)((char *)param_set + insttmpl->in_parameter_templates[number].offset);
	switch (insttmpl->in_parameter_templates[number].type)
	{	
		case LVD_PARAM_SWITCH:		*LVD_CAST_SWITCH(param_data) ? sprintf(text, "True") : sprintf(text, "False"); break;
		case LVD_PARAM_NUMBER:		
		{
			lvd_double_to_text(*LVD_CAST_NUMBER(param_data), insttmpl->in_parameter_templates[number].decimals, text);
			break;
		}
		case LVD_PARAM_RGBA:		sprintf(text, "R: %f, G: %f, B: %f, A: %f", LVD_CAST_RGBA(param_data)->r, LVD_CAST_RGBA(param_data)->g, LVD_CAST_RGBA(param_data)->b, LVD_CAST_RGBA(param_data)->a); break;
		case LVD_PARAM_COORD2D: 	sprintf(text, "X: %f, Y: %f", LVD_CAST_COORD2D(param_data)->x, LVD_CAST_COORD2D(param_data)->y); break;
		case LVD_PARAM_STRING:		sprintf(text, "%s", (char *)LVD_CAST_STRING(param_data)); break;
		case LVD_PARAM_STRINGLIST:	sprintf(text, "Index: %i", *LVD_CAST_STRINGLIST(param_data)); break;
		case LVD_PARAM_POINTER:		sprintf(text, "Data pointer: %p", *LVD_CAST_POINTER(param_data)); break;
		default: 			sprintf(text, "Unknown data type at %p", param_data); 
	}
}




/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
////////////////// DEBUGGING & DUMPING FUNCTIONS ////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////

// Intended for debugging (inside hosts)
// Input: livido_instance_template_t->palettes
// Prints out the palette list as found in livido_instance_template_t->palettes
void lvd_dump_palettes(livido_palette_t *pals) 
{
	printf("\t\tPalettes: ");
	while (*pals != LVD_PALETTE_END) 
	{
		printf("%s (bpp: %i) ", lvd_get_palette_name(*pals), LIVIDO_PALETTE_BITS(*pals));
		pals++;
	}
	printf("\n");
}


// Providing the one of the same_ parameters
// it dumps the english name of the channel it references
void lvd_dump_channel_dependancy(int dep) 
{
	if (dep > 0) 
		printf("Input channel %i\n", dep - 1);
	else if (dep < 0)
		printf("Output channel %i\n", -dep - 1);
	else 
		printf("None\n");
}



// Intended for debugging (inside hosts)
// Input: pointer to the channel template
// Prints out the information about channel template
void lvd_dump_channel_template(livido_channel_template_t *chan) 
{
	printf("\t\tChannel name: %s\n", chan->name);
	if (chan->flags & LIVIDO_CHNLTMPL_OPTIONAL)
		printf("\t\tChannel is optional\n");
	else 
		printf("\t\tChannel is mandatory\n");
	lvd_dump_palettes(chan->palettes);
	if (!(chan->flags & (LIVIDO_CHNLTMPL_SAME_AS_PALETTE | LIVIDO_CHNLTMPL_SAME_AS_SIZE)))
		printf("\t\tChannel size and palette are independant of other channels\n");
	else  
	if ((chan->flags & LIVIDO_CHNLTMPL_SAME_AS_PALETTE) && 
	   (chan->flags & LIVIDO_CHNLTMPL_SAME_AS_SIZE))
	{
		printf("\t\tChannel palette and size has to conform to ");
		lvd_dump_channel_dependancy(chan->same_as);
	} else	
	if (chan->flags & LIVIDO_CHNLTMPL_SAME_AS_PALETTE ) 
	{
		printf("\t\tChannel palette has to conform to ");
		lvd_dump_channel_dependancy(chan->same_as);
	} else
	if (chan->flags & LIVIDO_CHNLTMPL_SAME_AS_SIZE) 
	{
		printf("\t\tChannel size has to conform to ");
		lvd_dump_channel_dependancy(chan->same_as);
	}

	if (chan->width == 0 && chan->height == 0) 
		printf("\t\tChannel width and height are arbitrary\n");
	else
		printf("\t\tChannel accepts only frames of size: %i x %i\n", chan->width, chan->height);
}


// Intended for debugging (inside hosts)
// Input: pointer to the livido_instance_template_t->in_channels or out_channels
// Prints out the information about all channels in the list
void lvd_dump_channel_templates(livido_channel_template_t *chans) 
{
	while (chans->palettes != NULL)
	{	
		lvd_dump_channel_template(chans);
		chans++;
	}
}



// Intended for debugging (inside hosts)
// Input: the type of the parameter and pointer to it
// Prints out the value of the paramter in human readable form
void lvd_dump_value_direct(int type, void *param_data, int decimals) 
{
	switch (type)
	{	
		case LVD_PARAM_SWITCH:		*LVD_CAST_SWITCH(param_data) ? printf("True") : printf("False"); break;
		case LVD_PARAM_NUMBER:		printf("Number: %2.2f", *LVD_CAST_NUMBER(param_data)); break;
		case LVD_PARAM_RGBA:		printf("R: %f, G: %f, B: %f, A: %f", LVD_CAST_RGBA(param_data)->r, LVD_CAST_RGBA(param_data)->g, LVD_CAST_RGBA(param_data)->b, LVD_CAST_RGBA(param_data)->a); break;
		case LVD_PARAM_COORD2D: 	printf("X: %f, Y: %f", LVD_CAST_COORD2D(param_data)->x, LVD_CAST_COORD2D(param_data)->y); break;
		case LVD_PARAM_STRING:		printf("\"%s\"", (char *)LVD_CAST_STRING(param_data)); break;
		case LVD_PARAM_STRINGLIST:	printf("Choice number %i", *LVD_CAST_STRINGLIST(param_data)); break;
		case LVD_PARAM_POINTER:		printf("Data pointer: %p", *LVD_CAST_POINTER(param_data)); break;
		default: 			printf("Unknown data type at %p", param_data); 
	}
}


// just different implementation of previous one (different input parameters)
// Input: the class, the pointer to the parameter set and the serial number of parameter
// Prints out the value of the paramter in human readable form
void lvd_dump_value(const livido_instance_template_t *cl, void *paramset, int number) 
{
	lvd_type_string text;
	lvd_value_to_text(cl, paramset, number, text);
	printf(text);
}

void lvd_dump_parameter_set(const livido_instance_template_t *lc, void *params) 
{
	int number = 0;
	while (lc->in_parameter_templates[number].name != NULL)
	{	

		printf("Parameter (%i) \"%s\" is: ", number, lc->in_parameter_templates[number].name);		

		lvd_dump_value(lc, params, number);

		printf("\n");
		number++;
	}
}

int lvd_dump_stringlist_options(lvd_type_string *st) {
	int c = 0;
	printf("\t\tStringlist options: ");
	while (st[c][0] != 0)
	{ 
		if (c!=0) printf(", ");
		printf("\"%s\"", st[c]);
		c++;
	}
	printf("\n");
	return c; 
}


// Dumps the single parameter description in human readable form
void lvd_dump_in_parameter_template(const livido_instance_template_t *lc, int num)
{
	livido_parameter_template_t *param = &(lc->in_parameter_templates[num]);
	printf("\t\tParameter name: %s\n", param->name);
	printf("\t\t\tDescription: %s\n", param->description);
	printf("\t\t\tHint: %s\n", param->hint);
	printf("\t\t\tType: %s\n", lvd_get_type_name(param->type));
	switch (param->type) // check if min and max have meaning 
	{
		case LVD_PARAM_NUMBER:
			printf("\t\t\tMin: %2.2f, Max: %2.2f, Decimals: %i, Step size: %2.2f\n", 
				*lvd_param_number(lc, lc->params_in_min, num), 
				*lvd_param_number(lc, lc->params_in_max, num), 
				param->decimals, 
				param->step_size);
			break;
		case LVD_PARAM_STRINGLIST:
			lvd_dump_stringlist_options((lvd_type_string*)(param->choice_list));
			break;
		default: 
			break;
	}
	printf("\t\t\tDefault value: ");
	// typecast is not important
	lvd_dump_value_direct(param->type, LVD_GET_VOID(lc, lc->params_in_def, num), param->decimals);
	printf("\n");
}

// Dumps the list of parameter descriptions in human readable form
void lvd_dump_in_parameter_templates(const livido_instance_template_t *lc)
{
	int i = 0;
	while (lc->in_parameter_templates[i].name != NULL)
	{	
		lvd_dump_in_parameter_template(lc, i);
		i++;
	}
}

// Dumps the livido_instance_template_t in human readable form 
// Including palettes, input channels, output channels and parameter descriptions
void lvd_dump_instance_template(const livido_instance_template_t *cl) 
{
	printf("Dump for class name: %s\n", cl->name);
	printf("\tAuthor: %s\n", cl->author);
	printf("\tDescription: %s\n", cl->description);
	printf("\tPlugin version: %i\n", cl->version);
	printf("\tMade for livido API version: %i (This host API version: %i)\n", cl->version, LIVIDO_HEADER_VERSION); 
	printf("\tParameters' templates %p, number of params: %i\n", (void *)cl->in_parameter_templates, lvd_num_parameters(cl->in_parameter_templates));
	lvd_dump_in_parameter_templates(cl);
	printf("\tInput channels: %p,    number of channels: %i\n", (void *)cl->in_channel_templates, lvd_num_channel_templates(cl->in_channel_templates));
	lvd_dump_channel_templates(cl->in_channel_templates);
	printf("\tOutput channels: %p,   number of channels: %i\n", (void *)cl->out_channel_templates, lvd_num_channel_templates(cl->out_channel_templates));
	lvd_dump_channel_templates(cl->out_channel_templates);
	printf("\tFunctions: init %p, deinit %p, process %p, get_parameters %p\n", (void *)cl->init, (void*)cl->deinit, (void*)cl->process, (void*)cl->interpolate_parameters);
}







