/*
// Copyright (C) 2000 Julien Carme
// Copyright (C) 2001 Neil Stevens <neil@qualityassistant.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2, as
// published by the Free Software Foundation.
//
// 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 "renderer.h"
#include "main.h"
#include "display.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <SDL.h>


SDL_Color color_table[NB_PALETTES][256];
short current_colors[256];

byte* surface1;
byte* surface2;

void generate_colors(void)
{
	int i,k;
	float colors[NB_PALETTES][2][3]={{{1,1,1},{1,1,1}},
					 {{2,1.5,0},{0,0.5,2}},
					 {{0,1,2},{0,1,0}},
					 {{0,2,1},{0,0,1}},
					 {{2,0,0},{0,1,1}}};

	for (k=0;k<NB_PALETTES;k++)
	{
		for ( i=0; i<128; i++ )
		{
			color_table[k][i].r = colors[k][0][0]*i;
			color_table[k][i].g = colors[k][0][1]*i;
			color_table[k][i].b = colors[k][0][2]*i;
		}

		for ( i=0; i<128; i++ )
		{
			color_table[k][i+128].r = colors[k][0][0]*127+colors[k][1][0]*i;
			color_table[k][i+128].g = colors[k][0][1]*127+colors[k][1][1]*i;
			color_table[k][i+128].b = colors[k][0][2]*127+colors[k][1][2]*i;
		}
	}
}

void change_color(int t2,int t1,int w)
{
	unsigned int i;

	for (i=0;i<255;i++)
	{
		int r,g,b;
		r = (color_table[t1][i].r*w+color_table[t2][i].r*(256-w) )>>11;
		g = (color_table[t1][i].g*w+color_table[t2][i].g*(256-w) )>>10;
		b = (color_table[t1][i].b*w+color_table[t2][i].b*(256-w) )>>11;
		current_colors[i]=(r<<11)+(g<<5)+b;
	}
}

void compute_surface(t_interpol* vector_field)
{
	int i,j;
	int add_dest=0;
	int add_src;
	t_interpol *interpol;
	byte* ptr_pix;
	int color;
	byte* ptr_swap;

	for (j=0;j<scr_par.height;j++)
	{
		for (i=0;i<scr_par.width;i++)
		{
			interpol=&vector_field[add_dest];
			add_src=(interpol->coord & 0xFFFF)*scr_par.width+(interpol->coord>>16);
			ptr_pix=&((byte*)surface1)[add_src];
			color=(*(ptr_pix)*(interpol->weight>>24)
			       +*(ptr_pix+1)*((interpol->weight & 0xFFFFFF)>>16)
			       +*(ptr_pix+scr_par.width)*((interpol->weight & 0xFFFF)>>8)
			       +*(ptr_pix+scr_par.width+1)*(interpol->weight & 0xFF))>>8;

			if (color>255)
				surface2[add_dest]=255;
			else
				surface2[add_dest]=color;
			add_dest++;
		}
	}

	ptr_swap=surface1;
	surface1=surface2;
	surface2=ptr_swap;
}

void display_surface(void)
{
	int i,j;

	if (scr_par.scale>1)
	{
		for (i=0;i<scr_par.height;i++)
		{
			short* pdest=(short*)(screen->pixels+i*screen->pitch*scr_par.scale);
			byte* psrc=surface1+i*scr_par.width;

			if (scr_par.scale==2)
			{
				for (j=1; j<scr_par.width; j++)
				{
					*pdest = current_colors[*psrc++];
					pdest++;
					*pdest = *(pdest-1);
      					pdest++;
				}

				memcpy(screen->pixels+i*screen->pitch*2+screen->pitch,
					screen->pixels+i*screen->pitch*2,screen->pitch);
			}
/*		    else
		    {
			for (j=1;j<scr_par.width;j++) {
			    *(pdest++)=current_colors[*psrc++];
			    *(pdest++)=*(pdest-1);
			    *(pdest++)=*(pdest-1);
			}
			memcpy(screen->pixels+i*screen->pitch*3+screen->pitch,
			       screen->pixels+i*screen->pitch*3,screen->pitch);
			memcpy(screen->pixels+i*screen->pitch*3+screen->pitch*2,
			       screen->pixels+i*screen->pitch*3,screen->pitch);

		    }
*/
		}
	}
	else
	{
		byte* psrc=surface1;
		for (i=0;i<scr_par.height;i++)
		{
			short* pdest=(short*)(screen->pixels+i*screen->pitch);
			for (j=0;j<scr_par.width;j++)
			*pdest++=current_colors[*psrc++];
		}
	}
	SDL_UpdateRect(screen, 0, 0, 0, 0);
}

void blur(t_interpol* vector_field)
{
	compute_surface(vector_field);
	display_surface();
}

void plot1(int x,int y,int c);
void plot1(int x,int y,int c)
{
	if (x>0 && x<scr_par.width-3 && y>0 && y<scr_par.height-3)
		assign_max(&(surface1)[x+y*scr_par.width],c);
}

void plot2(int x,int y,int c);
void plot2(int x,int y,int c)
{
	int ty;

	if (x>0 && x<scr_par.width-3 && y>0 && y<scr_par.height-3) {
		ty = y*scr_par.width;
		assign_max(&(surface1)[x+ty],c);
		assign_max(&(surface1)[x+1+ty],c);
		assign_max(&(surface1)[x+ty+scr_par.width],c);
		assign_max(&(surface1)[x+1+ty+scr_par.width],c);
	}
}


void plot3(int x,int y,int c);
void plot3(int x,int y,int c)
{
	int ty;

	if (x>0 && x<scr_par.width-3 && y>0 && y<scr_par.height-3) {
		ty = y*scr_par.width;
		assign_max(&(surface1)[x+ty],c/3);
		assign_max(&(surface1)[x+1+ty],c>>1);
		assign_max(&(surface1)[x+ty+scr_par.width],c>>1);
		assign_max(&(surface1)[x+1+ty+scr_par.width],c);
		assign_max(&(surface1)[x+ty+(scr_par.width<<1)],c/3);
		assign_max(&(surface1)[x+2+ty+(scr_par.width<<1)],c/3);
		assign_max(&(surface1)[x+1+ty+(scr_par.width<<1)],c>>1);
		assign_max(&(surface1)[x+2+ty+scr_par.width],c>>1);
		assign_max(&(surface1)[x+2+ty+scr_par.width],c/3);
	}
}

#if 0
void line(int x1,int y1,int x2,int y2,int c)
{
	int i,j;
	float x,y,vx,vy,d;
	const float step=1;

	if (x1==x2 && y1==y2)
		plot3(x1,y1,255);
	else {
		x=x1;
		y=y1;
		d=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
		vx=step*(x2-x1)/d;
		vy=step*(y2-y1)/d;
		for (i=0;i<(int)(d/step)+1;i++) {
			plot1(x,y,c);
			x+=vx;
			y+=vy;
		}
	}
}
#endif

#define SWAP(x,y)	\
	x = x + y;	\
	y = x - y;	\
	x = x - y;

void line(int _x1, int _y1, int _x2, int _y2, int _c)
{
	int dx, dy, cxy, dxy;
	/* calculate the distances */
	dx = abs(_x1 - _x2);
	dy = abs(_y1 - _y2);

	cxy = 0;
	if (dy > dx)
	{
		/* Follow Y axis */
		if (_y1 > _y2)
		{
			SWAP(_y1, _y2);
			SWAP(_x1, _x2);
		}

		if (_x1 > _x2)
			dxy = -1;
		else
			dxy = 1;

		for (_y1=_y1; _y1<_y2; _y1++)
		{
			cxy += dx;
			if (cxy >= dy)
			{
				_x1+= dxy;
				cxy -= dy;
			}
			plot1(_x1, _y1, _c);
		}
	}
	else
	{
		/* Follow X axis */
		if (_x1 > _x2)
		{
			SWAP(_x1, _x2);
			SWAP(_y1, _y2);
		}

		if (_y1 > _y2)
			dxy = -1;
		else
			dxy = 1;

		for (_x1=_x1; _x1<_x2; _x1++)
		{
			cxy += dy;
			if (cxy >= dx)
			{
				_y1+=dxy;
				cxy -= dx;
			}
		 	plot1(_x1, _y1, _c);
		}
	}
}

struct sincos
{
	int i;
	float *f;
};

/* Little optimization for cos/sin functions */
static struct sincos cosw = { 0, NULL };
static struct sincos sinw = { 0, NULL };

void spectral(t_effect* current_effect,short data[2][512])
{
	int i, halfheight, halfwidth;
	float old_y1,old_y2;
	float _y1=(((data[0][0]+data[1][0])>>9)*current_effect->spectral_amplitude*scr_par.height)>>12;
	float _y2=(((data[0][0]+data[1][0])>>9)*current_effect->spectral_amplitude*scr_par.height)>>12;
	const int density_lines=5;
	const int step=4;
	const int shift=(current_effect->spectral_shift*scr_par.height)>>8;

	if (cosw.i != scr_par.width || sinw.i != scr_par.width)
	{
		free(cosw.f);
		free(sinw.f);
		sinw.f = cosw.f = NULL;
		sinw.i = cosw.i = 0;
	}

	if (cosw.i == 0 || cosw.f == NULL)
	{
	 	float halfPI  = (float)PI/2;
		cosw.i = scr_par.width;
		cosw.f = malloc(sizeof(float)*scr_par.width);
		for (i=0; i<scr_par.width;i+=step)
			cosw.f[i] = cos((float)i/scr_par.width*PI+halfPI);
	}

	if (sinw.i == 0 || sinw.f == NULL)
	{
		float halfPI = (float)PI/2;
		sinw.i = scr_par.width;
		sinw.f = malloc(sizeof(float)*scr_par.width);
		for (i=0; i<scr_par.width;i+=step)
			sinw.f[i] = sin((float)i/scr_par.width*PI+halfPI);
	}

	if (current_effect->mode_spectre==3)
	{
		if (_y1<0)
			_y1=0;

		if (_y2<0)
			_y2=0;
	}

	halfheight = scr_par.height >> 1;
	halfwidth  = scr_par.width >> 1;
	for (i=step;i<scr_par.width;i+=step)
	{
		old_y1=_y1;
		old_y2=_y2;
		_y1=((data[1][(i<<9)/scr_par.width/density_lines]>>8)*
		    current_effect->spectral_amplitude*scr_par.height)>>12;
		_y2=((data[0][(i<<9)/scr_par.width/density_lines]>>8)*
		    current_effect->spectral_amplitude*scr_par.height)>>12;

		switch (current_effect->mode_spectre)
		{
		case 0:
			line(i-step,halfheight+shift+old_y2,
			     i,halfheight+shift+_y2,
			     current_effect->spectral_color);
			break;
		case 1:
			line(i-step,halfheight+shift+old_y1,
			     i,halfheight+shift+_y1,
			     current_effect->spectral_color);
			line(i-step,halfheight-shift+old_y2,
			     i,halfheight-shift+_y2,
			     current_effect->spectral_color);
			break;
		case 2:
			line(i-step,halfheight+shift+old_y1,
			     i,halfheight+shift+_y1,
			     current_effect->spectral_color);
			line(i-step,halfheight-shift+old_y1,
			     i,halfheight-shift+_y1,
			     current_effect->spectral_color);
			line(halfwidth+shift+old_y2,i-step,
			     halfwidth+shift+_y2,i,
			     current_effect->spectral_color);
			line(halfwidth-shift+old_y2,i-step,
			     halfwidth-shift+_y2,i,
			     current_effect->spectral_color);
			break;
		case 3:
			if (_y1<0)
				_y1=0;
			if (_y2<0)
				_y2=0;
		case 4:
			line(halfwidth  + cosw.f[i-step] * (shift+old_y1),
			     halfheight + sinw.f[i-step] * (shift+old_y1),
			     halfwidth  + cosw.f[i]      * (shift+_y1),
			     halfheight + sinw.f[i]      * (shift+_y1),
			     current_effect->spectral_color);
			line(halfwidth  - cosw.f[i-step] * (shift+old_y2),
			     halfheight + sinw.f[i-step] * (shift+old_y2),
			     halfwidth  - cosw.f[i]      * (shift+_y2),
			     halfheight + sinw.f[i]      * (shift+_y2),
			     current_effect->spectral_color);
			break;
		}
	}

	if (current_effect->mode_spectre==3 || current_effect->mode_spectre==4)
		line(halfwidth  + cosw.f[scr_par.width - step] * (shift+_y1),
		     halfheight + sinw.f[scr_par.width - step] * (shift+_y1),
		     halfwidth  - cosw.f[scr_par.width - step] * (shift+_y2),
		     halfheight + sinw.f[scr_par.width - step] * (shift+_y2),
		     current_effect->spectral_color);
}

void curve(t_effect* current_effect)
{
	int i,j,k;
	float v,vr;
	float x,y;
	float amplitude=(float)current_effect->curve_amplitude/256;

	for (j=0;j<2;j++)
	{
		v=80;
		vr=0.001;
		k=current_effect->x_curve;
		for (i=0;i<64;i++)
		{
			x=cos((float)(k)/(v+v*j*1.34))*scr_par.height*amplitude;
			y=sin((float)(k)/(1.756*(v+v*j*0.93)))*scr_par.height*amplitude;
			plot2(x*cos((float)k*vr)+y*sin((float)k*vr)+scr_par.width/2,
			      x*sin((float)k*vr)-y*cos((float)k*vr)+scr_par.height/2,
			      current_effect->curve_color);
			k++;
		}
	}
	current_effect->x_curve=k;
}


void init_sdl(void)
{
	surface1=(byte*)malloc(scr_par.width*scr_par.height);
	surface2=(byte*)malloc(scr_par.width*scr_par.height);

	if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
	{
		fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
		exit(1);
	}

	SDL_WM_SetCaption("Tyler", 0L);
	screen = SDL_SetVideoMode(scr_par.width * scr_par.scale,
		scr_par.height * scr_par.scale, 16, VIDEO_FLAGS);

	if ( screen == NULL )
	{
		fprintf(stderr, "Couldn't init video mode: %s\n", SDL_GetError());
		exit(1);
	}
	SDL_ShowCursor(0);
	SDL_EnableKeyRepeat(0,0);
}

void toggle_fullscreen(void)
{
	SDL_WM_ToggleFullScreen(screen);
}

void close_sdl(void)
{
	SDL_Quit();
}