// **********************************************************************
//                            OpenAlchemist
//                        ---------------------
//
//  File        : CommonResources.cpp
//  Description : 
//  Author      : Guillaume Delhumeau <guillaume.delhumeau@gmail.com>
//  License     : GNU General Public License 2 or higher
//
// **********************************************************************

/************************************************************************/
/* Includes                                                             */
/************************************************************************/
#include <SDL.h>
#include <SDL_image.h>

#include "CommonResources.h"
#include "Preferences.h"
#include "misc.h"
#include "Piece.h"
#include "IniFile.h"
#include "memory.h"

/************************************************************************/
/* Constants                                                            */
/************************************************************************/
const int Piece::_score[12] = { 1, 3, 9, 30, 90, 300, 900, 3000, 9000, 30000, 90000, 300000 };

/************************************************************************/
/* Singleton instance                                                   */
/************************************************************************/
static CommonResources* p_instance = NULL;

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
CommonResources::CommonResources()
{
	skin_zip = NULL;
	p_current_player = NULL;
	player2_support = false;
	normal_color.r = 255;
	normal_color.g = 255;
	normal_color.b = 255;
	selected_color.r = 250;
	selected_color.g = 151;
	selected_color.b = 55;
	disabled_color.r = 115;
	disabled_color.g = 115;
	disabled_color.b = 115;
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
CommonResources::~CommonResources()
{
	unload_gfx();
	if (skin_zip)
		zip_close(skin_zip);
}

/************************************************************************/
/* Init                                                                 */
/************************************************************************/
void CommonResources::init(GameEngine* p_engine)
{
	this -> p_engine = p_engine;
	read_scores();
	player1.add_listener(&statistics);
}

/************************************************************************/
/* Load GFX                                                             */
/************************************************************************/
void CommonResources::load_gfx(SDL_Renderer *gc, std::string skin)
{
	if (this->skin != skin)
	{
		if (skin_zip)
			zip_close(skin_zip);
		skin_zip = zip_open(skin.c_str(), ZIP_RDONLY, NULL);
	}
	this -> skin = skin;

	unload_gfx();

	CL_ResourceManager gfx("general.xml");

	main_font = CL_Font_Sprite(gc, "font", &gfx);

	// Then, propreties
	if (gfx.resource_exists("pieces/width"))
	{
		pieces_width = CL_Integer_to_int("pieces/width", &gfx);
		pieces_height = CL_Integer_to_int("pieces/height", &gfx);
		pieces_preview_width = CL_Integer_to_int("pieces/preview/width", &gfx, pieces_width / 2);
		pieces_preview_height = CL_Integer_to_int("pieces/preview/height", &gfx, pieces_height / 2);
	}
	else
	{
		CL_ResourceManager gfx_pieces("pieces.xml");
		pieces_width = CL_Integer_to_int("pieces/width", &gfx_pieces);
		pieces_height = CL_Integer_to_int("pieces/height", &gfx_pieces);
		pieces_preview_width = CL_Integer_to_int("pieces/preview/width", &gfx_pieces, pieces_width / 2);
		pieces_preview_height = CL_Integer_to_int("pieces/preview/height", &gfx_pieces, pieces_height / 2);
	}

	player1.load_gfx(gc, "player1/");
	if (gfx.resource_exists("player2/game/top"))
	{
		player2_support = true;
	}
	front_layer.load_gfx(gc, skin);

	key.skins.set_active(gfx.get_boolean_resource("keys/skins", true));
}

SDL_Texture *CommonResources::load_texture(SDL_Renderer *gc, std::string filename, int &w, int &h)
{
	return load_texture(skin_zip, gc, filename, w, h);
}

SDL_Texture *CommonResources::load_texture(zip_t *zip_file, SDL_Renderer *gc, std::string filename, int &w, int &h)
{
	SDL_Surface *img = NULL;
	zip_stat_t file_stat;
	zip_stat(zip_file, filename.c_str(), ZIP_STAT_SIZE, &file_stat);
	std::vector<char> buffer(file_stat.size);
	zip_file_t *f = zip_fopen(zip_file, filename.c_str(), 0);
	int sz = zip_fread(f, buffer.data(), file_stat.size);
	SDL_RWops *rwopsmem = SDL_RWFromConstMem(buffer.data(), sz);
	img = IMG_Load_RW(rwopsmem, 1);
	if (img)
	{
		w = img->w;
		h = img->h;
		SDL_Texture *t = SDL_CreateTextureFromSurface(gc, img);
		SDL_FreeSurface(img);
		return t;
	}
	else
	{
		w = 0;
		h = 0;
		std::cout << filename << ": " << IMG_GetError() << std::endl;
		return NULL;
	}
}

void CommonResources::release_texture(SDL_Texture *p)
{
	SDL_DestroyTexture(p);
}

/************************************************************************/
/* Unload GFX                                                           */
/************************************************************************/
void CommonResources::unload_gfx()
{
	player1.unload_gfx();
	front_layer.unload_gfx();
}

/************************************************************************/
/* Read scores                                                          */
/************************************************************************/
void CommonResources::read_scores()
{
	std::string path = get_save_path();

	FILE *file = fopen((path+get_path_separator()+"highscores").c_str(), "rb");
	if (file)
	{
		char compatibility[5];
		size_t data_read = fread(&compatibility, sizeof(char), 5, file);
		if (4 == data_read)
		{
			uint32_t hs;
			memcpy(&hs, compatibility, 4);
			statistics.highscore = hs;
		}
		else if (5 == data_read)
		{
			fseek(file, 0, SEEK_SET);
			IniFile ini;
			ini.read(file);
			statistics.highscore = ini.get("Highscore", statistics.highscore);
			statistics.maxcombo = ini.get("Maxcombo", statistics.maxcombo);
			statistics.totalcombo = ini.get("Totalcombo", statistics.totalcombo);
			statistics.unlocked_pieces = ini.get("UnlockedPieces", statistics.unlocked_pieces);
		}
		else
			std::cout << "Can't read highscores file.\n";
		fclose(file);
	}
	else
		std::cout << "Can't read highscores file. Probably doesn't exist.\n";
}

/************************************************************************/
/* Save scores                                                          */
/************************************************************************/
void CommonResources::save_scores()
{
	std::string path = get_save_path();
	FILE *file = fopen((path+get_path_separator()+"highscores").c_str(), "wb");
	if (file)
	{
		IniFile ini;
		ini.clear();
		ini.add("Highscore", statistics.highscore);
		ini.add("Maxcombo", statistics.maxcombo);
		ini.add("Totalcombo", statistics.totalcombo);
		ini.add("UnlockedPieces", statistics.unlocked_pieces);

		ini.write(file, "Statistics");
		fclose(file);
	}
	else
		std::cout << "Can't write highscores file.\n";
}

/************************************************************************/
/* Get singleton instance                                               */
/************************************************************************/
CommonResources* common_resources_get_instance()
{ 
#ifdef DEBUG
	if(!p_instance)
	{
		std::cout << "Common Resources Init must be called" << std::endl;
	}
#endif
	return p_instance;
}

/************************************************************************/
/* Init common resources                                                */
/************************************************************************/
void common_resources_init()
{
	if(!p_instance)
		p_instance = my_new CommonResources();
}

/************************************************************************/
/* Term common resources                                                */
/************************************************************************/
void common_resources_term()
{
	if(p_instance)
	{
		my_delete(p_instance);
		p_instance = NULL;
	}
}
