#include <SDL.h>
//#include <SDL_image.h>
#include "SDLX.h"

//data types
//struct SDLX_Bitmap
//{
//  SDL_Surface* surface; //surface is used when we have a software buffer 
//  SDL_Texture* texture; //texture is used when directly writing to renderer
//	int width;
//	int height;
//};
//struct SDLX_Color
//{
//	unsigned char r;
//	unsigned char g;
//	unsigned char b;
//	unsigned char a;
//};
//
//struct SDLX_Event
//{
//	SDL_Event sdl_event;
//	SDL_Keycode keycode;
//	Uint8 button;
//	Uint32 type;
//};

//global variables
SDL_Window* window;
SDL_Renderer *renderer;
SDL_Surface* buffer;
SDL_Keycode last_code;
SDLX_Color color_mask;
bool use_buffer = false; //not available to apps

//functions

//SDL system functions
//SDLX_Init: the only note when using a buffer:
//if we are not using a buffer, then we have created a texture for all bitmaps to be used in rendering
//   in that case, after any putpixel and before draw, we have to refresh the bitmap texture by calling SDLX_CreateBitmapTexture 
//   so, PutPixel deletes the texture and DrawBitmap recreates it if NULL
bool SDLX_Init(const char* title, int w, int h, bool useBuffer)
{
	use_buffer = useBuffer;

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
		return false;

	//SDL_Image
	//IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF);

	window = SDL_CreateWindow(title, 50, 50, w, h, SDL_WINDOW_SHOWN);
	if (window == NULL)
	{
		SDL_Quit();
		return false;
	}

	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
	if (renderer == NULL)
	{
		SDL_DestroyWindow(window);
		SDL_Quit();
		return false;
	}
	SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); //doesn't seem to be needed

	//create a buffer surface for rendering
	Uint32 rmask, gmask, bmask, amask;
	/* SDL interprets each pixel as a 32-bit number, so our masks must depend
	on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	rmask = 0xff000000;
	gmask = 0x00ff0000;
	bmask = 0x0000ff00;
	amask = 0x000000ff;
#else
	rmask = 0x000000ff;
	gmask = 0x0000ff00;
	bmask = 0x00ff0000;
	amask = 0xff000000;
#endif
	if (use_buffer)
		buffer = SDL_CreateRGBSurface(0, w, h, 32, rmask, gmask, bmask, amask);
	else
		buffer = NULL;

	//which color is transparent
	color_mask.r = color_mask.g = color_mask.b = color_mask.a = 255;

	return true;
}
void SDLX_End()
{
	SDL_FreeSurface(buffer);
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
}
void SDLX_Delay(int n)
{
	SDL_Delay(n);
}
bool SDLX_PollEvent(SDLX_Event* e)
{
	bool res = SDL_PollEvent(&e->sdl_event);
	e->type = e->sdl_event.type;
	e->keycode = e->sdl_event.key.keysym.sym;
	e->button = e->sdl_event.button.button;
	return res;
}

//use these two when using a buffer
void SDLX_ClearBuffer()
{
	SDL_FillRect(buffer, NULL, SDL_MapRGB(buffer->format, 0, 0, 0));
}
void SDLX_RenderBuffer()
{
	SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, buffer);
	SDL_RenderCopy(renderer, tex, NULL, NULL);
	SDL_RenderPresent(renderer);
	SDL_DestroyTexture(tex);
}
//use these two when not using a buffer 
void SDLX_Render()
{
	if (use_buffer)
		SDLX_RenderBuffer();
	else
		SDL_RenderPresent(renderer);
}
void SDLX_Clear()
{
	if (use_buffer)
		SDLX_ClearBuffer();
	else
		SDL_RenderClear(renderer);
}

//bitmaps
//if we are using texture for rendering we have to call this at the start and before render for a frame if pixels have changed
void SDLX_CreateBitmapTexture(SDLX_Bitmap* bmp)
{
	if (bmp->texture != NULL)
		SDL_DestroyTexture(bmp->texture);
	bmp->texture = SDL_CreateTextureFromSurface(renderer, bmp->surface);
	SDL_SetTextureBlendMode(bmp->texture, SDL_BLENDMODE_BLEND); //does not seem to be needed
}
void SDLX_DestroyBitmap(SDLX_Bitmap* bmp)
{
	if (bmp != NULL)
	{
		if (bmp->texture != NULL)
			SDL_DestroyTexture(bmp->texture);
		if (bmp->surface != NULL)
			SDL_FreeSurface(bmp->surface);
		delete bmp;
	}
}

SDLX_Bitmap* SDLX_LoadBitmap(const char * fname, bool useMask, SDLX_Color* mask)
{
	SDLX_Bitmap* bmp = new SDLX_Bitmap();
	bmp->surface = NULL;
	bmp->texture = NULL;

	//load the file
	bmp->surface = SDL_LoadBMP(fname); //IMG_Load(fname); // 
	
	if (bmp->surface == NULL)
	{
		SDLX_DestroyBitmap(bmp);
		return NULL;
	}
	SDL_SetSurfaceBlendMode(bmp->surface, SDL_BLENDMODE_BLEND);
	bmp->width = bmp->surface->w;
	bmp->height = bmp->surface->h;

	if (useMask)
	{
		if (mask == NULL)
			SDL_SetColorKey(bmp->surface, SDL_TRUE, SDL_MapRGB(bmp->surface->format, color_mask.r, color_mask.g, color_mask.b));
		else
			SDL_SetColorKey(bmp->surface, SDL_TRUE, SDL_MapRGB(bmp->surface->format, mask->r, mask->g, mask->b));
	}

	if (!use_buffer)
	{
		SDLX_CreateBitmapTexture(bmp);
	}

	return bmp;
}

void SDLX_SetTransparentColor(SDLX_Bitmap* bmp, char r, char g, char b)
{
	SDL_SetColorKey(bmp->surface, SDL_TRUE, SDL_MapRGB(bmp->surface->format, r, g, b));
}

//can use surface/buffer or textre/renderer
void SDLX_DrawBitmap(SDLX_Bitmap* bmp, int x, int y, int v)
{
	if (bmp == NULL)
		return;

	SDL_Rect rect;
	rect.x = x;
	rect.y = y;
	rect.w = bmp->width;
	rect.h = bmp->height;
	//if we have created the texture, then write it to renderer
	//if not, we are using the buffer
	if (!use_buffer)
	{
		if (bmp->texture == NULL)
			SDLX_CreateBitmapTexture(bmp);
		Uint8 a;
		SDL_GetTextureAlphaMod(bmp->texture, &a);
		SDL_SetTextureAlphaMod(bmp->texture, v * 255 / 100);
		SDL_RenderCopy(renderer, bmp->texture, NULL, &rect);
		SDL_SetTextureAlphaMod(bmp->texture, a);
	}
	else
	{
		Uint8 a;
		SDL_GetSurfaceAlphaMod(bmp->surface, &a);
		SDL_SetSurfaceAlphaMod(bmp->surface, v * 255 / 100);
		SDL_BlitSurface(bmp->surface, NULL, buffer, &rect);
		SDL_SetSurfaceAlphaMod(bmp->surface, a);
	}
}

bool SDLX_IsPixelMask(SDLX_Bitmap* bmp, SDLX_Color* c)
{
	Uint32 key;
	Uint8 r, g, b, a;
	SDL_GetColorKey(bmp->surface, &key);
	SDL_GetRGBA(key, bmp->surface->format, &r, &g, &b, &a);
	if (c->r == r && c->g == g && c->b == b)
		return true;
	else
		return false;
}

//helper functions for getpixel and putpixel
//http://sdl.beuc.net/sdl.wiki/Pixel_Access
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
	int bpp = surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

	switch (bpp) {
	case 1:
		return *p;
		break;

	case 2:
		return *(Uint16 *)p;
		break;

	case 3:
		if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
			return p[0] << 16 | p[1] << 8 | p[2];
		else
			return p[0] | p[1] << 8 | p[2] << 16;
		break;

	case 4:
		return *(Uint32 *)p;
		break;

	default:
		return 0;       /* shouldn't happen, but avoids warnings */
	}
}
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
	int bpp = surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to set */
	Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

	switch (bpp) {
	case 1:
		*p = pixel;
		break;

	case 2:
		*(Uint16 *)p = pixel;
		break;

	case 3:
		if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
			p[0] = (pixel >> 16) & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = pixel & 0xff;
		}
		else {
			p[0] = pixel & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = (pixel >> 16) & 0xff;
		}
		break;

	case 4:
		*(Uint32 *)p = pixel;
		break;
	}
}
//getpixel using surface (only bitmap)
void SDLX_GetPixel(SDLX_Bitmap* bmp, int x, int y, SDLX_Color* c)
{
	if (bmp == NULL)
		return;
	if (bmp->surface == NULL)
		return;
	Uint32 p = getpixel(bmp->surface, x, y);
	Uint8 r;
	Uint8 g;
	Uint8 b;
	Uint8 a;
	SDL_GetRGBA(p, bmp->surface->format, &r, &g, &b, &a);
	c->r = r;
	c->g = g;
	c->b = b;
	c->a = a;
}
//putpixel using surface (will work for a bitmap or screen (if bmp is NULL)
//if we are not using a buffer, then we have created a texture for bitmap to be used in rendering
//   in that case, after putpixel, we have to refresh the texture by calling SDLX_CreateBitmapTexture
void SDLX_PutPixel(SDLX_Bitmap* bmp, int x, int y, SDLX_Color* c)
{
	SDLX_PutPixel(bmp, x, y, c->r, c->g, c->b, c->a);
}
void SDLX_PutPixel(SDLX_Bitmap* bmp, int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
	if (bmp == NULL) //write to screen
	{
		if (buffer != NULL)
		{
			Uint32 p = SDL_MapRGBA(buffer->format, r, g, b, a);
			putpixel(buffer, x, y, p);
		}
		else
		{
			SDL_SetRenderDrawColor(renderer, r, g, b, a);
			SDL_RenderDrawPoint(renderer, x, y);
		}
		//now return
		return;
	}

	//if there is a bitmap
	if (bmp->surface == NULL)
		return;

	//if valid bitmap
	Uint32 p = SDL_MapRGBA(bmp->surface->format, r, g, b, a);
	putpixel(bmp->surface, x, y, p);

	if (bmp->texture != NULL)
		SDL_DestroyTexture(bmp->texture);
	bmp->texture = NULL; //after putpixel, texture is no longer valid
}

SDLX_Bitmap* SDLX_CloneBitmap(SDLX_Bitmap* src)
{
	SDLX_Bitmap* bmp = new SDLX_Bitmap();
	bmp->surface = NULL;
	bmp->texture = NULL;

	bmp->surface = SDL_CreateRGBSurface(0, src->width, src->height, src->surface->format->BitsPerPixel, 0,0,0, 255);
	if (bmp->surface == NULL)
	{
		SDLX_DestroyBitmap(bmp);
		return NULL;
	}

	SDL_SetSurfaceBlendMode(bmp->surface, SDL_BLENDMODE_BLEND);
	bmp->width = bmp->surface->w;
	bmp->height = bmp->surface->h;

	Uint32 key;
	SDL_GetColorKey(src->surface, &key);
	SDL_SetColorKey(src->surface, 0, 0); //temporarily disable color key to copy all values
	SDL_BlitSurface(src->surface, NULL, bmp->surface, NULL);
	SDL_SetColorKey(src->surface, SDL_TRUE, key); //restore key
	SDL_SetColorKey(bmp->surface, SDL_TRUE, key);

	if (!use_buffer)
	{
		SDLX_CreateBitmapTexture(bmp);
	}

	return bmp;
}


//events
//wait for key. saved in last_key
void SDLX_GetKey()
{
	SDLX_Event e;
	bool wait = true;
	while (wait)
	{
		if (SDLX_PollEvent(&e))
		{
			if (e.type == SDL_QUIT)
			{
				wait = false;
			}
			if (e.type == SDL_KEYDOWN)
			{
				last_code = e.keycode;
				wait = false;
			}
		}
	}
}