/**
   @file unipagecount.c

   @brief unipagecount - Count the number of glyphs defined in each page
                         of 256 code points

   @author Paul Hardy, unifoundry <at> unifoundry.com, December 2007
   
   @copyright Copyright (C) 2007, 2008, 2013, 2014 Paul Hardy

   This program counts the number of glyphs that are defined in each
   "page" of 256 code points, and prints the counts in an 8 x 8 grid.
   Input is from stdin.  Output is to stdout.

   The background color of each cell in a 16-by-16 grid of 256 code points
   is shaded to indicate percentage coverage.  Red indicates 0% coverage,
   green represents 100% coverage, and colors in between pure red and pure
   green indicate partial coverage on a scale.

   Each code point range number can be a hyperlink to a PNG file for
   that 256-code point range's corresponding bitmap glyph image.

   Synopsis:

             unipagecount < font_file.hex > count.txt
             unipagecount -phex_page_num < font_file.hex  -- just 256 points
             unipagecount -h < font_file.hex              -- HTML table
             unipagecount -P1 -h < font.hex > count.html  -- Plane 1, HTML out
             unipagecount -l < font_file.hex              -- linked HTML table
*/
/*
   LICENSE:

      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, see <http://www.gnu.org/licenses/>.
*/

/*
   2018, Paul Hardy: Changed "Private Use" to "Private Use Area" in
   output HTML file.

   21 October 2023 [Paul Hardy]:
   - Added full prototype for mkftable function in main function.

   6 September 2025 [Paul Hardy]:
      - Changed pageno from "int" to "unsigned" for compatibility
        with sscanf definition.
*/

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

#define MAXBUF 256 ///< Maximum input line size - 1.


/**
   @brief The main function.

   @param[in] argc The count of command line arguments.
   @param[in] argv Pointer to array of command line arguments.
   @return This program exits with status 0.
*/
int
main (int argc, char *argv[])
{

   char inbuf[MAXBUF]; /* Max 256 characters in an input line */
   int i, j;  /* loop variables */
   unsigned plane=0; /* Unicode plane number, 0 to 0x16 */
   unsigned page;  /* unicode page (256 bytes wide) */
   unsigned unichar; /* unicode character */
   int pagecount[256] = {256 * 0};
   int onepage=0; /* set to one if printing character grid for one page */
   unsigned pageno=0; /* page number selected if only examining one page */
   int html=0;   /* =0: print plain text; =1: print HTML */
   int links=0;  /* =1: print HTML links; =0: don't print links */

   /* make (print) flipped HTML table */
   void mkftable (unsigned plane, int pagecount[256], int links);

   size_t strlen(const char *s);

   if (argc > 1 && argv[1][0] == '-') {  /* Parse option */
      plane = 0;
      for (i = 1; i < argc; i++) {
         switch (argv[i][1]) {
            case 'p':  /* specified -p<hexpage> -- use given page number */
               sscanf (&argv[1][2], "%x", &pageno);
               if (pageno >= 0 && pageno <= 255) onepage = 1;
               break;
            case 'h':  /* print HTML table instead of text table */
               html = 1;
               break;
            case 'l':  /* print hyperlinks in HTML table */
               links = 1;
               html = 1;
               break;
            case 'P':  /* Plane number specified */
               plane = atoi(&argv[1][2]);
               break;
         }
      }
   }
   /*
      Initialize pagecount to account for noncharacters.
   */
   if (!onepage && plane==0) {
      pagecount[0xfd] = 32;  /* for U+FDD0..U+FDEF */
   }
   pagecount[0xff] = 2;   /* for U+nnFFFE, U+nnFFFF */
   /*
      Read one line at a time from input.  The format is:

         <hexpos>:<hexbitmap>

      where <hexpos> is the hexadecimal Unicode character position
      in the range 00..FF and <hexbitmap> is the sequence of hexadecimal
      digits of the character, laid out in a grid from left to right,
      top to bottom.  The character is assumed to be 16 rows of variable
      width.
   */
   while (fgets (inbuf, MAXBUF-1, stdin) != NULL) {
      sscanf (inbuf, "%X", &unichar);
      page = unichar >> 8;
      if (onepage) { /* only increment counter if this is page we want */
         if (page == pageno) { /* character is in the page we want */
            pagecount[unichar & 0xff]++; /* mark character as covered */
         }
      }
      else { /* counting all characters in all pages */
         if (plane == 0) {
            /* Don't add in noncharacters (U+FDD0..U+FDEF, U+FFFE, U+FFFF) */
            if (unichar < 0xfdd0 || (unichar > 0xfdef && unichar < 0xfffe))
               pagecount[page]++;
         }
         else {
            if ((page >> 8) == plane) { /* code point is in desired plane */
               pagecount[page & 0xFF]++;
            }
         }
      }
   }
   if (html) {
      mkftable (plane, pagecount, links);
   }
   else {  /* Otherwise, print plain text table */
      if (plane > 0) fprintf (stdout, "  ");
      fprintf (stdout,
         "     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\n");
      for (i=0; i<0x10; i++) {
         fprintf (stdout,"%02X%X ", plane, i); /* row header */
         for (j=0; j<0x10; j++) {
            if (onepage) {
               if (pagecount[i*16+j])
                  fprintf (stdout," *  ");
               else
                  fprintf (stdout," .  ");
            }
            else {
               fprintf (stdout, "%3X ", pagecount[i*16+j]);
            }
         }
         fprintf (stdout,"\n");
      }
   
   }
   exit (0);
}


/**
   @brief Create an HTML table linked to PNG images.

   This function creates an HTML table to show PNG files
   in a 16 by 16 grid.  The background color of each "page"
   of 256 code points is shaded from red (for 0% coverage)
   to green (for 100% coverage).

   @param[in] plane The Unicode plane, 0..17.
   @param[in] pagecount Array with count of glyphs in each 256 code point range.
   @param[in] links 1 = generate hyperlinks, 0 = do not generate hyperlinks.
*/
void
mkftable (unsigned plane, int pagecount[256], int links)
{
   int i, j;
   int count;
   unsigned bgcolor;
   
   printf ("<html>\n");
   printf ("<body>\n");
   printf ("<table border=\"3\" align=\"center\">\n");
   printf ("  <tr><th colspan=\"16\" bgcolor=\"#ffcc80\">");
   printf ("GNU Unifont Glyphs<br>with Page Coverage for Plane %d<br>(Green=100%%, Red=0%%)</th></tr>\n", plane);
   for (i = 0x0; i <= 0xF; i++) {
      printf ("  <tr>\n");
      for (j = 0x0; j <= 0xF; j++) {
         count = pagecount[ (i << 4) | j ];
         
         /* print link in cell if links == 1 */
         if (plane != 0 || (i < 0xd || (i == 0xd && j < 0x8) || (i == 0xf && j > 0x8))) {
            /* background color is light green if completely done */
            if (count == 0x100) bgcolor = 0xccffcc;
            /* otherwise background is a shade of yellow to orange to red */
            else bgcolor = 0xff0000 | (count << 8) | (count >> 1);
            printf ("    <td bgcolor=\"#%06X\">", bgcolor);
            if (plane == 0)
               printf ("<a href=\"png/plane%02X/uni%02X%X%X.png\">%X%X</a>", plane, plane, i, j, i, j);
            else
               printf ("<a href=\"png/plane%02X/uni%02X%X%X.png\">%02X%X%X</a>", plane, plane, i, j, plane, i, j);
            printf ("</td>\n");
         }
         else if (i == 0xd) {
            if (j == 0x8) {
               printf ("    <td align=\"center\" colspan=\"8\" bgcolor=\"#cccccc\">");
               printf ("<b>Surrogate Pairs</b>");
               printf ("</td>\n");
            }  /* otherwise don't print anything more columns in this row */
         }
         else if (i == 0xe) {
            if (j == 0x0) {
               printf ("    <td align=\"center\" colspan=\"16\" bgcolor=\"#cccccc\">");
               printf ("<b>Private Use Area</b>");
               printf ("</td>\n");
            }  /* otherwise don't print any more columns in this row */
         }
         else if (i == 0xf) {
            if (j == 0x0) {
               printf ("    <td align=\"center\" colspan=\"9\" bgcolor=\"#cccccc\">");
               printf ("<b>Private Use Area</b>");
               printf ("</td>\n");
            }
         }
      }
      printf ("  </tr>\n");
   }
   printf ("</table>\n");
   printf ("</body>\n");
   printf ("</html>\n");

   return;
}
