#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "vg4.h"

#ifndef S_ISDIR
# define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
#endif

#define VGI_FONT_SEPSPACE 32
#define VGI_FONT_SEPC_MAX 3

extern const char * vg4_get_prefix(int *);
extern void vg4_font_gap_remove(const char *, int, int, VG_BOOL, int *, int);

int cmd_font(int, char **);

static int cmd_font_convert(int, char **);
static int cmd_font_show(int, char **);

enum { HAS_IM = 1, HAS_GM };
static int exec_fontconvert(int, char *, char *, char *, char *, int, const char *);


/* process command "font" */
int
cmd_font(int argc, char **argv)
{
  int retw = 0;

  if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)  { goto help_font; }
  argc--; argv++;

  if (strcmp(argv[0], "convert") == 0) {
    retw = cmd_font_convert(argc, argv);
  } else if (strcmp(argv[0], "show") == 0) {
    retw = cmd_font_show(argc, argv);
  } else {
    goto help_font;
  }

  return retw;

help_font:
  fprintf(stderr, "\nUsage: %s font <subcommand> [<options>]\n", argv0);
  fprintf(stderr, "Subcommands:\n");
  fprintf(stderr, "- convert:  convert true-type font into VgaGames-font\n");
  fprintf(stderr, "- show:     show characters of a VgaGames-font\n");
  fprintf(stderr, "(Use \"%s font <subcommand> -h|--help\" for more infos)\n", argv0);

  return 1;
} /* Ende cmd_font */


/* process command "font convert" */
static int
cmd_font_convert(int argc, char **argv)
{
  int retw = 0;
  int i1, iex, anzz, cindex, dist_w, dist_h, sepc[VGI_FONT_SEPC_MAX], sepi, unknownc;
  char *ptr, *fontfile, *fontdir, *pointsize;
  char bfin[4096], *bfptr, bfzch[8], execmd[256];
  size_t szin;
  VG_BOOL fullwidth;

  if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)  { goto help_font_convert; }

  opterr = opterr ? opterr : 1;

  fontfile = fontdir = pointsize = NULL;
  dist_w = dist_h = 0;
  memset(sepc, 0, sizeof(sepc)); sepi = 0;
  unknownc = 0;
  fullwidth = VG_FALSE;

  while ((i1 = getopt(argc, argv, "+f:d:p:x:y:s:u:w")) != -1) {
    switch(i1) {
      case 'f': fontfile = optarg; break;
      case 'd': fontdir = optarg; break;
      case 'p': pointsize = optarg; break;
      case 'x': dist_w = atoi(optarg); break;
      case 'y': dist_h = atoi(optarg); break;
      case 's': if (sepi < VGI_FONT_SEPC_MAX && vg4->misc->utf8_next(optarg, strlen(optarg), &cindex) > 0) {
                  if (cindex > VGI_FONT_SEPSPACE) { sepc[sepi++] = cindex; }  /* greater space */
                }
                break;
      case 'u': if (vg4->misc->utf8_next(optarg, strlen(optarg), &cindex) > 0) {
                  if (cindex > VGI_FONT_SEPSPACE) { unknownc = cindex; }  /* greater space */
                }
                break;
      case 'w': fullwidth = VG_TRUE; break;
      default:  goto help_font_convert;
    }
  }
  if (fontfile == NULL || *fontfile == '\0' || fontdir == NULL || *fontdir == '\0') { goto help_font_convert; }
  if (strcspn(fontdir, "/.") != strlen(fontdir)) { fprintf(stderr, "-d must just contain a directory with no \".\"\n"); return 1; }
  if (pointsize != NULL && atoi(pointsize) < 4) { fprintf(stderr, "-p must be greater equal 4\n"); return 1; }
  if (dist_w < 0) { dist_w = 0; }
  if (dist_h < 0) { dist_h = 0; }

  /* search for conversion program */
  if ((ptr = which_cmd("gm")) != NULL) {
    iex = HAS_GM;
  } else if ((ptr = which_cmd("convert")) != NULL) {
    iex = HAS_IM;
  } else {
    fprintf(stderr, "%s: needs installed \"convert\" from ImageMagick or \"gm\" from GraphicsMagick\n", argv0);
    return 1;
  }
  snprintf(execmd, sizeof(execmd), "%s", ptr);

  /* read from stdin */
  printf("Reading from stdin\n"); fflush(stdout);
  clearerr(stdin);
  szin = fread(bfin, 1, sizeof(bfin), stdin);
  if (szin == 0 && !feof(stdin)) {
    fprintf(stderr, "%s: error reading from stdin\n", argv0);
    return 1;
  } else if (szin == sizeof(bfin)) {
    fprintf(stderr, "%s: too much input data from stdin\n", argv0);
    return 1;
  }

  /* create output directory */
  { struct stat sbuf;
    if (stat(fontdir, &sbuf) == 0) {
      fprintf(stderr, "%s: \"%s\" exists - aborting\n", argv0, fontdir);
      return 1;
    }
    mkdir(fontdir, 0777);
    if (stat(fontdir, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) {
      fprintf(stderr, "%s: cannot create %s\n", argv0, fontdir);
      return 1;
    }
  }

  /* convert each UTF8-character from stdin */
  bfptr = bfin;
  while ((anzz = vg4->misc->utf8_next(bfptr, szin, &cindex)) > 0) {
    if (anzz >= (int)sizeof(bfzch)) { anzz = -1; break; }
    memmove(bfzch, bfptr, anzz); bfzch[anzz] = '\0';
    bfptr += anzz;
    szin -= anzz;
    if (cindex <= VGI_FONT_SEPSPACE) { continue; }  /* lower equal space */
    if (!exec_fontconvert(iex, fontdir, fontfile, pointsize, bfzch, cindex, execmd)) { return 1; }
  }
  if (anzz < 0) {
    fprintf(stderr, "%s: invalid UTF8-character from stdin\n", argv0);
    return 1;
  }

  /* convert character for unknown characters */
  if (unknownc <= 0) { unknownc = '?'; }
  anzz = vg4->misc->utf8_from_codepoint(unknownc, bfzch);
  if (anzz > 0) {
    bfzch[anzz] = '\0';
    if (!exec_fontconvert(iex, fontdir, fontfile, pointsize, bfzch, 0, execmd)) { return 1; }
  }

  /* remove empty lines and cols and add distances */
  vg4_font_gap_remove(fontdir, dist_w, dist_h, fullwidth, sepc, sepi);

  return retw;

help_font_convert:
  fprintf(stderr, "\nUsage: %s font convert <options>\n", argv0);
  fprintf(stderr, "Convert true-type font into VgaGames-font\n");
  fprintf(stderr, "Options:\n");
  fprintf(stderr, " -f <string>: font file to read\n");
  fprintf(stderr, " -d <string>: name of font-directory to be created,\n");
  fprintf(stderr, "              (the name of the converted font will be identical\n");
  fprintf(stderr, "               to the name of the font-directory)\n");
  fprintf(stderr, " -p <int>:    (optional:) desired point size of font\n");
  fprintf(stderr, " -x <int>:    distance in pixels between characters\n");
  fprintf(stderr, " -y <int>:    distance in pixels between lines\n");
  fprintf(stderr, " -s <char>:   (optional:) additional UTF8-character,\n");
  fprintf(stderr, "               which can be used for line-wrapping besides space,\n");
  fprintf(stderr, "               (may be repeated up to %d times)\n", VGI_FONT_SEPC_MAX);
  fprintf(stderr, " -u <char>:   (optional:) UTF8-character for unknown characters, (default: ?)\n");
  fprintf(stderr, " -w:          create font as non-proportional (each character has same width)\n");
  fprintf(stderr, "Reads from stdin UTF8-characters to convert into font-directory\n");
  fprintf(stderr, "\n");
  fprintf(stderr, "Example:\n");
  fprintf(stderr, "  echo \"ABC\" | %s font convert -f myfont.ttf -d myfont -x 2 -y 3 -s= -s- -u'?' -w\n", argv0);
  fprintf(stderr, "Convert characters \"A\", \"B\" and \"C\" from myfont.ttf\n");
  fprintf(stderr, "into new created myfont/ directory,\n");
  fprintf(stderr, "use 2 pixels for character distance and 3 pixels for line distance,\n");
  fprintf(stderr, "use for additional line-wrapping \"=\" and \"-\",\n");
  fprintf(stderr, "use for unknown characters \"?\",\n");
  fprintf(stderr, "create font as non-proportional.\n");

  return 1;
} /* Ende cmd_font_convert */


/* do conversion */
static int
exec_fontconvert(int iex, char *fontdir, char *fontfile, char *pointsize, char *bfzch, int cindex, const char *execmd)
{
  if (iex == HAS_IM) {  /* ImageMagick */
    char *argp[] = { "convert", "-font", NULL, NULL, NULL, NULL, NULL, NULL };
    char label[9 + strlen(bfzch) + 1];
    char dirfile[strlen(fontdir) + 1 + 16 + 4];
    int i1, argi = 2;
    argp[argi++] = fontfile;
    if (pointsize != NULL) {
      argp[argi++] = "-pointsize";
      argp[argi++] = pointsize;
    }
    snprintf(label, sizeof(label), "label: ");  /* space because of wrong interpretion of ? and * */
    memmove(label + strlen(label), bfzch, strlen(bfzch) + 1);
    argp[argi++] = label;
    if (cindex > 0) {
      snprintf(dirfile, sizeof(dirfile), "%s/%d.pbm", fontdir, cindex);
    } else {
      snprintf(dirfile, sizeof(dirfile), "%s/unknown.pbm", fontdir);
    }
    argp[argi++] = dirfile;
    i1 = noshell_system(execmd, argp);
    if (i1 != 0) {
      char **pptr;
      printf("Calling: ");
      for (pptr = &argp[0]; *pptr != NULL; pptr++) { printf("%s ", *pptr); }
      printf("\n");
      fprintf(stderr, "%s: Conversion failed at \"%s\"\n", argv0, bfzch);
      return 0;
    }

  } else if (iex == HAS_GM) {  /* GraphicsMagick */
    char *argp[] = { "gm", "convert", "-font", NULL, NULL, NULL, NULL, NULL, NULL };
    char label[9 + strlen(bfzch) + 1];
    char dirfile[strlen(fontdir) + 1 + 16 + 4];
    int i1, argi = 3;
    argp[argi++] = fontfile;
    if (pointsize != NULL) {
      argp[argi++] = "-pointsize";
      argp[argi++] = pointsize;
    }
    snprintf(label, sizeof(label), "label: ");  /* space because of wrong interpretion of ? and * */
    memmove(label + strlen(label), bfzch, strlen(bfzch) + 1);
    argp[argi++] = label;
    if (cindex > 0) {
      snprintf(dirfile, sizeof(dirfile), "%s/%d.pbm", fontdir, cindex);
    } else {
      snprintf(dirfile, sizeof(dirfile), "%s/unknown.pbm", fontdir);
    }
    argp[argi++] = dirfile;
    i1 = noshell_system(execmd, argp);
    if (i1 != 0) {
      char **pptr;
      printf("Calling: ");
      for (pptr = &argp[0]; *pptr != NULL; pptr++) { printf("%s ", *pptr); }
      printf("\n");
      fprintf(stderr, "%s: Conversion failed at \"%s\"\n", argv0, bfzch);
      return 0;
    }
  }

  return 1;
} /* Ende exec_fontconvert */


/* process command "font show" */
static int
cmd_font_show(int argc, char **argv)
{
  int i1, wsize, nchars;
  const char *prefix, *fontname, *selname;
  struct VG_Font *fntp;
  struct VG_Fontchar *carray;
  char buf[512], *tchars, dflfont[64];
  size_t blen, tsize;
  struct VG_Canvas *cvas;
  VG_BOOL wext, do_rcol;
  int fwidth, fheight, fgaptop, fgapbottom;

  if (argc < 2)  { goto help_font_show; }
  if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)  { goto help_font_show; }

  prefix = vg4_get_prefix(NULL);

  opterr = opterr ? opterr : 1;

  fontname = NULL;
  wsize = VG_WINDOW_SIZE_LOW;
  wext = VG_FALSE;
  do_rcol = VG_FALSE;

  while ((i1 = getopt(argc, argv, "+RWE")) != -1) {
    switch(i1) {
      case 'R': do_rcol = VG_TRUE; break;
      case 'W': wsize = VG_WINDOW_SIZE_HIGH; break;
      case 'E': wext = VG_TRUE; break;
      default:  goto help_font_show;
    }
  }

  if (optind < argc) { fontname = argv[optind++]; }
  if (fontname == NULL || *fontname == '\0') { goto help_font_show; }

  /* open window */
  vg4->window->size_extend(&wext);
  if (!vg4->window->open(wsize, VG_WINDOW_SCALE_BEST)) { return 1; }
  vg4->window->fill(VG_COLOR_RGB(64, 64, 64));

  /* load font and set to default */
  vg4->font->path(".");
  fntp = vg4->font->load(fontname);
  if (fntp == NULL) { return 1; }
  vg4->font->maxsize(fntp, &fwidth, &fheight);
  vg4->font->gaps(fntp, &fgaptop, &fgapbottom);
  printf("Font %s: maxwidth=%d, maxheight=%d, gap-top=%d, gap-bottom=%d\n",
         vg4->font->getname(fntp), fwidth, fheight, fgaptop, fgapbottom);
  vg4->font->getdefault(dflfont, sizeof(dflfont));
  vg4->font->setdefault(fontname);

  /* get characters */
  nchars = vg4->font->getchars(fntp, &carray);
  if (nchars == 0) { fprintf(stderr, "%s: No characters defined\n", fontname); return 1; }

  /* create text */
  tsize = 0;
  snprintf(buf, sizeof(buf), "%%{table[cells=25,25,25,25 boxwidth=100%% fullraster=0x888888]:\n");
  blen = strlen(buf);
  tchars = malloc(sizeof(char) * (tsize + blen + 1));
  if (tchars == NULL) { fprintf(stderr, "%s: malloc: %s\n", fontname, strerror(errno)); return 1; }
  vg4->misc->strcpy(tchars + tsize, blen + 1, buf);
  tsize += blen;
  snprintf(buf, sizeof(buf), "%%{cell: INDEX%%} %%{cell: WIDTH%%} %%{cell: HEIGHT%%} %%{cell: CHAR%%}\n");
  blen = strlen(buf);
  tchars = realloc(tchars, sizeof(char) * (tsize + blen + 1));
  if (tchars == NULL) { fprintf(stderr, "%s: realloc: %s\n", fontname, strerror(errno)); return 1; }
  vg4->misc->strcpy(tchars + tsize, blen + 1, buf);
  tsize += blen;
  for (i1 = 0; i1 < nchars; i1++) {
    if (do_rcol) {
      snprintf(buf, sizeof(buf),
               "%%{cell: %d%%} %%{cell: %d%%} %%{cell: %d%%} %%{cell: %%{txt[rectcolor=0xff0000]: %s%%}%%}\n",
               carray[i1].index, carray[i1].width, carray[i1].height,
               (strcmp(carray[i1].character, " ") == 0 ? "%{sp:%}" : carray[i1].character)
              );
    } else {
      snprintf(buf, sizeof(buf),
               "%%{cell: %d%%} %%{cell: %d%%} %%{cell: %d%%} %%{cell: %s%%}\n",
               carray[i1].index, carray[i1].width, carray[i1].height,
               (strcmp(carray[i1].character, " ") == 0 ? "%{sp:%}" : carray[i1].character)
              );
    }
    blen = strlen(buf);
    tchars = realloc(tchars, sizeof(char) * (tsize + blen + 1));
    if (tchars == NULL) { fprintf(stderr, "%s: realloc: %s\n", fontname, strerror(errno)); return 1; }
    vg4->misc->strcpy(tchars + tsize, blen + 1, buf);
    tsize += blen;
  }
  snprintf(buf, sizeof(buf), "%%}\n");
  blen = strlen(buf);
  tchars = realloc(tchars, sizeof(char) * (tsize + blen + 1));
  if (tchars == NULL) { fprintf(stderr, "%s: realloc: %s\n", fontname, strerror(errno)); return 1; }
  vg4->misc->strcpy(tchars + tsize, blen + 1, buf);
  tsize += blen;

  free(carray);

  /* load canvas and set title and text */
  snprintf(buf, sizeof(buf), "%s/share/vgagames4/canvas/%s/fontchars/fontchars.top.cvas", prefix, (wsize == VG_WINDOW_SIZE_LOW ? "low" : "high"));
  cvas = vg4->canvas->load(buf, NULL);
  if (cvas == NULL) { return 1; }
  vg4->canvas->text_set(cvas, "title", fontname);
  vg4->canvas->text_set(cvas, "chars", tchars);

  /* execute canvas */
  vg4->canvas->exec(cvas, NULL, &selname);

  /* clean up */
  free(tchars);
  vg4->font->setdefault(dflfont);
  vg4->font->destroy(fntp);
  vg4->canvas->destroy(cvas);
  vg4->window->close();

  return 0;

help_font_show:
  fprintf(stderr, "\nUsage: %s font show [<options>] <fontname>\n", argv0);
  fprintf(stderr, "Show characters of the font\n");
  fprintf(stderr, "Options:\n");
  fprintf(stderr, " -R:  draw a rectangle around the characters\n");
  fprintf(stderr, " -W:  use VG_WINDOW_SIZE_HIGH, (default: VG_WINDOW_SIZE_LOW)\n");
  fprintf(stderr, " -E:  use window-size extension\n");
  fprintf(stderr, "\n");
  return 1;
} /* Ende cmd_font_show */
