// ----------------
//     INCLUDES
// ----------------

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <malloc.h>
//#include <io.h>
#include <glob.h>

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

//		FILE *loggy;

// --------------------------------------------
//      SPRITE CREATION AND MANAGEMENT
// --------------------------------------------

// CREATE NEW SPRITE TEMPLATE
t_ik_sprite *new_sprite(int32 w, int32 h)
{
	t_ik_sprite *spr;
	int i;

	spr=(t_ik_sprite *)malloc(sizeof(t_ik_sprite));
	if (!spr)
		return NULL;

	spr->image=new_image(w,h);
	spr->data=(uint8 *)spr->image->pixels;
	spr->w=spr->image->w;
	spr->h=spr->image->h;
	spr->co=0;
	for(i=0; i < 16; i++) {
		spr->co_cache[i] = NULL;
	}


	return spr;
}

// FIND APPROXIMATE COLOR OF SPRITE
int32 calc_sprite_color(t_ik_sprite *spr)
{
	int32 x,y,r,g,b,npx,c;

	if (!spr)
		return 0;

	r=0;g=0;b=0;npx=0;
	for (y=0;y<spr->h;y++)
	for (x=0;x<spr->w;x++)
	{
		c=spr->data[y*spr->w+x];
		if (c)
		{
			c=get_palette_entry(c);
			r+=(c>>16)&255; g+=(c>>8)&255; b+=c&255;
//			r+=pal[c*3]; g+=pal[c*3+1]; b+=pal[c*3+2];
			npx++;
		}
	}

	if (npx>0)
	{
		spr->co=get_rgb_color(r/npx,g/npx,b/npx);
	}

	return spr->co;
}

// GRAB SPRITE FROM IMAGE
t_ik_sprite *get_sprite(t_ik_image *img, int32 x, int32 y, int32 w, int32 h)
{
	t_ik_sprite *spr;
	SDL_Rect srcr, destr;

	if (!img)
		return NULL;

	spr=new_sprite(w,h);
	if (!spr)
		return NULL;

	srcr.x = x;
	srcr.y = y;
	srcr.w = w;
	srcr.h = h;
	destr.x = destr.y = 0;
	destr.w = spr->w;
	destr.h = spr->h;

	SDL_BlitSurface(img, &srcr, spr->image, &destr);

	calc_sprite_color(spr);

	return spr;
}

// DESTROY SPRITE AND FREE MEMORY
void free_sprite(t_ik_sprite *spr)
{
	int i;
	if (spr)
	{
		del_image(spr->image);
		for(i = 0; i < 16; i++) 
			del_image(spr->co_cache[i]); 
		free(spr);
	}
}

// --------------------------------------------
//     SPRITEPAK CREATION AND MANAGEMENT
// --------------------------------------------

t_ik_spritepak *new_spritepak(int32 num)
{
	t_ik_spritepak *pak;

	pak = (t_ik_spritepak*)calloc(1, sizeof(t_ik_spritepak));
	if (!pak)
		return NULL;

	pak->num = num;
	pak->spr = (t_ik_sprite**)calloc(num, sizeof(t_ik_sprite*));

	return pak;
}

void free_spritepak(t_ik_spritepak *pak)
{
	int x;

	if (!pak)
		return;

	for (x = 0; x < pak->num; x++)
	{
		free_sprite(pak->spr[x]);
		pak->spr[x]=NULL;
	}
	free(pak->spr);
	free(pak);
}

t_ik_spritepak *load_sprites(char *fname)
{
	// NOTE: load_sprites loads default .SPR, and FRAMES from the mod
	glob_t globbuf;
	int i;
	int fnum;
	char spritedir[256];
	char framename[256];
	int rep[256];
	int max;
	t_ik_image *img;
	uint8 *buffu;

	FILE *fil;
	t_ik_spritepak *pak;
	int32 x,num;
	int32 w,h,c;

/*
if loading a mod, look at the folder for new frames
and mark them in the replacement array.
*/
	for (x = 0; x < 256; x++)
		rep[x] = 0;
	max = 0;
	if (strlen(moddir))	
	{
		sprintf(spritedir, "%s%s", moddir, fname);
		sprintf(spritedir+strlen(spritedir)-4, "/\0");
		sprintf(framename, "%sframe*.tga", spritedir);

		if (glob(framename, 0, NULL, &globbuf))
		{
			for(i = 0; i< globbuf.gl_pathc; i++)
			{
				sscanf(globbuf.gl_pathv[i]+5, "%03d", &fnum);
				if (fnum < 256)
				{
					rep[fnum] = 1;
					if (fnum+1 > max)
						max = fnum+1;
				}
			}
			globfree(&globbuf);
		}
	}

	fil=fopen(fname,"rb");	// don't use myopen here
	if (!fil)
		return NULL;

	num=fgetc(fil);
	num+=fgetc(fil)*256;

	if (num > max)
		max = num;

	pak = new_spritepak(max);
	if (!pak)
	{ fclose(fil); return NULL; }

	for (x=0;x<max;x++)
	{
		// header
		if (x < num)
		{
			w=fgetc(fil);
			w+=fgetc(fil)*256;
			h=fgetc(fil);
			h+=fgetc(fil)*256;
			c = fgetc(fil);
			fgetc(fil);
			fgetc(fil);
			fgetc(fil);
			buffu = (uint8*)malloc(w * h);
			fread(buffu,1,w*h,fil);

			if (!rep[x])
			{
			// if not marked as rep, make new sprite
				pak->spr[x]=new_sprite(w,h);
				pak->spr[x]->co=c;
				// data
				memcpy(pak->spr[x]->data, buffu, w*h);
			}

			free(buffu);
		}
		if (rep[x])
		{
			sprintf(framename, "%sframe%03d.tga", spritedir, x);
			img = ik_load_tga(framename, NULL);
			if (img)
			{
				pak->spr[x]=get_sprite(img, 0, 0, img->w, img->h);
				del_image(img);
			}
		}
	}

	fclose(fil);

	return pak;
}

void save_sprites(char *fname, t_ik_spritepak *pak)
{
	FILE *fil;
	int32 x;
	int32 num = pak->num;

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

	fputc(num&255, fil);
	fputc(num>>8, fil);

	for (x=0;x<num;x++)
	{
		// header
		fputc(pak->spr[x]->w & 255,fil);
		fputc(pak->spr[x]->w >> 8,fil);
		fputc(pak->spr[x]->h & 255,fil);
		fputc(pak->spr[x]->h >> 8,fil);
		fputc(pak->spr[x]->co,fil);
		// filler
		fputc(0,fil);
		fputc(0,fil);
		fputc(0,fil);
		// data
		fwrite(pak->spr[x]->data,1,pak->spr[x]->w*pak->spr[x]->h,fil);
	}
}

// --------------------------------------------
//	      SPRITE DRAWING FUNCTIONS
// --------------------------------------------

// basic sprite draw.. corner align, 0-masked
// flags:
// 1:  center align (move up-left by half the size)
// 2:  color 
// 4:  blank
void ik_dsprite(t_ik_image *img, int32 x, int32 y, t_ik_sprite *spr, int32 flags)
{
	uint8 co=0;
	int i;
	SDL_Rect srcr, destr;
	t_ik_image *myimg;
	uint8 *data;

	if (flags&1) { x-=spr->w>>1; y-=spr->h>>1; }  // centered
	if (flags&2) { co=flags>>8; }  // colored
	if (flags&4) ik_drawbox(img, x, y, x+spr->w-1, y+spr->h-1, 0);

	myimg = spr->image;

	srcr.x = srcr.y = 0;
	srcr.w = spr->w;
	srcr.h = spr->h;
	destr.x = x;
	destr.y = y;
	destr.w = img->w;
	destr.h = img->h;

	if(co) {
		if(spr->co_cache[co]) {
			myimg = spr->co_cache[co];
		} else {
			myimg = new_image(spr->w, spr->h);
			data = (uint8 *)myimg->pixels;

			for (i=0; i < spr->w*spr->h; i++) {
				if (spr->data[i] && spr->data[i] < 16) {
					data[i] = spr->data[i] + co*16;
				} else {
					data[i] = spr->data[i];
				}
			}
			spr->co_cache[co] = myimg; 
		}
	}

	SDL_BlitSurface(myimg, &srcr, img, &destr);

}

// basic rsprite draw.. center align, rotation, scale (0-masked)
// flags:
// 1:  Light   (flags = 1 + lightcolor*256)
// 2:  Trans   (50% transparency)
// 4:  Add     
void ik_drsprite(t_ik_image *img, int32 x, int32 y, int32 r, int32 s, t_ik_sprite *spr, int32 flags)
{
  int32 x1,y1,x2;
	int32 size;
	int32 xt,yt,c,cx,cy;
	int32 cutleft, cutright;
	int32 dx,dy;
  uint8 *p1;

	if (s<=2) 
	{	
		c = spr->co;
		if (c)
		{
			if (y>=clip.y && x>=clip.x && x<clip.w && y<clip.h)
			{
				if (flags)
				{
			    p1=ik_image_pointer(img, x, y);

					if (flags&1)  c=gfx_lightbuffer[(c<<8)+(flags>>8)];
					if (flags&2)  c=gfx_transbuffer[(c<<8)+(*p1)];
					if (flags&4)  c=gfx_addbuffer[(c<<8)+(*p1)];

				  *p1=c;
				}
				else
					ik_putpixel(img, x,y,c);
			}
		}
		return;
	}

	s=(s<<10)/MAX(spr->w,spr->h);
  size=MAX(spr->w,spr->h)*s>>11; 

  if (x<clip.x-size || y<clip.y-size || x>=clip.w+size || y>=clip.h+size) return;

	r &= 1023;
	dx = cos1k[r]*1024/s;
	dy = -sin1k[r]*1024/s;
	cx=(spr->w+1)<<15;
	cy=(spr->h+1)<<15;

	size=(int32)(size*1.4);

  for (y1=-size; y1<size; y1++)
  {
  	if (y1+y>=clip.y && y1+y<clip.h)
    {
      x2=x+size;if (x2>clip.w) x2=clip.w;
			x1=x-size;if (x1<clip.x) x1=clip.x;
			xt=cx+(x1-x)*dx-y1*dy;
			yt=cy+y1*dx+(x1-x)*dy;

			cutleft=0; cutright=0;
			// Clamp X
			if (dx>0)
			{
				if (xt+(x2-x1)*dx>spr->w<<16) 	cutright=MAX(cutright, (xt+(x2-x1)*dx-(spr->w<<16))/dx); 
				if (xt<0)												cutleft=MAX(cutleft, -xt/dx+1); 
			}
			else if (dx<0)
			{
				if (xt+(x2-x1)*dx<0)			cutright=MAX(cutright, (xt+(x2-x1)*dx)/dx); 
				if (xt>spr->w<<16)				cutleft=MAX(cutleft, -(xt-(spr->w<<16))/dx+1);
			}
			else if (xt<0 || xt>=spr->w<<16)	 x2=x1;   // don't draw hline

			// Clamp Y
			if (x2>x1)
			if (dy>0)
			{
				if (yt+(x2-x1)*dy>spr->h<<16)			cutright=MAX(cutright, (yt+(x2-x1)*dy-(spr->h<<16))/dy);
				if (yt<0)													cutleft=MAX(cutleft, -yt/dy+1);
			}
			else if (dy<0)
			{
				if (yt+(x2-x1)*dy<0)				cutright=MAX(cutright, (yt+(x2-x1)*dy)/dy); 
				if (yt>spr->h<<16)					cutleft=MAX(cutleft, -(yt-(spr->h<<16))/dy+1); 
			}
			else if (yt<0 || yt>=spr->h<<16)	x2=x1;  // don't draw hline

			// Apply clamps
			if (cutleft)
			{ xt+=dx*cutleft; yt+=dy*cutleft; x1+=cutleft; }
			if (cutright)
			{ x2-=cutright; }

      p1=ik_image_pointer(img, x1, y1+y);

			// innerloops
			if (!flags)  // "clean" .. no special fx .. fast
				for (;x1<x2;x1++)
		    {
					c=spr->data[(yt>>16)*spr->w+(xt>>16)];
					if (c)
					  *p1=c;

	        p1++;
					xt+=dx; yt+=dy;
				}
			else  // light, transparency, additive
				for (;x1<x2;x1++)
		    {
					c=spr->data[(yt>>16)*spr->w+(xt>>16)];
					if (c)
					{
						if (flags&1)  c=gfx_lightbuffer[(c<<8)+(flags>>8)];
						if (flags&2)  c=gfx_transbuffer[(c<<8)+(*p1)];
						if (flags&4)  c=gfx_addbuffer[(c<<8)+(*p1)];
					  *p1=c;
					}

	        p1++;
					xt+=dx; yt+=dy;
				}    
    }
  }
}

// sprite line draw.. line of tiled sprites (useful for laser beams etc)
// flags:
// 1:  Light   (flags = 1 + lightcolor*256)
// 2:  Trans   (50% transparency)
// 4:  Add     

void ik_dspriteline(t_ik_image *img, int32 xb, int32 yb, int32 xe, int32 ye, int32 s, 
										int32 offset, int32 ybits, t_ik_sprite *spr, int32 flags)
{
	double r;
  int32 x1,y1,x2;
	int32 size;
	int32 xt,yt,c,cx;
	int32 cutleft, cutright;
	int32 dx,dy;
	int32 xl0,yl0,xl1,yl1,topy;
  uint8 *p1;

	if (s<=2) 
	{	
		ik_drawline(img, xb,yb,xe,ye,spr->co);
		return;
	}

	s=(s<<6)/MAX(spr->w,spr->h);
  size=MAX(spr->w,spr->h)*s>>7; 

	xl0=MAX(clip.x, MIN(xb-size, xe-size));
	xl1=MIN(clip.w, MAX(xb+size, xe+size));
	yl0=MAX(clip.y, MIN(yb-size, ye-size));
	yl1=MIN(clip.h, MAX(yb+size, ye+size));

	if (xl0>xl1 || yl0>yl1) return;  // if clipped out
  if (xl1<clip.x || yl1<clip.y || xl0>=clip.w || yl0>=clip.h) return;

	r=atan2(xe-xb, yb-ye);
	dx=(int32)(cos(r)*65536*64/s);
	dy=-(int32)(sin(r)*65536*64/s);
	cx=(spr->w+1)<<15;

//	topy=-(int32)sqrt((xe-xb)*(xe-xb)+(ye-yb)*(ye-yb))*(65536*64/s);
	topy=(ye-yb)*dx+(xe-xb)*dy;

  for (y1=yl0; y1<yl1; y1++)
  {
  	if (y1>=clip.y && y1<clip.h)
    {
      x2=xl1;if (x2>clip.w) x2=clip.w;
			x1=xl0;if (x1<clip.x) x1=clip.x;
			xt=cx+(x1-xb)*dx-(y1-yb)*dy;
			yt=(y1-yb)*dx+(x1-xb)*dy;

			cutleft=0; cutright=0;
			// Clamp X
			if (dx>0)
			{
				if (xt+(x2-x1)*dx>spr->w<<16) 	cutright=MAX(cutright, (xt+(x2-x1)*dx-(spr->w<<16))/dx); 
				if (xt<0)												cutleft=MAX(cutleft, -xt/dx+1); 
			}
			else if (dx<0)
			{
				if (xt+(x2-x1)*dx<0)			cutright=MAX(cutright, (xt+(x2-x1)*dx)/dx); 
				if (xt>spr->w<<16)				cutleft=MAX(cutleft, -(xt-(spr->w<<16))/dx+1);
			}
			else if (xt<0 || xt>=spr->w<<16)	x2=x1;  // don't draw hline

			// Clamp Y
			if (x2>x1)
			if (dy>0)
			{
				if (yt+(x2-x1)*dy>0)		cutright=MAX(cutright, (yt+(x2-x1)*dy)/dy);
				if (yt<topy)						cutleft=MAX(cutleft, (topy-yt)/dy+1);
			}
			else if (dy<0)
			{
				if (yt+(x2-x1)*dy<topy)		cutright=MAX(cutright, -(topy-(yt+(x2-x1)*dy))/dy+1); 
				if (yt>0)									cutleft=MAX(cutleft, -yt/dy+1); 
			}
			else if (yt<topy || yt>=0)	 x2=x1;   // don't draw hline

			// Apply clamps
			if (cutleft)
			{ xt+=dx*cutleft; yt+=dy*cutleft; x1+=cutleft; }
			if (cutright)
			{ x2-=cutright; }

      p1=ik_image_pointer(img, x1, y1);

			// innerloops
			if (!flags)  // "clean" .. no special fx .. fast
				for (;x1<x2;x1++)
		    {
					c=spr->data[(((yt>>ybits)+offset)&(spr->h-1))*spr->w+(xt>>16)];
					if (c)
					  *p1=c;

	        p1++;
					xt+=dx; yt+=dy;
				}
			else  // light, transparency, additive
				for (;x1<x2;x1++)
		    {
					c=spr->data[(((yt>>ybits)+offset)&(spr->h-1))*spr->w+(xt>>16)];
					if (c)
					{
						if (flags&1)  c=gfx_lightbuffer[(c<<8)+(flags>>8)];
						if (flags&2)  c=gfx_transbuffer[(c<<8)+(*p1)];
						if (flags&4)  c=gfx_addbuffer[(c<<8)+(*p1)];
					  *p1=c;
					}

	        p1++;
					xt+=dx; yt+=dy;
				}    
    }
  }
}

// ex:ts=2 sw=2
