/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

#ifdef HAVE_LIBPNG
#include <png.h>
#endif
#include "port.h"
#include "crosshairs.h"

static const char	*crosshairs[32] =
{
	"`              "  // Crosshair 0 (no image)
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               ",

	"`              "  // Crosshair 1 (the classic small dot)
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"       #.      "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               "
	"               ",

	"`              "  // Crosshair 2 (a standard cross)
	"               "
	"               "
	"               "
	"      .#.      "
	"      .#.      "
	"    ...#...    "
	"    #######    "
	"    ...#...    "
	"      .#.      "
	"      .#.      "
	"               "
	"               "
	"               "
	"               ",

	"`     .#.      "  // Crosshair 3 (a standard cross)
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	".......#......."
	"###############"
	".......#......."
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      ",

	"`              "  // Crosshair 4 (an X)
	"               "
	"               "
	"    .     .    "
	"   .#.   .#.   "
	"    .#. .#.    "
	"     .#.#.     "
	"      .#.      "
	"     .#.#.     "
	"    .#. .#.    "
	"   .#.   .#.   "
	"    .     .    "
	"               "
	"               "
	"               ",

	"`.           . "  // Crosshair 5 (an X)
	".#.         .#."
	" .#.       .#. "
	"  .#.     .#.  "
	"   .#.   .#.   "
	"    .#. .#.    "
	"     .#.#.     "
	"      .#.      "
	"     .#.#.     "
	"    .#. .#.    "
	"   .#.   .#.   "
	"  .#.     .#.  "
	" .#.       .#. "
	".#.         .#."
	" .           . ",

	"`              "  // Crosshair 6 (a combo)
	"               "
	"               "
	"               "
	"    #  .  #    "
	"     # . #     "
	"      #.#      "
	"    ...#...    "
	"      #.#      "
	"     # . #     "
	"    #  .  #    "
	"               "
	"               "
	"               "
	"               ",

	"`      .       "  // Crosshair 7 (a combo)
	" #     .     # "
	"  #    .    #  "
	"   #   .   #   "
	"    #  .  #    "
	"     # . #     "
	"      #.#      "
	".......#......."
	"      #.#      "
	"     # . #     "
	"    #  .  #    "
	"   #   .   #   "
	"  #    .    #  "
	" #     .     # "
	"       .       ",

	"`      #       "  // Crosshair 8 (a diamond cross)
	"      #.#      "
	"     # . #     "
	"    #  .  #    "
	"   #   .   #   "
	"  #    .    #  "
	" #     .     # "
	"#......#......#"
	" #     .     # "
	"  #    .    #  "
	"   #   .   #   "
	"    #  .  #    "
	"     # . #     "
	"      #.#      "
	"       #       ",

	"`     ###      "  // Crosshair 9 (a circle cross)
	"    ## . ##    "
	"   #   .   #   "
	"  #    .    #  "
	" #     .     # "
	" #     .     # "
	"#      .      #"
	"#......#......#"
	"#      .      #"
	" #     .     # "
	" #     .     # "
	"  #    .    #  "
	"   #   .   #   "
	"    ## . ##    "
	"      ###      ",

	"`     .#.      "  // Crosshair 10 (a square cross)
	"      .#.      "
	"      .#.      "
	"   ....#....   "
	"   .#######.   "
	"   .#     #.   "
	"....#     #...."
	"#####     #####"
	"....#     #...."
	"   .#     #.   "
	"   .#######.   "
	"   ....#....   "
	"      .#.      "
	"      .#.      "
	"      .#.      ",

	"`     .#.      "  // Crosshair 11 (an interrupted cross)
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"               "
	".....     ....."
	"#####     #####"
	".....     ....."
	"               "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      "
	"      .#.      ",

	"`.           . "  // Crosshair 12 (an interrupted X)
	".#.         .#."
	" .#.       .#. "
	"  .#.     .#.  "
	"   .#.   .#.   "
	"               "
	"               "
	"               "
	"               "
	"               "
	"   .#.   .#.   "
	"  .#.     .#.  "
	" .#.       .#. "
	".#.         .#."
	" .           . ",

	"`      .       "  // Crosshair 13 (an interrupted combo)
	" #     .     # "
	"  #    .    #  "
	"   #   .   #   "
	"    #  .  #    "
	"               "
	"               "
	".....     ....."
	"               "
	"               "
	"    #  .  #    "
	"   #   .   #   "
	"  #    .    #  "
	" #     .     # "
	"       .       ",

	"`####     #### "  // Crosshair 14
	"#....     ....#"
	"#.           .#"
	"#.           .#"
	"#.           .#"
	"       #       "
	"       #       "
	"     #####     "
	"       #       "
	"       #       "
	"#.           .#"
	"#.           .#"
	"#.           .#"
	"#....     ....#"
	" ####     #### ",

	"`  .#     #.   "  // Crosshair 15
	"   .#     #.   "
	"   .#     #.   "
	"....#     #...."
	"#####     #####"
	"               "
	"               "
	"               "
	"               "
	"               "
	"#####     #####"
	"....#     #...."
	"   .#     #.   "
	"   .#     #.   "
	"   .#     #.   ",

	"`      #       "  // Crosshair 16
	"       #       "
	"       #       "
	"   ....#....   "
	"   .   #   .   "
	"   .   #   .   "
	"   .   #   .   "
	"###############"
	"   .   #   .   "
	"   .   #   .   "
	"   .   #   .   "
	"   ....#....   "
	"       #       "
	"       #       "
	"       #       ",

	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};


bool S9xLoadCrosshairFile (int idx, const char *filename)
{
	if (idx < 1 || idx > 31)
		return (false);

	char	*s = (char *) calloc(15 * 15 + 1, sizeof(char));
	if (s == NULL)
	{
		fprintf(stderr, "S9xLoadCrosshairFile: malloc error while reading ");
		perror(filename);
		return (false);
	}

	FILE	*fp = fopen(filename, "rb");
	if (fp == NULL)
	{
		fprintf(stderr, "S9xLoadCrosshairFile: Couldn't open ");
		perror(filename);
		free(s);
		return (false);
	}

	size_t	l = fread(s, 1, 8, fp);
	if (l != 8)
	{
		fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
		free(s);
		fclose(fp);
		return (false);
	}

#ifdef HAVE_LIBPNG
	png_structp	png_ptr;
	png_infop	info_ptr;

	if (!png_sig_cmp((png_byte *) s, 0, 8))
	{
		png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
		if (!png_ptr)
		{
			free(s);
			fclose(fp);
			return (false);
		}

		info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr)
		{
			png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
			free(s);
			fclose(fp);
			return (false);
		}

		png_init_io(png_ptr, fp);
		png_set_sig_bytes(png_ptr, 8);
		png_read_info(png_ptr, info_ptr);

		png_uint_32	width, height;
		int			bit_depth, color_type;

		png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
		if (color_type != PNG_COLOR_TYPE_PALETTE)
		{
			fprintf(stderr, "S9xLoadCrosshairFile: Input PNG is not a palettized image!\n");
			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
			free(s);
			fclose(fp);
			return (false);
		}

		if (bit_depth == 16)
			png_set_strip_16(png_ptr);

		if (width != 15 || height != 15)
		{
			fprintf(stderr, "S9xLoadCrosshairFile: Expecting a 15x15 PNG\n");
			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
			free(s);
			fclose(fp);
			return (false);
		}

		png_color	*pngpal;
		png_byte	*trans;
		int			num_palette = 0, num_trans = 0;
		int			transcol = -1, fgcol = -1, bgcol = -1;

		png_get_PLTE(png_ptr, info_ptr, &pngpal, &num_palette);
		png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);

		if (num_palette != 3 || num_trans != 1)
		{
			fprintf(stderr, "S9xLoadCrosshairFile: Expecting a 3-color PNG with 1 trasnparent color\n");
			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
			free(s);
			fclose(fp);
			return (false);
		}

		for (int i = 0; i < 3; i++)
		{
			if (trans[0] == i)
				transcol = i;
			else
			if (pngpal[i].red ==   0 && pngpal[i].green ==   0 && pngpal[i].blue ==   0)
				bgcol = i;
			else
			if (pngpal[i].red == 255 && pngpal[i].green == 255 && pngpal[i].blue == 255)
				fgcol = i;
		}

		if (transcol < 0 || fgcol < 0 || bgcol < 0)
		{
			fprintf(stderr, "S9xLoadCrosshairFile: PNG must have 3 colors: white (fg), black (bg), and transparent.\n");
			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
			free(s);
			fclose(fp);
			return (false);
		}

		png_set_packing(png_ptr);
		png_read_update_info(png_ptr, info_ptr);
		png_byte	*row_pointer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];

		for (int r = 0; r < 15 * 15; r += 15)
		{
			png_read_row(png_ptr, row_pointer, NULL);

			for (int i = 0; i < 15; i++)
			{
				if (row_pointer[i] == transcol)
					s[r + i] = ' ';
				else
				if (row_pointer[i] == fgcol)
					s[r + i] = '#';
				else
				if (row_pointer[i] == bgcol)
					s[r + i] = '.';
				else
				{
					fprintf(stderr, "S9xLoadCrosshairFile: WTF? This was supposed to be a 3-color PNG!\n");
					png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
					free(s);
					fclose(fp);
					return (false);
				}
			}
		}

		s[15 * 15] = 0;
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
	}
	else
#endif
	{
		l = fread(s + 8, 1, 15 - 8, fp);
		if (l != 15 - 8)
		{
			fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
			free(s);
			fclose(fp);
			return (false);
		}

		if (getc(fp) != '\n')
		{
			fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
			free(s);
			fclose(fp);
			return (false);
		}

		for (int r = 1; r < 15; r++)
		{
			l = fread(s + r * 15, 1, 15, fp);
			if (l != 15)
			{
				fprintf(stderr, "S9xLoadCrosshairFile: File is too short! (note: PNG support is not available)\n");
				free(s);
				fclose(fp);
				return (false);
			}

			if (getc(fp) != '\n')
			{
				fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
				free(s);
				fclose(fp);
				return (false);
			}
		}

		for (int i = 0; i < 15 * 15; i++)
		{
			if (s[i] != ' ' && s[i] != '#' && s[i] != '.')
			{
				fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
				free(s);
				fclose(fp);
				return (false);
			}
		}
	}

	fclose(fp);

	if (crosshairs[idx] != NULL && crosshairs[idx][0] != '`')
		free((void *) crosshairs[idx]);
	crosshairs[idx] = s;

	return (true);
}

const char * S9xGetCrosshair (int idx)
{
	if (idx < 0 || idx > 31)
		return (NULL);

	return (crosshairs[idx]);
}