/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2009,2010 Free Software Foundation, Inc.
 *
 *  GRUB 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 Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TAGS_H
#include FT_TRUETYPE_TABLES_H
#include <freetype/ftsynth.h>

#undef __FTERRORS_H__
#define FT_ERROR_START_LIST   const char *ft_errmsgs[] = { 
#define FT_ERRORDEF(e, v, s)  [e] = s,
#define FT_ERROR_END_LIST     };
#include FT_ERRORS_H   

#define GRUB_FONT_DEFAULT_SIZE		16

#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))

struct grub_glyph_info
{
  int width;
  int height;
  int x_ofs;
  int y_ofs;
  int device_width;
  int bitmap_size;
  unsigned char *bitmap;
};

static void
add_pixel (unsigned char **data, int *mask, int not_blank)
{
  if (*mask == 0)
    {
      (*data)++;
      **data = 0;
      *mask = 128;
    }

  if (not_blank)
    **data |= *mask;

  *mask >>= 1;
}

static void
add_glyph (FT_UInt glyph_idx, FT_Face face,
	   unsigned int char_code,
	   struct grub_glyph_info *glyph_info)
{
  int width, height;
  int cuttop, cutbottom, cutleft, cutright;
  unsigned char *data;
  int mask, i, j, bitmap_size;
  FT_GlyphSlot glyph;
  int flag = FT_LOAD_RENDER | FT_LOAD_MONOCHROME;
  FT_Error err;

  err = FT_Load_Glyph (face, glyph_idx, flag);
  if (err)
    {
      printf ("Freetype Error %d loading glyph 0x%x for U+0x%x",
	      err, glyph_idx, char_code);

      if (err > 0 && err < (signed) ARRAY_SIZE (ft_errmsgs))
	printf (": %s\n", ft_errmsgs[err]);
      else
	printf ("\n");
      return;
    }

  glyph = face->glyph;

  if (glyph->next)
    printf ("%x\n", char_code);

  cuttop = cutbottom = cutleft = cutright = 0;

  width = glyph->bitmap.width;
  height = glyph->bitmap.rows;

  bitmap_size = ((width * height + 7) / 8);
  glyph_info->bitmap = malloc (bitmap_size);
  if (!glyph_info->bitmap)
    {
      fprintf (stderr, "grub-gen-asciih: error: out of memory");
      exit (1);
    }
  glyph_info->bitmap_size = bitmap_size;

  glyph_info->width = width;
  glyph_info->height = height;
  glyph_info->x_ofs = glyph->bitmap_left;
  glyph_info->y_ofs = glyph->bitmap_top - height;
  glyph_info->device_width = glyph->metrics.horiAdvance / 64;

  mask = 0;
  data = &glyph_info->bitmap[0] - 1;
  for (j = cuttop; j < height + cuttop; j++)
    for (i = cutleft; i < width + cutleft; i++)
      add_pixel (&data, &mask,
		 glyph->bitmap.buffer[i / 8 + j * glyph->bitmap.pitch] &
		 (1 << (7 - (i & 7))));
}

static void
write_font_ascii_bitmap (FILE *file, FT_Face face)
{
  struct grub_glyph_info glyph;
  int char_code;

  fprintf (file, "/* THIS CHUNK OF BYTES IS AUTOMATICALLY GENERATED */\n");
  fprintf (file, "unsigned char ascii_bitmaps[] =\n");
  fprintf (file, "{\n");

  for (char_code = 0; char_code <= 0x7f; char_code++)
    {
      FT_UInt glyph_idx;
      
      glyph_idx = FT_Get_Char_Index (face, char_code);
      if (!glyph_idx)
	return;
      add_glyph (glyph_idx, face, char_code, &glyph);

      if (glyph.width == 8 && glyph.height == 16
	  && glyph.x_ofs == 0 && glyph.y_ofs == 0)
	{
	  int row;
	  for (row = 0; row < 16; row++)
	    fprintf (file, "0x%02x, ", glyph.bitmap[row]);
	}
      else
	{
	  unsigned char glph[16];
	  int p = 0, mask = 0x80;
	  int row, col;
	  int dy = 12 - glyph.height - glyph.y_ofs;
	  for (row = 0; row < 16; row++)
	    glph[row] = 0;
	  for (row = 0; row < glyph.height; row++)
	    for (col = 0; col < glyph.width; col++)
	      {
		int val = glyph.bitmap[p] & mask;
		mask >>= 1;
		if (mask == 0)
		  {
		    mask = 0x80;
		    p++;
		  }
		if (val && dy + row >= 0
		    && dy + row < 16
		    && glyph.x_ofs + col >= 0
		    && glyph.x_ofs + col < 8)
		  glph[dy + row] |= 1 << (7 - (glyph.x_ofs + col));
	      }
	  for (row = 0; row < 16; row++)
	    fprintf (file, "0x%02x, ", glph[row]);
	}
      fprintf (file, "\n");
      free (glyph.bitmap);
    }
  fprintf (file, "};\n");
}

int
main (int argc, char *argv[])
{
  FT_Library ft_lib;
  FT_Face ft_face;
  FILE *file;

  if (argc != 3)
    {
      fprintf (stderr, "grub-gen-asciih: usage: INPUT OUTPUT");
      return 1;
    }

  if (FT_Init_FreeType (&ft_lib))
    {
      fprintf (stderr, "grub-gen-asciih: error: FT_Init_FreeType fails");
      return 1;
    }

  {
    int size;
    FT_Error err;

    err = FT_New_Face (ft_lib, argv[1], 0, &ft_face);
    if (err)
      {
	fprintf (stderr, "can't open file %s, index %d: error %d",
		 argv[1], 0, err);
	if (err > 0 && err < (signed) ARRAY_SIZE (ft_errmsgs))
	  fprintf (stderr, ": %s\n", ft_errmsgs[err]);
	else
	  fprintf (stderr, "\n");

	return 1;
      }

    if ((ft_face->face_flags & FT_FACE_FLAG_SCALABLE) ||
	(! ft_face->num_fixed_sizes))
      size = GRUB_FONT_DEFAULT_SIZE;
    else
      size = ft_face->available_sizes[0].height;

    if (FT_Set_Pixel_Sizes (ft_face, size, size))
      {
	fprintf (stderr, "grub-gen-asciih: error: can't set %dx%d font size", size, size);
	return 1;
      }
  }

  file = fopen (argv[2], "w");
  if (! file)
    {
      fprintf (stderr, "grub-gen-asciih: error: cannot write to `%s': %s", argv[2],
	       strerror (errno));
      return 1;
    }

  write_font_ascii_bitmap (file, ft_face);

  fclose (file);

  FT_Done_Face (ft_face);

  FT_Done_FreeType (ft_lib);

  return 0;
}