#include <fenix/fxdll.h>
#include <stdlib.h>
#include <SDL/SDL.h>

/* PluginVersion is used to identify the plugin structures against which
 * we're linking to prevent potential mismatches and segmentation faults
 */
unsigned int PluginVersion = FXDLL_VERSION;

/*#include <string.h>// for log
char logfile = 0;
FILE *logf = NULL;

void say (char * t)
{
	if (!logfile){
		logfile = 1;
		logf = fopen ("dllout.txt","w+");
	}
	fwrite (t, sizeof (char), strlen (t), logf);
}*/


typedef struct fgfx_rolltable{
	GRAPH * g;
	Uint8 * cdata;
} fgfx_rt; 


SDL_PixelFormat *fmt = NULL;
char fgfx_table[8192];

#define FGFX_TST(num) (fgfx_table[num/8]>>(num%8)) & 0x1
#define FGFX_SET(num) fgfx_table[num/8] |= (0x1 << (num%8))
#define FGFX_CLR(num) fgfx_table[num/8] &= ~(0x1 << (num%8))


// auxiliares


static int fgfx_init (INSTANCE *my, int *params)
{
	Uint16 i = 0;
	Uint8 r, g, b = 0;

	enable_16bits = 1;
	fmt = (*_screen)->format;
	for (i = 0; i < 65535; i ++){
		SDL_GetRGB (i, fmt, &r, &g, &b);
		if	( ((r) && (!g) && (!b)) ||
			  ((!r) && (g) && (!b)) ||
			  ((!r) && (!g) && (b)) ||
			  ((r == g) && (!b)) ||
			  ((!r) && (g == b)) ||
			  ((r == b) && (!g))      )
			FGFX_SET(i);
		else
			FGFX_CLR(i);
	}
	i = SDL_MapRGB (fmt, 0, 0, 0);
	FGFX_CLR (i);
	return (0);
}


static int fgfx_change_color (INSTANCE *my, int *params)
{
	GRAPH * gra = NULL;
	int i, j = 0;
	int pit = 0;
	int w, h;
	Uint16 c = 0;
	Uint16 * datap = NULL;
	Uint16 src = 0;
	Uint16 dest = 0;

	gra = bitmap_get (params[0], params[1]);
	src = (Uint16) params[2];
	dest = (Uint16) params[3];
	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	//gra->modified = 1;
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			c = datap[(i * (pit / 2)) + j];
			
			if (c == src){
				datap[(i * (pit / 2)) + j] = dest;
			}
		}
	}
	return (0);
}


static int fgfx_lower_cut (INSTANCE *my, int *params)
{
	GRAPH * gra = NULL;
	int i, j = 0;
	int pit = 0;
	int w, h;
	Uint16 c, c2 = 0;
	Uint16 * datap = NULL;
	Uint8 r, r2, g, g2, b, b2 = 0;

	gra = bitmap_get (params[0], params[1]);
	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	c2 = params[2];
	//gra->modified = 1;
	SDL_GetRGB (c2, fmt, &r2, &g2, &b2);
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			c = datap[(i * (pit / 2)) + j];
			SDL_GetRGB (c, fmt, &r, &g, &b);
			if (r <= r2 && g <= g2 && b <= b2)
				datap[(i * (pit / 2)) + j] = c2;
		}
	}
	return (0);
}

static int fgfx_upper_cut (INSTANCE *my, int *params)
{
	GRAPH * gra = NULL;
	int i, j = 0;
	int pit = 0;
	int w, h;
	Uint16 c, c2 = 0;
	Uint16 * datap = NULL;
	Uint8 r, r2, g, g2, b, b2 = 0;

	gra = bitmap_get (params[0], params[1]);
	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	c2 = params[2];
	//gra->modified = 1;
	SDL_GetRGB (c2, fmt, &r2, &g2, &b2);
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			c = datap[(i * (pit / 2)) + j];
			SDL_GetRGB (c, fmt, &r, &g, &b);
			if (r >= r2 && g >= g2 && b >= b2)
				datap[(i * (pit / 2)) + j] = c2;
		}
	}
	return (0);
}



static int fgfx_fade_map (INSTANCE *my, int *params)
{
	GRAPH * gra = NULL;
	int i, j = 0;
	int pit = 0;
	int w, h;
	Uint16 c = 0;
	Uint8 r, g, b = 0;
	Uint16 * datap = NULL;
	Uint16 r2, g2, b2 = 0;
	Uint16 p1, p2, p3 = 0;

	gra = bitmap_get (params[0], params[1]);
	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	p1 = abs ((Uint16)params[2]);
	p2 = abs ((Uint16)params[3]);
	p3 = abs ((Uint16)params[4]);
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	//gra->modified = 1;
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			c = datap[(i * (pit / 2)) + j];
			SDL_GetRGB (c, fmt, &r, &g, &b);
			r2 = (((Uint16)r) * p1) / 100;
			g2 = (((Uint16)g) * p2) / 100;
			b2 = (((Uint16)b) * p3) / 100;
			r = (r2 > 255)?255:(Uint8)r2;
			g = (g2 > 255)?255:(Uint8)g2;
			b = (b2 > 255)?255:(Uint8)b2;
			c = SDL_MapRGB (fmt, r, g, b);
			datap[(i * (pit / 2)) + j] = c;
		}
	}

	return (0);
}


static int fgfx_change_ranges (INSTANCE *my, int *params)
{

	GRAPH * gra = NULL;
	int i, j = 0;
	int pit = 0;
	int w, h;
	Uint16 c = 0;
	Uint8 r, g, b = 0;
	Uint8 c1[3] = {0, 0, 0};
	Uint8 c2[3] = {0, 0, 0};
	Uint8 c3[3] = {0, 0, 0};
	Uint8 c4[3] = {0, 0, 0};
	Uint8 c5[3] = {0, 0, 0};
	Uint8 c6[3] = {0, 0, 0};
	Uint16 * datap = NULL;

	gra = bitmap_get (params[0], params[1]);
	SDL_GetRGB (params[2], fmt, &c1[0], &c1[1], &c1[2]);
	SDL_GetRGB (params[3], fmt, &c2[0], &c2[1], &c2[2]);
	SDL_GetRGB (params[4], fmt, &c3[0], &c3[1], &c3[2]);
	SDL_GetRGB (params[5], fmt, &c4[0], &c4[1], &c4[2]);
	SDL_GetRGB (params[6], fmt, &c5[0], &c5[1], &c5[2]);
	SDL_GetRGB (params[7], fmt, &c6[0], &c6[1], &c6[2]);

 	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	//gra->modified = 1;
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			c = datap[(i * (pit / 2)) + j];
			if (FGFX_TST(c)){
				SDL_GetRGB (c, fmt, &r, &g, &b);
				if (!r){
					if (!g){
						if (b){ // 0 0 x - 3
							c = SDL_MapRGB (fmt, (Uint8)(((Uint16)c3[0] * (Uint16)b) / 255), (Uint8)(((Uint16)c3[1] * (Uint16)b) / 255), (Uint8)(((Uint16)c3[2] * (Uint16)b) / 255));
							datap[(i * (pit / 2)) + j] = c;
						}
					}
					else{
						if (!b){ // 0 x 0 - 2
							c = SDL_MapRGB (fmt, (Uint8)(((Uint16)c2[0] * (Uint16)g) / 255), (Uint8)(((Uint16)c2[1] * (Uint16)g) / 255), (Uint8)((c2[2] * g) / 255));
							datap[(i * (pit / 2)) + j] = c;
						}
						else{
							if (g == b){ // 0 x x - 5
								c = SDL_MapRGB (fmt, (Uint8)(((Uint16)c5[0] * (Uint16)b) / 255), (Uint8)(((Uint16)c5[1] * (Uint16)b) / 255), (Uint8)(((Uint16)c5[2] * (Uint16)b) / 255));
								datap[(i * (pit / 2)) + j] = c;
							}
						}
					}
				}
				else{
					if (!g){
						if (!b){ // x 0 0 - 1
							c = SDL_MapRGB (fmt, (Uint8)((c1[0] * r) / 255), (Uint8)((c1[1] * r) / 255), (Uint8)((c1[2] * r) / 255));
							datap[(i * (pit / 2)) + j] = c;
						}
						else{
							if (r == b){ // x 0 x - 6
								c = SDL_MapRGB (fmt, (Uint8)(((Uint16)c6[0] * (Uint16)r) / 255), (Uint8)(((Uint16)c6[1] * (Uint16)r) / 255), (Uint8)(((Uint16)c6[2] * (Uint16)r) / 255));
								datap[(i * (pit / 2)) + j] = c;
							}
						}
					}
					else{
						if (!b){ // x x 0 - 4
							c = SDL_MapRGB (fmt, (Uint8)(((Uint16)c4[0] * (Uint16)r) / 255), (Uint8)(((Uint16)c4[1] * (Uint16)r) / 255), (Uint8)(((Uint16)c4[2] * (Uint16)r) / 255));
							datap[(i * (pit / 2)) + j] = c;
						}
					}
				}
			}
		}
	}
	return (0);
}


static int fgfx_roll_graph (INSTANCE *my, int *params)
{
	fgfx_rt * frt = NULL;
	GRAPH * gra = bitmap_get (params[0], params[1]);
	int i, j = 0;
	int pit = 0;
	int w, h;
	Uint8 r, g, b = 0;
	Uint8 * destp = NULL;
	Uint16 * datap = NULL;

	if (!gra)
		return (-1);
	else
		frt = malloc (sizeof (fgfx_rt));
		frt->g = gra;
		w = gra->width;
		h = gra->height;
		frt->cdata = malloc (sizeof (Uint8) * 3 * (w * h));
		destp = frt->cdata;
		datap = (Uint16 *) gra->data;
		pit = gra->pitch;
		for (i = 0; i < h; i++){
			for (j = 0; j < w; j++){
				SDL_GetRGB (datap[(i * (pit / 2)) + j], fmt, &r, &g, &b);
				destp[(((i * w) + j) * 3)] = r;
				destp[(((i * w) + j) * 3) + 1] = g;
				destp[(((i * w) + j) * 3) + 2] = b;
			}
		}
	return ((int)frt);
}


static int fgfx_unroll_graph (INSTANCE *my, int *params)
{
	fgfx_rt * frt = (fgfx_rt *) params[0];

	if (!frt)
		return (-1);
	free (frt->cdata);
	free (frt);
	return (0);
}


static int fgfx_roll_rgb (INSTANCE *my, int *params)
{
	fgfx_rt * frt = NULL;
	GRAPH * gra = NULL;
	int i, j, k = 0;
	int pit = 0;
	int w, h;
	Uint8 r, g, b = 0;
	Uint16 * datap = NULL;
	Uint8 * rollp = NULL;
	Sint16 rd = (Sint16) params[1];
	Sint16 gd = (Sint16) params[2];
	Sint16 bd = (Sint16) params[3];

	frt = (fgfx_rt *)params[0];
	gra = frt->g;

	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	rollp = frt->cdata;
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	//gra->modified = 1;
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			k = (((i * w) + j) * 3);
			r = rollp[k];
			g = rollp[k + 1];
			b = rollp[k + 2];
			if (!r && !g && !b){
			}
			else{
				r = (Uint8) ((Sint16) r + rd);
				g = (Uint8) ((Sint16) g + gd);
				b = (Uint8) ((Sint16) b + bd);
				if (!r && !g && !b){
					r = 1;
					g = 1;
					b = 1;
				}
				rollp[k] = r;
				rollp[k + 1] = g;
				rollp[k + 2] = b;
				datap[(i * (pit / 2)) + j] = SDL_MapRGB (fmt, r, g, b);
			}
		}
	}
	return (0);
}


static int fgfx_roll_rgb_lim (INSTANCE *my, int *params)
{
	fgfx_rt * frt = NULL;
	GRAPH * gra = NULL;
	int i, j, k = 0;
	int pit = 0;
	int w, h;
	Uint8 r, g, b = 0;
	Uint16 * datap = NULL;
	Uint8 * rollp = NULL;
	Sint16 rd = (Sint16) params[1];
	Sint16 gd = (Sint16) params[2];
	Sint16 bd = (Sint16) params[3];

	frt = (fgfx_rt *)params[0];
	gra = frt->g;

	if (!gra)
		return (-1);
	if (gra->depth != 16)
		return (-2);
	datap = (Uint16 *) gra->data;
	rollp = frt->cdata;
	w = gra->width;
	h = gra->height;
	pit = gra->pitch;
	//gra->modified = 1;
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			k = (((i * w) + j) * 3);
			r = rollp[k];
			g = rollp[k + 1];
			b = rollp[k + 2];
			if (!r && !g && !b){
			}
			else{
				if (rd >= 0)
					if ((((Sint16)r) + rd) > 255) r = 255;
					else r = (Uint8) ((Sint16) r + rd);
				else
					if ((((Sint16)r) + rd) < 0) r = 0;
					else r = (Uint8) ((Sint16) r + rd);
				if (gd >= 0)
					if ((((Sint16)g) + gd) > 255) g = 255;
					else g = (Uint8) ((Sint16) g + gd);
				else
					if ((((Sint16)g) + gd) < 0) g = 0;
					else g = (Uint8) ((Sint16) g + gd);
				if (bd >= 0)
					if ((((Sint16)b) + bd) > 255) b = 255;
					else b = (Uint8) ((Sint16) b + bd);
				else
					if ((((Sint16)b) + bd) < 0) b = 0;
					else b = (Uint8) ((Sint16) b + bd);
				
				if (!r && !g && !b){
					r = 1;
					g = 1;
					b = 1;
				}
				rollp[k] = r;
				rollp[k + 1] = g;
				rollp[k + 2] = b;
				datap[(i * (pit / 2)) + j] = SDL_MapRGB (fmt, r, g, b);
			}
		}
	}
	return (0);
}


static int fgfx_rgb (INSTANCE *my, int *params)
{
	Uint16 a = SDL_MapRGB (fmt, (Uint8) params[0], (Uint8) params[1], (Uint8) params[2]);
	if (!a)
		return (a);
	if (!params[0] && !params[1] && !params[2])
		return (0);
	else
		return (gr_rgb (params[0], params[1], params[2]));
}


static int fgfx_convert_to_16 (INSTANCE *my, int *params)
{
	GRAPH * gra = NULL;
	int i, j = 0;
	int pit = 0;
	int w, h;
	SDL_Color * c;
	Uint8 * datap = NULL;
	Uint16 * destp = NULL;

	gra = bitmap_get (params[0], params[1]);
	if (!gra)
		return (-1);
	if (gra->depth != 8)
		return (-2);
	datap = (Uint8 *) gra->data;
	w = gra->width;
	h = gra->height;
	destp = malloc (sizeof (Uint16) * w * h);
	pit = gra->pitch;
	//gra->modified = 1;
	for (i = 0; i < h; i++){
		for (j = 0; j < w; j++){
			c = &((*_palette)[datap[(i * (pit / 2)) + j]]);
			destp[(i * (pit / 2)) + j] = SDL_MapRGB (fmt, c->r, c->g, c->b);
		}
	}
	free (gra->data);
	gra->data = (void *)destp;
	gra->data = (void *)destp;
	gra->depth = 16;
	gra->pitch = (gra->pitch * 2);
	gra->widthb = (gra->widthb * 2);
	return (0);
}


FENIX_MainDLL RegisterFunctions (COMMON_PARAMS)
{
    FENIX_DLLImport

	FENIX_export ("FGFX_INIT", "", TYPE_DWORD, fgfx_init);
	FENIX_export ("FGFX_CHANGE_COLOR", "IIII", TYPE_DWORD, fgfx_change_color);
	FENIX_export ("FGFX_LOWER_CUT", "III", TYPE_DWORD, fgfx_lower_cut);
	FENIX_export ("FGFX_UPPER_CUT", "III", TYPE_DWORD, fgfx_lower_cut);
	FENIX_export ("FGFX_ROLL_GRAPH", "II", TYPE_DWORD, fgfx_roll_graph);
	FENIX_export ("FGFX_UNROLL_GRAPH", "I", TYPE_DWORD, fgfx_unroll_graph);
	FENIX_export ("FGFX_ROLL_RGB", "IIII", TYPE_DWORD, fgfx_roll_rgb);
	FENIX_export ("FGFX_ROLL_RGB_LIM", "IIII", TYPE_DWORD, fgfx_roll_rgb_lim);
	FENIX_export ("FGFX_CHANGE_RANGES", "IIIIIIII", TYPE_DWORD, fgfx_change_ranges);
	FENIX_export ("FGFX_RGB", "III", TYPE_DWORD, fgfx_rgb);
	FENIX_export ("FGFX_CONVERT_TO_16", "II", TYPE_DWORD, fgfx_convert_to_16);
	FENIX_export ("FGFX_FADE_MAP", "IIIII", TYPE_DWORD, fgfx_fade_map);
}
