/*
	fuzz.c : Fancy software filter.
	I was bored.
	(C)2016 Marisa Kirisame, UnSX Team.
	Released under the MIT license.
*/
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <time.h>

#ifndef FUZZ_FONT_PATH
#error please define FUZZ_FONT_PATH with the absolute path to a bitmap font
#endif

typedef struct
{
	float r,g,b,a;
} rgbfpx_t;

typedef struct
{
	Uint8 r,g,b,a;
} __attribute__((packed)) rgb8px_t;

float t = 0.f;
const int fw = 640, fh = 480;

float rn( float sdx, float sdy )
{
	return cosf(sdy*3874.8674f+sdx*6783.5325f)*2737.8474f;
}

float fract( float x )
{
	return x-floorf(x);
}

SDL_Window *w;
SDL_Surface *ws, *fz;

rgbfpx_t *fl1, *fl2, *fl3;
const int fw1 = 320, fw2 = 160, fw3 = 80, fh1 = 240, fh2 = 120, fh3 = 60;

rgbfpx_t layers[3] =
{
	// Void
	{0.71f,0.67f,0.95f,1.f},
	{0.66f,0.84f,0.73f,1.f},
	{0.95f,0.73f,0.81f,1.f}
	// SWWM GZ
	//{0.91f,0.87f,1.95f,1.f},
	//{0.66f,1.84f,0.73f,1.f},
	//{1.35f,0.73f,1.21f,1.f}
	// RED ONI
	//{0.61f,0.77f,0.85f,1.f},
	//{0.86f,0.64f,0.63f,1.f},
	//{1.25f,0.33f,0.41f,1.f}
	// RED-EYED RAMPAGE
	//{1.81f,0.97f,0.75f,1.f},
	//{0.36f,0.64f,0.93f,1.f},
	//{1.25f,0.33f,0.41f,1.f}
	// RED STAR OF INNOCENCE
	//{1.31f,0.87f,1.25f,1.f},
	//{0.76f,0.64f,0.63f,1.f},
	//{1.25f,1.13f,0.21f,1.f}

};
float speed[3] =
{
	1.3526f,
	0.7843f,
	0.3725f
};

void mklayer1( void )
{
	for ( int y=0; y<fh1; y++ )
	{
		float rg;
		for ( int x=0; x<fw1; x++ )
		{
			rg = 2.f*fabsf(fract(rn(x,y)+t*speed[0])-0.5f);
			fl1[x+y*fw1].r = layers[0].r*rg;
			fl1[x+y*fw1].g = layers[0].g*rg;
			fl1[x+y*fw1].b = layers[0].b*rg;
			fl1[x+y*fw1].a = layers[0].a*rg;
		}
	}
}

void mklayer2( void )
{
	for ( int y=0; y<fh2; y++ )
	{
		float rg;
		for ( int x=0; x<fw2; x++ )
		{
			rg = 2.f*fabsf(fract(rn(x,y)+t*speed[1])-0.5f);
			fl2[x+y*fw2].r = layers[1].r*rg;
			fl2[x+y*fw2].g = layers[1].g*rg;
			fl2[x+y*fw2].b = layers[1].b*rg;
			fl2[x+y*fw2].a = layers[1].a*rg;
		}
	}
}

void mklayer3( void )
{
	for ( int y=0; y<fh3; y++ )
	{
		float rg;
		for ( int x=0; x<fw3; x++ )
		{
			rg = 2.f*fabsf(fract(rn(x,y)+t*speed[2])-0.5f);
			fl3[x+y*fw3].r = layers[2].r*rg;
			fl3[x+y*fw3].g = layers[2].g*rg;
			fl3[x+y*fw3].b = layers[2].b*rg;
			fl3[x+y*fw3].a = layers[2].a*rg;
		}
	}
}

float clamp( float a, float l, float h )
{
	return (a>h)?h:(a>l)?a:l;
}

void mergedown( void )
{
	rgbfpx_t twofivefive = {255.f,255.f,255.f,255.f};
	#pragma omp parallel for
	for ( int y=0; y<fh; y++ )
	{
		int y1, y2, y3;
		rgb8px_t *stripe;
		rgbfpx_t merged;
		y1 = y>>1;
		y2 = y>>2;
		y3 = y>>3;
		stripe = (rgb8px_t*)((Uint8*)fz->pixels+y*fz->pitch);
		for ( int x=0; x<fw; x++ )
		{
			int x1, x2, x3;
			x1 = x>>1;
			x2 = x>>2;
			x3 = x>>3;
			// gcc is stupid
			asm(	"movaps %1,%%xmm0\n"
				"mulps %2,%%xmm0\n"
				"mulps %3,%%xmm0\n"
				"mulps %4,%%xmm0\n"
				"movaps %%xmm0,%0\n"
				:"=m"(merged)
				:"m"(fl1[x1+y1*fw1]),"m"(fl2[x2+y2*fw2]),
				"m"(fl3[x3+y3*fw3]),"m"(twofivefive));
			stripe[x].r = clamp(merged.r,0,255);
			stripe[x].g = clamp(merged.g,0,255);
			stripe[x].b = clamp(merged.b,0,255);
			stripe[x].a = clamp(merged.a,0,255);
		}
	}
}

#define TMAX 64
int ti = 0;
float ts = 0.f, tl[TMAX] = {0.f};

float avg_fps( float nt )
{
	ts = ts-tl[ti]+nt;
	tl[ti] = nt;
	if ( ++ti == TMAX ) ti = 0;
	return ts/TMAX;
}

#define NANOS_SEC 1000000000L

long ticker( void )
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC_RAW,&ts);
	return ts.tv_nsec+ts.tv_sec*NANOS_SEC;
}

int main( void )
{
	SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS);
	w = SDL_CreateWindow("Fuzz",SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,fw,fh,SDL_WINDOW_SHOWN);
	ws = SDL_GetWindowSurface(w);
	fz = SDL_CreateRGBSurface(0,fw,fh,32,0xFF,0xFF00,0xFF0000,0xFF000000);
	SDL_SetSurfaceBlendMode(fz,SDL_BLENDMODE_NONE);
	fl1 = malloc(sizeof(rgbfpx_t)*fw1*fh1);
	fl2 = malloc(sizeof(rgbfpx_t)*fw2*fh2);
	fl3 = malloc(sizeof(rgbfpx_t)*fw3*fh3);
	TTF_Init();
	TTF_Font *fon = TTF_OpenFont(FUZZ_FONT_PATH,16);
	if ( !fon )
	{
		fprintf(stderr,"Could not open font '%s'.\n",FUZZ_FONT_PATH);
		TTF_Quit();
		free(fl3);
		free(fl2);
		free(fl1);
		SDL_FreeSurface(fz);
		SDL_DestroyWindow(w);
		SDL_Quit();
		return 1;
	}
	SDL_Event e;
	SDL_Color fpscol = {160,0,0,255};
	int active = 1;
	long tick, tock;
	float frame = 0.f, fps = NAN, fpsmin = INFINITY, fpsmax = -INFINITY,
		fpsavg = 0.f;
	char fpst[16];
	while ( active )
	{
		tick = ticker();
		while ( SDL_PollEvent(&e) ) if ( (e.type == SDL_QUIT)
			|| ((e.type == SDL_KEYDOWN)
			&& (e.key.keysym.sym == SDLK_ESCAPE)) ) active = 0;
		mklayer1();
		mklayer2();
		mklayer3();
		mergedown();
		SDL_BlitSurface(fz,0,ws,0);
		snprintf(fpst,15,"%.2f FPS",fps);
		SDL_Surface *txt = TTF_RenderText_Blended(fon,fpst,fpscol);
		SDL_BlitSurface(txt,0,ws,0);
		SDL_FreeSurface(txt);
		SDL_UpdateWindowSurface(w);
		tock = ticker();
		frame = (float)(tock-tick)/NANOS_SEC;
		fps = 1.f/frame;
		fpsavg = avg_fps(fps);
		if ( fps > fpsmax ) fpsmax = fps;
		if ( fps < fpsmin ) fpsmin = fps;
		printf("FPS: %.2f (%.2f min, %.2f max, %.2f avg)\n",fps,
			fpsmin,fpsmax,fpsavg);
		t += frame;
	}
	TTF_CloseFont(fon);
	TTF_Quit();
	free(fl3);
	free(fl2);
	free(fl1);
	SDL_FreeSurface(fz);
	SDL_DestroyWindow(w);
	SDL_Quit();
	return 0;
}
