/* BOGL - Ben's Own Graphics Library.
   Written by Ben Pfaff <pfaffben@debian.org>.

   This program 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 2 of the
   License, or (at your option) any later version.
   
   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA. */

#define _GNU_SOURCE 1
#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wctype.h>
#include "bogl.h"
#include "bogl-font.h"

static void print_glyph (u_int32_t *content, int height, int w);
static int bogl_write_font(int fd, struct bogl_font *font);

int
main (int argc, char *argv[])
{
  struct bogl_font *font;
  int index_size = 0;
  int content_size = 0;
  int i, j, k, cp, n;
  char buf[MB_LEN_MAX+1];

  setlocale (LC_ALL, "");

  /* Check for proper usage. */
  if (!((argc == 2) || (argc == 3 && !strcmp (argv[1], "-b"))))
    {
      fprintf (stderr, "Usage:\n%s font.bdf > font.c\n"
	       "%s -b font.bdf > font.bgf\n", argv[0], argv[0]);
      return EXIT_FAILURE;
    }

  /* Read font file. */
  font = bogl_read_bdf (argc == 2 ? argv[1] : argv[2]);
  if (!font)
    {
      fputs (bogl_error (), stderr);
      return EXIT_FAILURE;
    }

  /* Write BGF font if -b. */
  if (argc == 3)
    {
      if (bogl_write_font (1, font))
	{
	  fputs ("bogl_write_font failed\n", stderr);
	  return EXIT_FAILURE;
	}
      return EXIT_SUCCESS;
    }

  /* Output header. */
  printf ("/* Generated by bdftobogl.  Do not modify! */\n");
  printf ("#include \"bogl.h\"\n");

  /* Output offsets, and get index_size and content_size. */
  printf ("\n/* Offsets into index. */\n");
  printf ("static int _%s_offset[%d] = {\n", font->name, font->index_mask+1);
  for (i = 0; i <= font->index_mask; i++)
    {
      printf ("  %d, /* (0x%x) */\n", font->offset[i], i);
      for (j = font->offset[i]; font->index[j] != 0; j += 2)
	{
	  k = font->index[j+1] +
	    font->height * (((font->index[j] & font->index_mask) + 31) / 32);
	  if (k > content_size)
	    content_size = k;
	}
      if (j > index_size)
	index_size = j;
    }
  ++index_size;
  printf ("};\n");

  /* Output index. */
  printf ("\n/* Index into content data. */\n");
  printf ("static int _%s_index[%d] = {\n", font->name, index_size);
  i = 0;
  while (i < index_size)
    if (font->index[i] != 0 && i < index_size - 1)
      {
	printf ("  0x%x, %d,\n", font->index[i], font->index[i+1]);
	i += 2;
      }
    else if (font->index[i] == 0)
      printf ("  %d,\n", font->index[i++]);
    else
      printf ("  %d, /* Hm... */\n", font->index[i++]);
  printf ("};\n");

  /* Print out each character's picture and data. */
  printf ("\n/* Font character content data. */\n");
  printf ("static u_int32_t _%s_content[] = {\n\n", font->name);
  cp = 0;
  while (cp < content_size)
    {
      int width = 0;
      for (i = 0; i <= font->index_mask; i++)
	for (j = font->offset[i]; font->index[j] != 0; j += 2)
	  if (font->index[j+1] == cp)
	    {
	      wchar_t wc = (font->index[j] & ~font->index_mask) | i;
	      int w = font->index[j] & font->index_mask;
	      if (iswprint(wc))
		{
		  wctomb(0, 0);
		  n = wctomb(buf, wc);
		  buf[(n == -1) ? 0 : n] = '\0';
		  printf ("/* %d: character %s (0x%lx), width %d */\n",
			  cp, buf, (long)wc, w);
		}
	      else
		printf ("/* %d: unprintable character 0x%lx, width %d */\n",
			cp, (long)wc, w);
	      if (w > width)
		width = w;
	    }
      if (width && cp + font->height * ((width + 31) / 32) <= content_size)
	{
	  print_glyph (&font->content[cp], font->height, width);
	  printf ("\n");
	  cp += font->height * ((width + 31) / 32);
	}
      else
	printf ("0x%08x,\n", font->content[cp++]);
    }
  printf ("};\n\n");

  /* Print the font structure definition. */
  printf ("/* Exported structure definition. */\n");
  printf ("const struct bogl_font font_%s = {\n", font->name);
  printf ("  \"%s\",\n", font->name);
  printf ("  %d,\n", font->height);
  printf ("  0x%x,\n", font->index_mask);
  printf ("  _%s_offset,\n", font->name);
  printf ("  _%s_index,\n", font->name);
  printf ("  _%s_content,\n", font->name);
  printf ("};\n");

  return EXIT_SUCCESS;
}

/* Print a picture of the glyph for human inspection, then the hex
   data for machine consumption. */
static void
print_glyph (u_int32_t *content, int height, int width)
{
  int i, j;

  printf ("/* +");
  for (i = 0; i < width; i++)
    printf ("-");
  printf ("+\n");
  for (i = 0; i < height; i++)
    {
      printf ("   |");
      for (j = 0; j < width; j++)
	putchar (content[i+(j/32)*height] & (1 << (31-j%32)) ? '*' : ' ');
      printf ("|\n");
    }
  printf ("   +");
  for (i = 0; i < width; i++)
    printf ("-");
  printf ("+ */\n");

  for (i = 0; i < height*((width+31)/32); i++)
    printf ("0x%08x,\n", content[i]);
}

/* Write with repeated attempts and return number of bytes not written. */
static size_t my_write(int fd, const void *buf, size_t count)
{
  ssize_t n;

  while (count > 0 && (n = write(fd, buf, count)) != -1)
    buf += n, count -= n;
  return count;
}

const unsigned char zero_buf[16];

/* Write BGF font and return non-zero on failure. */
static int bogl_write_font(int fd, struct bogl_font *font)
{
  struct bogl_font font1;
  int i, j, k, index_size, content_size, name_len, name_pad;

  if (my_write(fd, "BGF1", 4))
    return 1;
  memcpy(&font1, font, sizeof(font1));

  /* Get index_size and content_size. */
  index_size = content_size = 0;
  for (i = 0; i <= font->index_mask; i++) {
    for (j = font->offset[i]; font->index[j] != 0; j += 2) {
      k = font->index[j+1] +
	font->height * (((font->index[j] & font->index_mask) + 31) / 32);
      if (k > content_size)
	content_size = k;
    }
    if (j > index_size)
      index_size = j;
  }
  ++index_size;

  name_len = strlen (font->name) + 1;
  name_pad = sizeof (long) - (name_len % sizeof (long));
  if (name_pad == sizeof (long))
    name_pad = 0;

  font1.name = (void *)0 + 4 + sizeof(font1);
  font1.offset = (void *)font1.name + name_len + name_pad;
  font1.index = (void *)font1.offset + (font->index_mask + 1) * sizeof(int);
  font1.content = (void *)font1.index + index_size * sizeof(int);

  if (my_write(fd, &font1, sizeof(font1)) ||
      my_write(fd, font->name, name_len) ||
      my_write(fd, zero_buf, name_pad) ||
      my_write(fd, font->offset, (font->index_mask + 1) * sizeof(int)) ||
      my_write(fd, font->index, index_size * sizeof(int)) ||
      my_write(fd, font->content, content_size * sizeof(u_int32_t)))
    return 1;

  return 0;
}
