#include <math.h>
#include "wcolgradgen.h"

int init_ca_list(col_att_list* pcal)
{
	(pcal)->link_count=0;
	(pcal)->plist=NULL;
	return 0;
}

int kill_ca_list(col_att_list* pcal)
{
	pcol_att current=pcal->plist;
	pcol_att next=NULL;

	while(current)
	{
		next=current->pnext;
		free(current);
		current=next;
	}
	(pcal)->link_count=0;
	(pcal)->plist=NULL;
	return 0;
}


// linearity: 0..255
// 0 - linearity = 0.0
// 128 - linearity = 1.0
// 255 - linearity = 2.0
void init_linearity_lut(pcol_att ca) {
	float l = ca->linearity;

	for (int i=0; i<LUT_SIZE+1; i++) {
		float t = (float)i / (float)LUT_SIZE;
		ca->LINEARITY[i] = pow(t, l);
	}
}


int add_to_ca_list(col_att_list* pcal, col_att_type t, col_att_mode m, int x, int y, int r, int g, int b, int range, float linearity, float multiplier)
{
	pcol_att first=pcal->plist;
	pcol_att work=NULL;
	work=(CA_DESC *)malloc(sizeof(col_att));
	if(work)
	{
		(work)->t=t;
		(work)->m=m;
		(work)->x=x;
		if (t==CA_GLOBAL) { (work)->x=100-x; if( ((work)->x < 0) || ((work)->x > 100) ) (work)->x=100; }
		(work)->y=y;
		(work)->r=r;
		(work)->g=g;
		(work)->b=b;
		(work)->range=abs(range);
		(work)->linearity=linearity;
		(work)->multiplier=multiplier;
		init_linearity_lut(work);
		if (t==CA_GLOBAL) (work)->range=100; /* that way x will be interpreted in % of color to mix in */
		(work)->pnext=first;
		(pcal)->link_count+=1;
		(pcal)->plist=work;
		return 0;
	}
	else return -1; /* not enough memory */
}


/* computes for a given pixel(x,y) the distance to the
	 specified "color attractor" */
float distance_to_ca(int x, int y, pcol_att ca)
{
	if (ca->t==CA_GLOBAL) {
		return abs(ca->x);
	}

	if (ca->t==CA_POINT)
	{
		float vx=ca->x-x;
		float vy=ca->y-y;

		//Norm 1 : N1(x,y) = |x| + |y|
		//return fabs(vx) + fabs(vy);

		//Norm 2 (euclidian distance) : N2(x,y) = sqrt(x^2 + y^2)
		return sqrt((vx*vx)+(vy*vy));

		//Norm p : Np(x,y) = (|x|^p + |y|^p)^(1/p)
		//return pow(pow(fabs(vx), ca->linearity) + pow(fabs(vy), ca->linearity), 1.0/ca->linearity);

		//Norm infinity : Ninfinity(x,y) = max(|x|,|y|)
		//return MAX(fabs(vx), fabs(vy));
	}

	if (ca->t==CA_HBAR) {
		return abs(ca->y-y);
	}

	if (ca->t==CA_VBAR) {
		return abs(ca->x-x);
	}

	return 0.0; /* invalid ca type */
}


inline float effect_modify(float dist, pcol_att ca)
{
	int i = (int)(LUT_SIZE*dist/ca->range);
	i = MID(0, i, LUT_SIZE);
	return ca->multiplier*ca->LINEARITY[i]*ca->range;
}


/*
float effect_modify(float dist, float p, float exp)
{
	return p*pow(dist,exp);
}
*/


void render_col_grad_to_bitmap(BITMAP *bmp, col_att_list* pcal)
{
	if (pcal==NULL) return; /* nothing to do */
	else if (bmp==NULL) return; /* nothing to do */
	else
	{
		int i;
		int x,y;
		int w=bmp->w;
		int h=bmp->h;
		int r,g,b;
		float fr,fg,fb;
		int inrange_count=0;
		pcol_att next=NULL;

		for(y=0; y<h; y++) for(x=0; x<w; x++) /* for every pixel...*/
		{
			r=0; g=0; b=0;
			fr=0; fg=0; fb=0;

			/* ...compute the color components for the pixel */
			next=pcal->plist;
			inrange_count=0; /* leftover from debugging */
			for(i=0; i<pcal->link_count; i++)
			{
				float d = distance_to_ca(x,y,next);
				d = effect_modify(d, next); /* modify effect */

				if ((d <= next->range) && (next->range!=0))
				{
					switch (next->m) /* evaluate CA_MODE */
					{
						case CA_ATTRACT: /* old version 1 standard(if next->p==1.0 and next->exp==1.0) */
							fr += (1.0 - (d / next->range)) * ((next)->r);
							fg += (1.0 - (d / next->range)) * ((next)->g);
							fb += (1.0 - (d / next->range)) * ((next)->b);
						break;

						case CA_ABSORB:
							fr -= (1.0 - (d / next->range)) * ((next)->r);
							fg -= (1.0 - (d / next->range)) * ((next)->g);
							fb -= (1.0 - (d / next->range)) * ((next)->b);
						break;
					};
				}

				next=next->pnext;
			}

			r=MID(0, (int)fr, 255);
			g=MID(0, (int)fg, 255);
			b=MID(0, (int)fb, 255);

			putpixel(bmp,x,y,makecol(r,g,b));
			if(key[KEY_ESC]) break; /* if accidantally called on "screen", this is an emergency exit */
		}
	}
}
