// **********************************************************************
//                            OpenAlchemist
//                        ---------------------
//
//  File        : SDL_CL_Sprite.cpp
//  Description : 
//  Author      : Dennis Payne <dulsi@identicalsoftware.com>
//  License     : GNU General Public License 2 or higher
//
// **********************************************************************

#include "SDL_CL_Sprite.h"
#include <SDL_image.h>
#include "CommonResources.h"

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
SDL_CL_Image_Impl::SDL_CL_Image_Impl()
	: _t(NULL), _x(-1), _y(-1), _w(-1), _h(-1), _release(false)
{
}

SDL_CL_Image_Impl::SDL_CL_Image_Impl(const std::string &filename, int x, int y, int w, int h)
	: _filename(filename), _t(NULL), _x(x), _y(y), _w(w), _h(h), _release(false)
{
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
SDL_CL_Image_Impl::~SDL_CL_Image_Impl()
{
	if (_release)
		common_resources_get_instance()->release_texture(_t);
	else
		SDL_DestroyTexture(_t);
}

void SDL_CL_Image_Impl::load(SDL_Renderer *r, zip_t *zip_file)
{
	CommonResources *cr = common_resources_get_instance();
	if (_x == -1)
		_x = 0;
	if (_y == -1)
		_y = 0;
	int w, h;
	_t = cr->load_texture(zip_file, r, _filename, w, h);
	if (_t)
	{
		_release = true;
	}
	if (_w == -1)
		_w = w;
	if (_h == -1)
		_h = h;
}

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
SDL_CL_Image::SDL_CL_Image()
{
}

SDL_CL_Image::SDL_CL_Image(SDL_Renderer *r, const std::string &name, SDL_CL_ResourceManager *rm)
{
	_impl = rm->_image_map[rm->translate_name(name)];
	_impl->load(r, rm->_zip_file);
}

SDL_CL_Image::SDL_CL_Image(SDL_Renderer *r, const std::string &filename)
{
	_impl = std::shared_ptr<SDL_CL_Image_Impl>(new SDL_CL_Image_Impl);
	_impl->_filename = filename;
    SDL_Surface *img = IMG_Load(filename.c_str());
    _impl->_x = 0;
    _impl->_y = 0;
    _impl->_w = img->w;
    _impl->_h = img->h;
    _impl->_t = SDL_CreateTextureFromSurface(r, img);
    SDL_FreeSurface(img);
}

/************************************************************************/
/* Draw                                                                 */
/************************************************************************/
void SDL_CL_Image::draw(SDL_Renderer *gc, int x, int y)
{
	SDL_Rect destrect;
	destrect.x = x;
	destrect.y = y;
	destrect.w = _impl->_w;
	destrect.h = _impl->_h;
	SDL_Rect srcrect;
	srcrect.x = _impl->_x;
	srcrect.y = _impl->_y;
	srcrect.w = _impl->_w;
	srcrect.h = _impl->_h;
	SDL_RenderCopy(gc, _impl->_t, &srcrect, &destrect);
}

int SDL_CL_Image::get_height()
{
	if (_impl.get())
	{
		return _impl->_h;
	}
	return 0;
}

int SDL_CL_Image::get_width()
{
	if (_impl.get())
	{
		return _impl->_w;
	}
	return 0;
}

void SDL_CL_Image::set_alpha(float a)
{
}

void SDL_CL_Sprite_Impl::add_frame(const std::string &filename, int x, int y, int w, int h)
{
	frame.push_back(std::shared_ptr<SDL_CL_Image_Impl>(new SDL_CL_Image_Impl(filename, x, y, w, h)));
}

void SDL_CL_Sprite_Impl::load_frames(SDL_Renderer *r, zip_t *zip_file)
{
	for (auto itr = frame.begin(); itr != frame.end(); itr++)
	{
		(*itr)->load(r, zip_file);
	}
}

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
SDL_CL_Sprite::SDL_CL_Sprite()
{
}

SDL_CL_Sprite::SDL_CL_Sprite(SDL_Renderer *r, const std::string &name, SDL_CL_ResourceManager *rm)
{
	_impl = rm->_sprite_map[rm->translate_name(name)];
	_impl->load_frames(r, rm->_zip_file);
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
SDL_CL_Sprite::~SDL_CL_Sprite()
{
}

/************************************************************************/
/* Draw                                                                 */
/************************************************************************/
void SDL_CL_Sprite::draw(SDL_Renderer *gc, int x, int y)
{
	std::shared_ptr<SDL_CL_Image_Impl> f = _impl->frame[_impl->_cur_frame];
	SDL_Rect destrect;
	destrect.x = x;
	destrect.y = y;
	destrect.w = f->_w;
	destrect.h = f->_h;
	SDL_Rect srcrect;
	srcrect.x = f->_x;
	srcrect.y = f->_y;
	srcrect.w = f->_w;
	srcrect.h = f->_h;
	SDL_RenderCopy(gc, f->_t, &srcrect, &destrect);
}

int SDL_CL_Sprite::get_height()
{
	if (_impl.get())
	{
		if (!_impl->frame.empty())
		{
			return _impl->frame[_impl->_cur_frame]->_h;
		}
	}
	return 0;
}

int SDL_CL_Sprite::get_width()
{
	if (_impl.get())
	{
		if (!_impl->frame.empty())
		{
			return _impl->frame[_impl->_cur_frame]->_w;
		}
	}
	return 0;
}

bool SDL_CL_Sprite::is_finished()
{
	if (_impl.get() && _impl->_animation_speed > 0)
	{
		if (_impl->_loop == true)
			return false;
		if (_impl->_cur_frame + 1 < _impl->frame.size())
			return false;
		if (_impl->_start_time != -1)
		{
			int cur_time = SDL_GetTicks();
			if (cur_time - _impl->_start_time > _impl->_animation_speed)
				return true;
		}
		return false;
	}
	return true;
}

void SDL_CL_Sprite::restart()
{
	if (_impl.get())
	{
		_impl->_start_time = -1;
		_impl->_cur_frame = 0;
	}
}

void SDL_CL_Sprite::set_alpha(float a)
{
	SDL_SetTextureAlphaMod(_impl->frame[_impl->_cur_frame]->_t, 255 * a);
}

void SDL_CL_Sprite::set_play_loop(bool loop)
{
	_impl->_loop = loop;
}

/************************************************************************/
/* Update                                                               */
/************************************************************************/
void SDL_CL_Sprite::update()
{
	if (_impl.get() && _impl->_animation_speed > 0)
	{
		if (_impl->_start_time == -1)
			_impl->_start_time = SDL_GetTicks();
		else if ((_impl->_cur_frame + 1 < _impl->frame.size()) || (_impl->_loop))
		{
			int cur_time = SDL_GetTicks();
			if (cur_time - _impl->_start_time > _impl->_animation_speed)
			{
				_impl->_start_time = cur_time;
				_impl->_cur_frame = (_impl->_cur_frame + 1) % _impl->frame.size();
			}
		}
	}
}

SDL_Texture *SDL_CL_Font_Impl::get_colored_font(SDL_Renderer *gc, SDL_Color c)
{
	for (auto itr = _color_texture.begin(); itr != _color_texture.end(); itr++)
	{
		if ((c.r == (*itr)->_c.r) && (c.g == (*itr)->_c.g) && (c.b == (*itr)->_c.b))
			return (*itr)->_t;
	}
	zip_t *skin_zip = common_resources_get_instance()->skin_zip;
	zip_stat_t file_stat;
	zip_stat(skin_zip, _sprite->frame.front()->_filename.c_str(), ZIP_STAT_SIZE, &file_stat);
	std::vector<char> buffer(file_stat.size);
	zip_file_t *f = zip_fopen(skin_zip, _sprite->frame.front()->_filename.c_str(), 0);
	int sz = zip_fread(f, buffer.data(), file_stat.size);
	SDL_RWops *rwopsmem = SDL_RWFromMem(buffer.data(), sz);
	SDL_Surface *img = IMG_Load_RW(rwopsmem, 1);
	if ((img->format->BytesPerPixel == 1) && (img->format->Rmask == 0) && (img->format->Gmask == 0) && (img->format->Bmask == 0) && (img->format->Amask == 0))
	{
		SDL_Surface *img_convert = SDL_ConvertSurfaceFormat(img, SDL_PIXELFORMAT_RGBA8888, 0);
		SDL_FreeSurface(img);
		img = img_convert;
	}
	{
		SDL_PixelFormat *fmt = img->format;
		Uint32 pixel = SDL_MapRGB(img->format, c.r, c.g, c.b);
		Uint32 alpha_pixel = SDL_MapRGBA(img->format, 0, 0, 0, 0);
		for (int y = 0; y < img->h; y++)
		{
			for (int x = 0; x < img->w; x++)
			{
				if (*((Uint32*)(img->pixels + (y * img->pitch) + x * fmt->BytesPerPixel)) != alpha_pixel)
					*((Uint32*)(img->pixels + (y * img->pitch) + x * fmt->BytesPerPixel)) = pixel;
			}
		}
	}
	SDL_Texture *t = SDL_CreateTextureFromSurface(gc, img);
	SDL_FreeSurface(img);
	_color_texture.push_back(std::unique_ptr<SDL_CL_Font_Impl::SDL_ColoredFont>(new SDL_CL_Font_Impl::SDL_ColoredFont(c, t)));
	return t;
}

SDL_CL_Font::SDL_CL_Font()
{
}

SDL_CL_Font::SDL_CL_Font(SDL_Renderer *r, const std::string &name, SDL_CL_ResourceManager *rm)
{
	_impl = rm->_font_map[rm->translate_name(name)];
	_impl->_sprite = rm->_sprite_map[_impl->_glyphs];
	_impl->_sprite->load_frames(r, rm->_zip_file);
}

void SDL_CL_Font::draw_text(SDL_Renderer *r, int x, int y, const std::string &text)
{
	y -= _impl->_sprite->frame[0]->_h;
	for (int i = 0; text[i] != 0; i++)
	{
		if (text[i] == ' ')
			x += _impl->_spacelen;
		else
		{
			for (int indx = 0; _impl->_letters[indx] != 0; indx++)
			{
				if (_impl->_letters[indx] == text[i])
				{
					std::shared_ptr<SDL_CL_Image_Impl> char_img = _impl->_sprite->frame[indx];
					SDL_Rect destrect;
					destrect.x = x;
					destrect.y = y;
					destrect.w = char_img->_w;
					destrect.h = char_img->_h;
					SDL_Rect srcrect;
					srcrect.x = char_img->_x;
					srcrect.y = char_img->_y;
					srcrect.w = char_img->_w;
					srcrect.h = char_img->_h;
					SDL_RenderCopy(r, char_img->_t, &srcrect, &destrect);
					x += char_img->_w;
					break;
				}
			}
		}
	}
}

void SDL_CL_Font::draw_text(SDL_Renderer *r, int x, int y, const std::string &text, SDL_Color c)
{
	SDL_Texture *colored_tex = _impl->get_colored_font(r, c);
	y -= _impl->_sprite->frame[0]->_h;
	for (int i = 0; text[i] != 0; i++)
	{
		if (text[i] == ' ')
			x += _impl->_spacelen;
		else
		{
			for (int indx = 0; _impl->_letters[indx] != 0; indx++)
			{
				if (_impl->_letters[indx] == text[i])
				{
					std::shared_ptr<SDL_CL_Image_Impl> char_img = _impl->_sprite->frame[indx];
					SDL_Rect destrect;
					destrect.x = x;
					destrect.y = y;
					destrect.w = char_img->_w;
					destrect.h = char_img->_h;
					SDL_Rect srcrect;
					srcrect.x = char_img->_x;
					srcrect.y = char_img->_y;
					srcrect.w = char_img->_w;
					srcrect.h = char_img->_h;
					SDL_RenderCopy(r, colored_tex, &srcrect, &destrect);
					x += char_img->_w;
					break;
				}
			}
		}
	}
}

int SDL_CL_Font::get_text_width(SDL_Renderer *r, const std::string &text)
{
	int width = 0;
	for (int i = 0; text[i] != 0; i++)
	{
		if (text[i] == ' ')
			width += _impl->_spacelen;
		else
		{
			for (int indx = 0; _impl->_letters[indx] != 0; indx++)
			{
				if (_impl->_letters[indx] == text[i])
				{
					std::shared_ptr<SDL_CL_Image_Impl> char_img = _impl->_sprite->frame[indx];
					width += char_img->_w;
					break;
				}
			}
		}
	}
	return width;
}

int SDL_CL_Font::get_text_height(SDL_Renderer *r, const std::string &text)
{
	int height = _impl->_sprite->frame[0]->_h;
	return height;
}
