#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>

#include "typedefs.h"
#include "iface_globals.h"
#include "gfx.h"
#include "is_fileio.h"
#include "interface.h"

#include <SDL.h>
#include <SDL_image.h>
#include <SDL_rwops.h>

//#define THICK_MAGNIFIER

// GLOBALS

t_ik_image *magni;
t_ik_image *screen;
t_ik_image screenbuf;
int gfx_width, gfx_height, gfx_fullscreen, gfx_switch;
int gfx_redraw;
SDL_Rect clip;

#ifdef MOVIE
int aframe = 0;
#endif

unsigned char *gfx_transbuffer;
unsigned char *gfx_lightbuffer;
unsigned char *gfx_addbuffer;

t_ik_image *dims[8];
int num_dims;

uint8 globalpal[768];
uint8 currentpal[768];

int32 sin1k[1024];
int32 cos1k[1024];

// PUTPIXEL/GETPIXEL

void ik_setclip(int32 left, int32 top, int32 right, int32 bottom)
{
	clip.x = left;
	clip.y = top;
	clip.w = right;
	clip.h = bottom;
	SDL_SetClipRect(screen, &clip); 
}

void ik_putpixel(t_ik_image *img, int32 x, int32 y, uint32 c)
{
	Uint8 *data = (Uint8 *)img->pixels;
	SDL_LockSurface(img);
	data[y*img->pitch+x]=c;
	SDL_UnlockSurface(img);
}

void ik_putpixel_add(t_ik_image *img, int32 x, int32 y, uint32 c)
{
	Uint8 *data = (Uint8 *)img->pixels;
	SDL_LockSurface(img);
	data[y*img->pitch+x]=gfx_addbuffer[data[y*img->pitch+x]+(c<<8)];
	SDL_UnlockSurface(img);
}

int32 ik_getpixel(t_ik_image *img, int32 x, int32 y)
{
	Uint8 *data = (Uint8 *)img->pixels;
	return data[y*img->pitch+x];
}

uint8 *ik_image_pointer(t_ik_image *img, int32 x, int32 y)
{
	Uint8 *data = (Uint8 *)img->pixels;
	return data+y*img->pitch+x;
}

void ik_drawline(t_ik_image *img, int32 xb, int32 yb, int32 xe, int32 ye, int32 c1, int32 c2, uint8 mask, uint8 fx)
{
	int32 x=xb<<16,y=yb<<16;
	int32 dx,dy,d,x1,y1;
	int32 cutb=0, cute=0;

  dx=xe-xb; dy=ye-yb;

  if (dx==0 && dy==0) return;

	x1=abs(dx); y1=abs(dy);
  if (x1>y1)
  {
  	d=abs(dx)+1;
  	dy=(dy<<16)/x1;
    dx=(dx<<16)/x1;
  }
  else
  {
  	d=abs(dy)+1;
    dx=(dx<<16)/y1;
  	dy=(dy<<16)/y1;
  }

	if (dx>0)  // clamp x
	{
		if (x<(clip.x<<16)) { cutb=MAX(((clip.x<<16)-x)/dx,cutb); }
		if (x+dx*d>(clip.w<<16)-1) { cute=MAX((x+dx*d-(clip.w<<16)+1)/dx, cute); }
	}
	else if (dx<0)
	{
		if (x>(clip.w<<16)-1) { cutb=MAX((x-(clip.w<<16)+1)/dx, cutb); }
		if (x+dx*d<(clip.x<<16)) { cute=MAX(((clip.x<<16)-x-dx*d)/dx, cute); }
	}
	else if (x>>16<clip.x || x>>16>=clip.w)  return;

//  ik_putpixel(img, x>>16,y>>16,c1); 


	if (dy>0)  // clamp y
	{
		if (y<(clip.x<<16)) { cutb=MAX(((clip.y<<16)-y)/dy,cutb); }
		if (y+dy*d>(clip.h<<16)-1) { cute=MAX((y+dy*d-(clip.h<<16)+1)/dy, cute); }
	}
	else if (dy<0)
	{
		if (y>(clip.h<<16)-1) { cutb=MAX((y-(clip.h<<16)+1)/dy, cutb); }
		if (y+dy*d<(clip.y<<16)) { cute=MAX(((clip.y<<16)-y-dy*d)/dy, cute); }
	}
	else if (y>>16<clip.y || y>>16>=clip.h)  return;

	if (cutb) { x+=dx*cutb; y+=dy*cutb; d-=cutb; }
	if (cute) { d-=cute; }

	if (d<0) d=0;
  while (d--)
  {
  	if ( (1<<(d&7)) & mask )  // check mask
    {
  		x1=x>>16;y1=y>>16;
	    if (x1>=clip.x && y1>=clip.y && x1<clip.w && y1<clip.h)
				if (!fx)
					ik_putpixel(img, x1,y1,c1); 
				else
					ik_putpixel_add(img, x1,y1,c1); 
   	}
    else if (c2>0)
    {
  		x1=x>>16;y1=y>>16;
	    if (x1>=clip.x && y1>=clip.y && x1<clip.w && y1<clip.h)
				if (!fx)
					ik_putpixel(img, x1,y1,c2); 
				else
					ik_putpixel_add(img, x1,y1,c2); 
   	}

  	x+=dx; y+=dy;
  }
}

void ik_drawbox(t_ik_image *img, int32 xb, int32 yb, int32 xe, int32 ye, int32 c)
{
	int32 y;
	int32 w;
	uint8 *p;

	if (xe < xb) { w = xe; xe = xb; xb = w; }
	if (ye < yb) { w = ye; ye = yb; yb = w; }

	y = MAX(yb, clip.y);
	p = (uint8 *)img->pixels + y*img->pitch + MAX(xb, clip.x);
	w = MIN(xe+1, clip.w) - MAX(xb, clip.x);

	while (y < MIN(ye+1,clip.h))
	{
		memset(p, c, w);
		y++; p += img->pitch;
	}

	/*
	for (y=MAX(yb,clip.y); y<MIN(ye+1,clip.h); y++)
		for (x=MAX(xb,clip.x); x<MIN(xe+1,clip.w); x++)
		{
			ik_putpixel(img, x, y, c);
		}
	*/
}

void ik_copybox(t_ik_image *src, t_ik_image *dst, int32 xb, int32 yb, int32 xe, int32 ye, int32 xd, int32 yd)
{
	int32 y;

	for (y=0;y<ye-yb;y++)
		memcpy(ik_image_pointer(dst,xd,y+yd),ik_image_pointer(src,xb,y+yb),xe-xb);
}

void ik_drawmeter(t_ik_image *img, int32 xb, int32 yb, int32 xe, int32 ye, int32 typ, int32 val, int32 c, int32 c2)
{
	ik_drawbox(img, xb, yb, xe, ye, c*16+12);
	ik_drawbox(img, xb+1, yb+1, xe-1, ye-1, c*16+1);
	if (typ==0) // vert
	{
		val = (val * (ye-yb-2)) / 100;
		ik_drawbox(img, xb+1, ye-1-val, xe-1, ye-1, c2);
	}
	else // horiz
	{
		val = (val * (xe-xb-2)) / 100;
		ik_drawbox(img, xb+1, yb+1, xb+1+val, ye-1, c2);
	}
}

// FIND RGB COLOR

int32 get_rgb_color(int32 r, int32 g, int32 b)
{
	int32 c,e,ee,x,r1,g1,b1;
	int32 c0;

	c=0;ee=200000;
	for (x=0;x<256;x++)
	{
		c0=get_palette_entry(x);
		r1=r-((c0>>16)&255); 
		g1=g-((c0>>8)&255); 
		b1=b-(c0&255);
		e=r1*r1+g1*g1+b1*b1;
		if (e<ee)
		{ c=x; ee=e; }
	}
	
	return c;
}

// CALCULATE COLOR TABLES

void calc_color_tables(uint8 *pal)
{
	int32 x,y;
	FILE *colormap;

	gfx_addbuffer=(unsigned char*)malloc(65536);
	gfx_transbuffer=(unsigned char*)malloc(65536);
	gfx_lightbuffer=(unsigned char*)malloc(65536);

	if (gfx_transbuffer==NULL || gfx_lightbuffer==NULL || gfx_addbuffer==NULL)
		return;  // fail

	colormap=myopen("graphics/colormap.dat", "rb");
	if (colormap)
	{
		fread(gfx_transbuffer, 1, 65536, colormap);
		fread(gfx_lightbuffer, 1, 65536, colormap);
		fread(gfx_addbuffer, 1, 65536, colormap);
		fclose(colormap);
		return;
	}

	for (y=0;y<256;y++)
		for (x=0;x<256;x++)
		{			
			gfx_transbuffer[y*256+x]=get_rgb_color( ((int32)pal[y*3]+pal[x*3])>>1,
																							((int32)pal[y*3+1]+pal[x*3+1])>>1,
																							((int32)pal[y*3+2]+pal[x*3+2])>>1 );
			gfx_lightbuffer[y*256+x]=get_rgb_color( ((int32)pal[y*3]*pal[x*3])>>8,
																							((int32)pal[y*3+1]*pal[x*3+1])>>8,
																							((int32)pal[y*3+2]*pal[x*3+2])>>8 );
			gfx_addbuffer[y*256+x]=get_rgb_color( MIN((int32)pal[y*3]+pal[x*3],255),
																						MIN((int32)pal[y*3+1]+pal[x*3+1],255),
																						MIN((int32)pal[y*3+2]+pal[x*3+2],255) );
		}

	colormap=myopen("graphics/colormap.dat","wb");

	fwrite(gfx_transbuffer, 1, 65536, colormap);
	fwrite(gfx_lightbuffer, 1, 65536, colormap);
	fwrite(gfx_addbuffer, 1, 65536, colormap);

	fclose(colormap);
}

void del_color_tables()
{
	if (gfx_transbuffer)  free(gfx_transbuffer);
	if (gfx_lightbuffer)  free(gfx_lightbuffer);
	if (gfx_addbuffer)  free(gfx_addbuffer);
}

// GENERATE OR LOAD IMAGE STRUCTS

t_ik_image *new_image(int32 w, int32 h)
{
	t_ik_image *img;
	uint32 colorkey, i;
	SDL_Color spal[256]; 

	img = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);

	for(i=0;i<256;i++) { 
		spal[i].r = globalpal[i*3]; 
		spal[i].g = globalpal[i*3+1]; 
		spal[i].b = globalpal[i*3+2]; 
	} 
	SDL_SetColors(img, spal, 0, 256); 
	SDL_SetColorKey(img, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0);

	return img;

}

void del_image(t_ik_image *img)
{
	SDL_FreeSurface(img);
}

t_ik_image *ik_load_pcx(char *fname, uint8 *pal)
{
	int length, blocks;
	t_ik_image *image;
	SDL_RWops *file;

	file = SDL_RWFromFile(fname, "rb");
	image=IMG_LoadPCX_RW(file);
	if(!image) {
		    printf("IMG_LoadPCX_RW: %s\n", IMG_GetError());
				return NULL;
	}

	// read palette from the end
	if (pal)
	{
		length=SDL_RWseek(file,-768,SEEK_END);
		if(length<0) {
			printf("Unable to seek inside %s!  (Bogus palette?)\n", fname);
		}

		blocks=SDL_RWread(file,pal,1,768);
		if(blocks<0) {
			printf("Unable to read palette from %s!\n", fname);
		}
	}

	SDL_RWclose(file);

	return image;					
}

t_ik_image *ik_load_tga(char *fname, uint8 *pal) 
{
	int length, blocks;
	t_ik_image *image;
	SDL_RWops *file;
	int p;

	file = SDL_RWFromFile(fname, "rb");
	image=IMG_LoadTGA_RW(file);
	if(!image) {
		    printf("IMG_LoadTGA_RW: %s\n", IMG_GetError());
				return NULL;
	}

	// read palette from the end
	if (pal)
	{
		length=SDL_RWseek(file,0,SEEK_SET);
		if(length<0) {
			printf("Unable to seek inside %s!  (Bogus palette?)\n", fname);
		}

		for (p = 0; p < 256; p++)
		{
			pal[p*3+2] = SDL_RWread(file,pal+(p*3+2),1,1);
			pal[p*3+1] = SDL_RWread(file,pal+(p*3+1),1,1);
			pal[p*3] = SDL_RWread(file,pal+p*3,1,1);
		}
		if(blocks<0) {
			printf("Unable to read palette from %s!\n", fname);
		}
	}

	SDL_RWclose(file);

	return image;
}


void ik_save_screenshot(t_ik_image *img, uint8 *pal)
{
	int n;
	FILE *fil;
	char fname[32];
	
	wants_screenshot = 0;

#ifdef MOVIE
	n = aframe;
	aframe++;
	sprintf(fname, "frames/fram%04d.tga", n);
	ik_save_tga(fname, img, pal);
#else
	
	n=0;
	while (n<1000)
	{
		sprintf(fname, "shot%04d.tga", n);
		fil = myopen(fname, "rb");
		if (!fil)
		{
			ik_save_tga(fname, img, pal);
			break;
		}
		else fclose(fil);

		n++;
	}
#endif
}

void ik_save_tga(char *fname, t_ik_image *img, uint8 *pal)
{
	int p;
	uint8 hdr[18] = {
		0, 1, 1,								// id_len, pal_type, img_type
		0, 0, 0, 1,	24,					// first_color, num_colors, pal_size
		0, 0, 0, 0,							// left, top
		img->w&255, img->w>>8,	// width
		img->h&255, img->h>>8,	// height
		8, 8										// bpp, des_bits
		};
	FILE *fil;

	fil = myopen(fname, "wb");
	if (!fil)
		return;

	// write header
	fwrite(hdr, 1, 18, fil);

	// write palette
	for (p = 0; p < 256; p++)
	{
		fputc(get_palette_entry(p)&255, fil);
		fputc((get_palette_entry(p)>>8)&255, fil);
		fputc(get_palette_entry(p)>>16, fil);
	}

	// write data
	for (p = img->h; p > 0; p--)
	{
		fwrite((uint8 *)img->pixels + (p-1)*img->pitch, 1, img->w, fil);
	}

	fclose(fil);

}

int get_direction(int32 dx, int32 dy)
{
	int32 a;

	if (dx==0 && dy==0)
		return 0;

	a = (int32)(atan2(dx, dy)*512/3.14159);
	a = (a + 1024) & 1023;

	return a;
}

int get_distance(int32 dx, int32 dy)
{
	int32 r;

	if (dx==0 && dy==0)
		return 0;

	r = (int32)sqrt(dx*dx + dy*dy);

	return r;
}

void halfbritescreen()
{
	int32 x, y;
	int32 l;
	uint8 *po;

	if (num_dims >= 8)
		return;

	prep_screen();
	dims[num_dims] = new_image(screen->w, screen->h);
	if (!dims[num_dims])
	{	free_screen(); return; }

	ik_copybox(screen, dims[num_dims], 0, 0, screen->w, screen->h, 0, 0);
	num_dims++;

	if (num_dims > 1)
		l = 15;
	else
		l = 11;

	l<<=8;

	for (y = 0; y < screen->h; y++)
	{
		po = (uint8 *)screen->pixels + screen->pitch*y;
		for (x = screen->w; x; x--)
		{	
			*po = gfx_lightbuffer[l+*po];
			po++;
		}
	}
	free_screen();
}

void reshalfbritescreen()
{
	if (num_dims <= 0)
		return;

	prep_screen();

	num_dims--;
	ik_copybox(dims[num_dims], screen, 0, 0, screen->w, screen->h, 0, 0);
	del_image(dims[num_dims]);
	dims[num_dims]=NULL;

	free_screen();
}

void resallhalfbritescreens()
{
	if (num_dims <= 0)
		return;

	prep_screen();

	while (num_dims)
	{
		num_dims--;
		ik_copybox(dims[num_dims], screen, 0, 0, screen->w, screen->h, 0, 0);
		del_image(dims[num_dims]);
		dims[num_dims]=NULL;
	}
	free_screen();
}

extern t_ik_spritepak *spr_IFbutton;

void ik_draw_mousecursor()
{
	ik_dsprite(screen, ik_mouse_x, ik_mouse_y, spr_IFbutton->spr[2], 0);
}

extern t_ik_spritepak *spr_SMraces;

void gfx_blarg()
{
	ik_dsprite(screen, 564, 448, spr_SMraces->spr[7], 0);
}

void gfx_initmagnifier()
{
	int x, y, r;
#ifndef THICK_MAGNIFIER
	int p;
#endif
	uint8 *data;

	magni = new_image(128, 128);
	data = (uint8 *)magni->pixels;
	for (y = 0; y < 128; y++)
		for (x = 0; x < 128; x++)
		{
			r = (int)sqrt ( (y+y-127)*(y+y-127) + (x+x-127)*(x+x-127) );
#ifdef THICK_MAGNIFIER
			if (r < 124)
				data[y*128+x] = 1;
			else if (r < 128)
				data[y*128+x] = 2;
			else 
				data[y*128+x] = 0;
#else
			if (r < 124)
				data[y*128+x] = 1;
			else 
				data[y*128+x] = 0;
#endif
		}
#ifndef THICK_MAGNIFIER
	p = 0;
	for (y = 0; y < 128; y++)
		for (x = 0; x < 128; x++)
		{
			if (!data[p])
			{
				r = 0;
				if (y > 0)
					if (data[p-128] == 1)
						r++;
				if (y < 127)
					if (data[p+128] == 1)
						r++;
				if (x > 0)
					if (data[p-1] == 1)
						r++;
				if (x < 127)
					if (data[p+1] == 1)
						r++;
				if (r)
					data[p]=2;
			}
			p++;
		}
#endif
}

void gfx_deinitmagnifier()
{
	del_image(magni);
}

void gfx_magnify()
{
	t_ik_sprite *mag;
	int  y;	// x
	unsigned char *p;
	//unsigned char *m;

//	mag = get_sprite(screen, ik_mouse_x-64, ik_mouse_y-64, 128, 128);
	mag = get_sprite(screen, ik_mouse_x-96, ik_mouse_y-48, 192, 96);
	p = (unsigned char *)mag->data;
	y = mag->h * mag->w;
	while (y--)
	{
		if (!*p)
			*p = 16;
		p++;
	}
/*
	p = mag->data;
	m = magni->data;
	for (y = 0; y < mag->h; y++)
		for (x = 0; x < mag->w; x++)
		{
			//if (y == 0 || x == 0 || y == mag->h-1 || x == mag->w-1)
			//	*p = 178;
			//else

			if (*m == 1)
			{
				if (!*p)
					*p = 16;
			}
			else if (*m == 2)
			{
				*p = 178;
			}
			else
				*p = *m;
			
			p++; m++;
		}

	ik_drsprite(screen, ik_mouse_x+1, ik_mouse_y+1, 0, 256, mag, 0);
	*/
	ik_drsprite(screen, ik_mouse_x+1, ik_mouse_y+1, 0, 384, mag, 0);
	interface_thinborder(screen, ik_mouse_x-192, ik_mouse_y-96, ik_mouse_x+192, ik_mouse_y+96, 11, -1);
	/*
	ik_drawline(screen, ik_mouse_x-192, ik_mouse_y-96, ik_mouse_x+191, ik_mouse_y-96, 178, 0, 255, 0);
	ik_drawline(screen, ik_mouse_x-192, ik_mouse_y+95, ik_mouse_x+191, ik_mouse_y+95, 178, 0, 255, 0);
	ik_drawline(screen, ik_mouse_x-192, ik_mouse_y-96, ik_mouse_x-192, ik_mouse_y+95, 178, 0, 255, 0);
	ik_drawline(screen, ik_mouse_x+191, ik_mouse_y-96, ik_mouse_x+191, ik_mouse_y+95, 178, 0, 255, 0);
	*/
	free_sprite(mag);
}
// ex:ts=2 sw=2
