/*  RetroArch - A frontend for libretro.
 *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
 *  Copyright (C) 2011-2020 - Daniel De Matteis
 *  Copyright (C) 2019-2020 - James Leaver
 *  Copyright (C) 2020-2022 - trngaje
 *  Copyright (C)      2022 - Michael Burgardt
 *
 *  RetroArch is free software: you can redistribute it and/or modify it under the terms
 *  of the GNU General Public License as published by the Free Software Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE.  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with RetroArch.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <boolean.h>

#include <string/stdstring.h>
#include <file/file_path.h>
#include <streams/rzip_stream.h>
#include <retro_miscellaneous.h>

#include "../../file_path_special.h"

#include "bitmapfont_6x10.h"

/* MACROS */
/* extended ASCII: Basic Latin + Latin-1 Supplement */
#define FONT_6X10_FILE_ENG      "bitmap6x10_eng.bin"
#define FONT_6X10_SIZE_ENG      2048
#define FONT_6X10_GLYPH_MIN_ENG 0x0
#define FONT_6X10_GLYPH_MAX_ENG 0xFF

/* Latin Supplement Extended: Latin Extended A + B */
#define FONT_6X10_FILE_LSE      "bitmap6x10_lse.bin"
#define FONT_6X10_SIZE_LSE      2688
#define FONT_6X10_GLYPH_MIN_LSE 0x100
#define FONT_6X10_GLYPH_MAX_LSE 0x24F

/* Loads a font of the specified language
 * Returns NULL if language is invalid or
 * font file is missing */
bitmapfont_lut_t *bitmapfont_6x10_load(unsigned language)
{
   char font_dir[DIR_MAX_LENGTH];
   char font_path[PATH_MAX_LENGTH];
   const char *font_file      = NULL;
   void *bitmap_raw           = NULL;
   unsigned char *bitmap_char = NULL;
   bitmapfont_lut_t *font     = NULL;
   int64_t font_size          = 0;
   int64_t len                = 0;
   size_t glyph_min           = 0;
   size_t glyph_max           = 0;
   size_t num_glyphs          = 0;
   size_t symbol_index;
   size_t i, j;

   /* Get font file associated with
    * specified language */
   switch (language)
   {
	   /* Needed individually for any non-Latin languages */
	   case RETRO_LANGUAGE_ENGLISH:
	      font_file = FONT_6X10_FILE_ENG;
	      font_size = FONT_6X10_SIZE_ENG;
	      glyph_min = FONT_6X10_GLYPH_MIN_ENG;
	      glyph_max = FONT_6X10_GLYPH_MAX_ENG;
	      break;
	      /* All Latin alphabet languages go here */
	   case RETRO_LANGUAGE_FRENCH:
	   case RETRO_LANGUAGE_SPANISH:
	   case RETRO_LANGUAGE_GERMAN:
	   case RETRO_LANGUAGE_ITALIAN:
	   case RETRO_LANGUAGE_DUTCH:
	   case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
	   case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
	   case RETRO_LANGUAGE_ESPERANTO:
	   case RETRO_LANGUAGE_POLISH:
	   case RETRO_LANGUAGE_VIETNAMESE:
	   case RETRO_LANGUAGE_TURKISH:
	   case RETRO_LANGUAGE_SLOVAK:
	   case RETRO_LANGUAGE_ASTURIAN:
	   case RETRO_LANGUAGE_FINNISH:
	   case RETRO_LANGUAGE_INDONESIAN:
	   case RETRO_LANGUAGE_SWEDISH:
	   case RETRO_LANGUAGE_CZECH:
	   case RETRO_LANGUAGE_HUNGARIAN:
#if 0
	   /* These languages are not yet added */
	   case RETRO_LANGUAGE_ROMANIAN:
	   case RETRO_LANGUAGE_CROATIAN:
	   case RETRO_LANGUAGE_SERBIAN:
	   case RETRO_LANGUAGE_WELSH:
#endif
	      font_file = FONT_6X10_FILE_LSE;
	      font_size = FONT_6X10_SIZE_LSE;
	      glyph_min = FONT_6X10_GLYPH_MIN_LSE;
	      glyph_max = FONT_6X10_GLYPH_MAX_LSE;
	      break;
	   default:
	      break;
   }

   /* Sanity check: should only trigger on bug */
   if (string_is_empty(font_file))
      goto error;

   /* Get font path */
   fill_pathname_application_special(font_dir, sizeof(font_dir),
         APPLICATION_SPECIAL_DIRECTORY_ASSETS_RGUI_FONT);
   fill_pathname_join_special(font_path, font_dir, font_file,
         sizeof(font_path));

   /* Attempt to read bitmap file */
   if (!rzipstream_read_file(font_path, &bitmap_raw, &len))
      goto error;

   /* Ensure that we have the correct number
    * of bytes */
   if (len != font_size)
      goto error;

   bitmap_char = (unsigned char *)bitmap_raw;
   num_glyphs  = (glyph_max - glyph_min) + 1;

   /* Initialise font struct */
   if (!(font = (bitmapfont_lut_t*)calloc(1, sizeof(bitmapfont_lut_t))))
      goto error;

   font->glyph_min = glyph_min;
   font->glyph_max = glyph_max;

   /* Note: Need to use a calloc() here, otherwise
    * we'll get undefined behaviour when calling
    * bitmapfont_free_lut() if the following loop fails */
   if (!(font->lut = (bool**)calloc(1, num_glyphs * sizeof(bool*))))
      goto error;

   /* Loop over all possible characters */
   for (symbol_index = 0; symbol_index < num_glyphs; symbol_index++)
   {
      /* Allocate memory for current symbol */
      font->lut[symbol_index] = (bool*)malloc(FONT_6X10_WIDTH *
            FONT_6X10_HEIGHT * sizeof(bool));
      if (!font->lut[symbol_index])
         goto error;

      for (j = 0; j < FONT_6X10_HEIGHT; j++)
      {
         for (i = 0; i < FONT_6X10_WIDTH; i++)
         {
            uint8_t rem     = 1 << ((i + j * FONT_6X10_WIDTH) & 7);
            size_t offset   = (i + j * FONT_6X10_WIDTH) >> 3;

            /* LUT value is 'true' if specified glyph
             * position contains a pixel */
            font->lut[symbol_index][i + (j * FONT_6X10_WIDTH)] =
                  (bitmap_char[FONT_6X10_OFFSET(symbol_index) + offset] & rem) > 0;
         }
      }
   }

   /* Clean up */
   free(bitmap_raw);

   return font;

error:
   if (bitmap_raw)
      free(bitmap_raw);

   if (font)
      bitmapfont_free_lut(font);

   return NULL;
}
