/****************************************************************************
 * tools/bdf-converter.c
 *
 *   Copyright (C) 2011 NX Engineering, S.A., All rights reserved.
 *   Author: Jose Pablo Carballo Gomez <jcarballo@nx-engineering.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/*
 * Based one the "Glyph Bitmap Distribution Format (BDF) Specification",
 * Version 2.2, by Adobe Systems Incorporated.
 *
 */

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/*
#define VERBOSE
#define DBG
*/

// BDF Specification Version 2.2:
// This version lifts the restriction on line length. In this version, the new
// maximum length of a value of the type string is 65535 characters, and hence
// lines may now be at least this long.

#define BDF_MAX_LINE_LENGTH 65535

/* Ranges of 7-bit and 8-bit fonts */

#define NXFONT_MIN7BIT 33
#define NXFONT_MAX7BIT 126

#define NXFONT_MIN8BIT 161
#define NXFONT_MAX8BIT 255

/****************************************************************************
 * Public Types
 ****************************************************************************/

/* This structure holds information about a glyph */

typedef struct glyphinfo_s
{
  char *name;       /* Name for they glyph */
  int   encoding;   /* The Adobe Standard Encoding value */
  int   dw_x0;      /* Width in x of the vector indicating
                     * the position of the next glyph's origin
                     * relative to the origin of this glyph */
  int   dw_y0;      /* Width in y of the vector indicating
                     * the position of the next glyph's origin
                     * relative to the origin of this glyph */
  int   bb_w;       /* The width of the black pixels in x */
  int   bb_h;       /* The height of the black pixels in y */
  int   bb_x_off;   /* X displacement of the lower left corner
                     * of the bitmap from origin 0 */
  int   bb_y_off;   /* Y displacement of the lower left corner
                     * of the bitmap from origin 0 */
  uint64_t *bitmap; /* Hexadecimal data for the character bitmap */
} glyphinfo_t;

/* This structures provides the metrics for one glyph */

typedef struct nx_fontmetric_s
{
  uint32_t stride   : 3;    /* Width of one font row in bytes */
  uint32_t width    : 6;    /* Width of the font in bits */
  uint32_t height   : 6;    /* Height of the font in rows */
  uint32_t xoffset  : 6;    /* Top, left-hand corner X-offset in pixels */
  uint32_t yoffset  : 6;    /* Top, left-hand corner y-offset in pixels */
  uint32_t unused   : 5;
} nx_fontmetric_t;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: MY_strtok_r
 *
 * Description:
 *    MinGW does not seem to provide strtok_r
 *
 ****************************************************************************/

#ifndef HAVE_STRTOK_R
static char *MY_strtok_r(char *str, const char *delim, char **saveptr)
{
  char *pbegin;
  char *pend = NULL;

  /* Decide if we are starting a new string or continuing from
   * the point we left off.
   */

  if (str)
    {
      pbegin = str;
    }
  else if (saveptr && *saveptr)
    {
      pbegin = *saveptr;
    }
  else
    {
      return NULL;
    }

  /* Find the beginning of the next token */

  for (;
       *pbegin && strchr(delim, *pbegin) != NULL;
       pbegin++);

  /* If we are at the end of the string with nothing
   * but delimiters found, then return NULL.
   */

  if (!*pbegin)
    {
      return NULL;
    }

  /* Find the end of the token */

  for (pend = pbegin + 1;
       *pend && strchr(delim, *pend) == NULL;
       pend++);

  /* pend either points to the end of the string or to
   * the first delimiter after the string.
   */

  if (*pend)
    {
      /* Turn the delimiter into a null terminator */

      *pend++ = '\0';
    }

  /* Save the pointer where we left off and return the
   * beginning of the token.
   */

  if (saveptr)
    {
      *saveptr = pend;
    }
  return pbegin;
}

#undef strtok_r
#define strtok_r MY_strtok_r
#endif

/****************************************************************************
 * Name: trimLine
 *
 * Description:
 *   Trims the line removing space characters at the front and at the end
 *   of the line.
 *
 * Input Parameters:
 *   line  - The line to trim
 *
 ****************************************************************************/

static void trimLine(char *line)
{
  char *str;
  str = line;
  char *strEnd;
  for (strEnd = str + strlen(str) - 1;
       strEnd >= str && isspace((int)(*strEnd));
       strEnd--);
  *(strEnd + 1) = 0;
}

/****************************************************************************
 * Name: bdf_parseIntLine
 *
 * Description:
 *   Parses a line containing a BDF property followed by integers. It will
 *   ignore the first token that corresponds to the property name.
 *
 * Input Parameters:
 *   line  - A line with a BDF property followed by integers, i.e.:
 *                  "FONTBOUNDINGBOX 8 13 0 -2"
 *   count - How many integers are specified by the BDF property. In the
 *           example above, count = 4.
 *   info  - A pointer to memory provided by the caller in which to
 *           return the array of integers. For the example above:
 *                info[0] =  8
 *                info[1] = 13
 *                info[2] =  0
 *                info[3] = -2
 *
 ****************************************************************************/

static void bdf_parseintline(char *line, unsigned int count, int *info)
{
  char *str, *token, *saveptr1;
  str = line;

  /* Ignore the key */

  token = (char *)strtok_r(str, " ", &saveptr1);

  while ((token = (char *)strtok_r(NULL, " ", &saveptr1)) && count--)
    {
      *(info++) = atoi(token);
    }
}

/****************************************************************************
 * Name: bdf_printglyphinfo
 *
 * Description:
 *   Prints the information available for a glyph.
 *
 * Input Parameters:
 *   ginfo  - A glyphinfo_t struct with the glyph's information.
 *
 ****************************************************************************/

#ifdef DBG
static void bdf_printglyphinfo(const glyphinfo_t *ginfo)
{
  printf("NAME     = %s\n", ginfo->name);
  printf("ENCODING = %d\n", ginfo->encoding);
  printf("DW_X0    = %d\n", ginfo->dw_x0);
  printf("DW_Y0    = %d\n", ginfo->dw_y0);
  printf("BB_W     = %d\n", ginfo->bb_w);
  printf("BB_H     = %d\n", ginfo->bb_h);
  printf("BB_X_OFF = %d\n", ginfo->bb_x_off);
  printf("BB_Y_OFF = %d\n", ginfo->bb_y_off);
  int i;
  for (i = 0; i < ginfo->bb_h; i++)
    {
      printf("BITMAP[%d] = %x\n", i, ginfo->bitmap[i]);
    }
}
#endif /* DBG */

/****************************************************************************
 * Name: bdf_printnxmetricinfo
 *
 * Description:
 *   Prints the information available for a glyph's metric in the NX
 *   graphics system.
 *
 * Input Parameters:
 *   info  - A nx_fontmetric_t struct with the glyph's information.
 *
 ****************************************************************************/

#ifdef DBG
static void bdf_printnxmetricinfo(const nx_fontmetric_t *info)
{
  printf("STRIDE  = %d\n", info->stride);
  printf("WIDTH   = %d\n", info->width);
  printf("HEIGHT  = %d\n", info->height);
  printf("XOFFSET = %d\n", info->xoffset);
  printf("YOFFSET = %d\n", info->yoffset);
}
#endif /* DBG */

/****************************************************************************
 * Name: bdf_getglyphinfo
 *
 * Description:
 *   Obtains the information for an individual glyph. The BDF properties
 *   taken into account are:
 *     - ENCODING
 *     - DWIDTH
 *     - BBX
 *   BDF properties ignored:
 *     - SWIDTH
 *     - SWIDTH1
 *     - DWIDTH1
 *     - VVECTOR
 *
 * Input Parameters:
 *   file  - The input file stream pointing to the first line of the
 *           glyph's information (right after STARTCHAR).
 *   ginfo - A glyphinfo_t struct to fill with the glyph's information.
 *
 ****************************************************************************/

static void bdf_getglyphinfo(FILE *file, glyphinfo_t *ginfo)
{
  char line[BDF_MAX_LINE_LENGTH];
  char lineCopy[BDF_MAX_LINE_LENGTH];
  char *str, *token, *saveptr1;
  bool done;

  done = false;

  while (fgets(line, BDF_MAX_LINE_LENGTH, file) != NULL && !done)
    {
      trimLine(line);
      strcpy(lineCopy, line);
      str = line;

      while ((token = (char *)strtok_r(str, " ", &saveptr1)))
        {

          /* ENCODING information */

          if (strcmp(token, "ENCODING") == 0)
            {
              token = (char *)strtok_r(NULL, " ", &saveptr1);
              ginfo->encoding = atoi(token);
            }

          /* DWIDTH information */

          if (strcmp(token, "DWIDTH") == 0)
            {
              token = (char *)strtok_r(NULL, " ", &saveptr1);
              ginfo->dw_x0 = atoi(token);
              token = (char *)strtok_r(NULL, " ", &saveptr1);
              ginfo->dw_y0 = atoi(token);
            }

          /* BBX information */

          else if (strcmp(token, "BBX") == 0)
            {
              int bbxinfo[4];
              bdf_parseintline(lineCopy, 4, bbxinfo);
              ginfo->bb_w     = bbxinfo[0];
              ginfo->bb_h     = bbxinfo[1];
              ginfo->bb_x_off = bbxinfo[2];
              ginfo->bb_y_off = bbxinfo[3];

              /* This is the last BDF property of interest*/

              done = true;
            }

          str = NULL;
        }
    }
}

/****************************************************************************
 * Name: bdf_getglyphbitmap
 *
 * Description:
 *   Obtains the character bitmap information for an individual glyph.
 *
 * Input Parameters:
 *   file  - The input file stream pointing to the first line of the
 *           glyph's bitmap (right after BITMAP).
 *   ginfo - A glyphinfo_t struct to fill with the glyph's bitmap.
 *
 ****************************************************************************/

static void bdf_getglyphbitmap(FILE *file, glyphinfo_t *ginfo)
{
  char line[BDF_MAX_LINE_LENGTH];
  uint64_t *bitmap;
  bool readingbitmap;

  bitmap = ginfo->bitmap;
  readingbitmap = true;

  while (readingbitmap)
    {
      if (fgets(line, BDF_MAX_LINE_LENGTH, file) != NULL)
        {
          trimLine(line);

          if (strcmp(line, "ENDCHAR") == 0)
            {
              readingbitmap = false;
            }
          else
            {
              char *endptr;
              *bitmap = strtoul(line, &endptr, 16);
              bitmap++;
            }
        }
      else
        {
          /* error condition */

          readingbitmap = false;
        }
    }
}

/****************************************************************************
 * Name: bdf_getstride
 *
 * Description:
 *   Obtains the stride for an individual glyph. The stride is the width
 *   of one glyph's bitmap row in bytes.
 *
 * Input Parameters:
 *   ginfo  - A glyphinfo_t struct with the glyph's information.
 *   stride - A pointer to memory provided by the caller in which to
 *            return the stride.
 *
 ****************************************************************************/
static void bdf_getstride(glyphinfo_t *ginfo, uint32_t *stride)
{
  *stride = (ginfo->bb_w % 8 == 0) ? ginfo->bb_w / 8 : ginfo->bb_w / 8 + 1;
}

/****************************************************************************
 * Name: bdf_printoutput
 *
 * Description:
 *   Prints to the output stream the information of an individual glyph in
 *   the NuttX font format.
 *
 * Input Parameters:
 *   out       - The output stream.
 *   ginfo     - A glyphinfo_t struct with the glyph's information.
 *   nxmetric  - A nx_fontmetric_t struct with the glyph's information.
 *
 ****************************************************************************/
static void bdf_printoutput(FILE *out,
                            glyphinfo_t *ginfo,
                            nx_fontmetric_t *nxmetric)
{

  /* Only interested in the 7 and 8 bit ranges */

  if ((ginfo->encoding >= NXFONT_MIN7BIT  &&
       ginfo->encoding <= NXFONT_MAX7BIT) ||
      (ginfo->encoding >= NXFONT_MIN8BIT  &&
       ginfo->encoding <= NXFONT_MAX8BIT))
    {
      /* Glyph general info */

      if (ginfo->bb_x_off < 0)
        {
          fprintf(out,
                  "/* %s (%d) -- NOTE: Xoffset should be %d, not 0. */\n",
                  ginfo->name,
                  ginfo->encoding,
                  ginfo->bb_x_off);
        }
      else
        {
          fprintf(out, "/* %s (%d) */\n", ginfo->name, ginfo->encoding);
        }

      /* Glyph metrics */

      fprintf(out,
              "#define NXFONT_METRICS_%d {%d, %d, %d, %d, %d, 0}\n",
              ginfo->encoding,
              nxmetric->stride,
              nxmetric->width,
              nxmetric->height,
              nxmetric->xoffset,
              nxmetric->yoffset);

      /* Glyph bitmap */

      fprintf(out, "#define NXFONT_BITMAP_%d {", ginfo->encoding);
      int i, j;
      for (i = 0; i < ginfo->bb_h - 1; i++)
        {
          for (j = 1; j <= nxmetric->stride; j++)
            {
              int nxbyteoffset;
              uint8_t  nxbyte = 0;
              uint64_t tempbitmap = ginfo->bitmap[i];

              /* Get the next byte */

              nxbyteoffset = (nxmetric->stride - j) * 8;
              nxbyte = (uint8_t)(tempbitmap >> nxbyteoffset);
              fprintf(out, "0x%x, ", nxbyte);
            }
        }

      /* Different behavior for the last bitmap */

      for (j = 1; j <= nxmetric->stride; j++)
        {
          int nxbyteoffset;
          uint8_t  nxbyte = 0;
          uint64_t tempbitmap = ginfo->bitmap[i];

          /* Get the next byte */

          nxbyteoffset = (nxmetric->stride - j) * 8;
          nxbyte = (uint8_t)(tempbitmap >> nxbyteoffset);

          if (j == nxmetric->stride)
            {
              fprintf(out, "0x%x}\n", nxbyte);
            }
          else
            {
              fprintf(out, "0x%x, ", nxbyte);
            }
        }

      fprintf(out, "\n");
    }
}

/****************************************************************************
 * Main
 ****************************************************************************/

int main(int argc, char **argv)
{
  FILE *file, *out;
  char line[BDF_MAX_LINE_LENGTH];
  char lineCopy[BDF_MAX_LINE_LENGTH];
  char *str, *token, *saveptr1;
  char *input, *output;

  /* FONTBOUNDINGBOX properties*/

  int fbb_x     = 0;
  int fbb_y     = 0;
//int fbb_x_off = 0;
  int fbb_y_off = 0;

  /* Input BDF file */

  input = argv[1];

  if (input == NULL)
    {
      printf("%s: no input file\n", argv[0]);
      exit(0);
    }

  file = fopen(input, "r");

  if (file == NULL)
    {
      printf("%s: error opening file %s\n", argv[0], input);
      exit(0);
    }
  else
    {
#ifdef VERBOSE
      printf("Opening \"%s\"\n", input);
#endif /* VERBOSE */
    }

  /* Output file */

  if (argv[2])
    {
      output = argv[2];
    }
  else
    {
      output = "nxfonts_myfont.h";
    }

  out  = fopen(output, "w");

  if (out == NULL)
    {
      printf("%s: error opening file %s\n", argv[0], output);
      fclose(file);
      exit(0);
    }
  else
    {
      while (fgets(line, BDF_MAX_LINE_LENGTH, file) != NULL)
        {

#ifdef DBG
          printf("--\n");
#endif /* DBG */

          // Save a copy of the line

          strcpy(lineCopy,line);

          // Clean it

          trimLine(line);
          str = line;

          while ((token = (char *)strtok_r(str, " ", &saveptr1)))
            {

              /* FONTBOUNDINGBOX - Global font information */

              if (strcmp(token, "FONTBOUNDINGBOX") == 0)
                {
                  int fbbinfo[4];
                  bdf_parseintline(lineCopy, 4, fbbinfo);
                  fbb_x     = fbbinfo[0];
                  fbb_y     = fbbinfo[1];
                //fbb_x_off = fbbinfo[2];
                  fbb_y_off = fbbinfo[3];

                  /* Print FONTBOUNDINGBOX information */

                  fprintf(out, "/* Maximum height and width of any");
                  fprintf(out, " glyph in the set */\n\n");
                  fprintf(out, "#define NXFONT_MAXHEIGHT  %d\n", fbb_y);
                  fprintf(out, "#define NXFONT_MAXWIDTH   %d\n\n", fbb_x);
                }

              /* STARTCHAR - Individual glyph information */

              if (strcmp(token, "STARTCHAR") == 0)
                {
                  glyphinfo_t ginfo;

                  /* Glyph name */

                  ginfo.name = (char *)strtok_r(NULL, " ", &saveptr1);

#ifdef VERBOSE
                  printf("Processing glyph: %s\n", ginfo.name);
#endif /* VERBOSE */

                  /* Glyph information:
                  *    ENCODING
                  *    DWIDTH
                  *    BBX
                  */

                  ginfo.encoding = 0;
                  ginfo.dw_x0    = 0;
                  ginfo.dw_y0    = 0;
                  ginfo.bb_w     = 0;
                  ginfo.bb_h     = 0;
                  ginfo.bb_x_off = 0;
                  ginfo.bb_y_off = 0;
                  bdf_getglyphinfo(file, &ginfo);

                  /* Glyph bitmap */

                  ginfo.bitmap = malloc(sizeof(uint64_t) * ginfo.bb_h);
                  bdf_getglyphbitmap(file, &ginfo);

#ifdef DBG
                  bdf_printglyphinfo(&ginfo);
#endif /* DBG */

                  /* Convert to nxfonts */

                  nx_fontmetric_t nxmetric;
                  uint32_t stride;
                  bdf_getstride(&ginfo, &stride);
                  nxmetric.stride  = stride;
                  nxmetric.width   = ginfo.bb_w;
                  nxmetric.height  = ginfo.bb_h;

                  /* The NuttX font format does not support
                   * negative X offsets. */

                  if (ginfo.bb_x_off < 0)
                    {
                      nxmetric.xoffset = 0;
                      printf("%s: ignoring negative x offset for "
                             "glyph '%s' (%d)\n",
                             argv[0],
                             ginfo.name,
                             ginfo.encoding);
                    }
                  else
                    {
                      nxmetric.xoffset = ginfo.bb_x_off;
                    }

                  nxmetric.yoffset = fbb_y + fbb_y_off -
                                     ginfo.bb_y_off - ginfo.bb_h;

#ifdef DBG
                  bdf_printnxmetricinfo(&nxmetric);
#endif /* DBG */

                  /* The space (32) character is treated differently */

                  if (ginfo.encoding == 32)
                    {
                      fprintf(out, "/* The width of a space */\n\n");
                      fprintf(out, "#define NXFONT_SPACEWIDTH %d\n\n", ginfo.dw_x0);
                    }
                  else
                    {
                      bdf_printoutput(out, &ginfo, &nxmetric);
                    }

                  /* Free memory */

                  free(ginfo.bitmap);

                }

              str = NULL;
            }

        }

      fclose(file);
      fclose(out);

      /* The End */

      printf("Generated \"%s\"\n", output);

    }

  return EXIT_SUCCESS;
}
