/*
 * [Asciitab.java]
 *
 * Summary: prepares ASCII table in HTML.
 *
 * Copyright: (c) 2001-2012 Roedy Green, Canadian Mind Products, http://mindprod.com
 *
 * Licence: This software may be copied and used freely for any purpose but military.
 *          http://mindprod.com/contact/nonmil.html
 *
 * Requires: JDK 1.5+
 *
 * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
 *
 * Version History:
 *  1.1 2009-03-22 add style markup, character entities
 *  1.2 2011-11-23 integrate into Palette
 */
package com.mindprod.palette;

import com.mindprod.entities.EntifyStrings;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import static java.lang.System.err;

/**
 * prepares ASCII table in HTML.
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 1.2 2011-11-23 integrate into Palette
 * @since 2001
 */
public final class Asciitab
    {
    // ------------------------------ CONSTANTS ------------------------------

    /**
     * boilerplace  used to prepare descriptions
     */
    private static final String GLUE1 = " <span class=\"acronym2\">";

    /**
     * boilerplace  used to prepare descriptions
     */
    private static final String GLUE2 = "</span>, <span class=\"means2\">";

    // -------------------------- STATIC METHODS --------------------------

    /**
     * how you display this character in HTML
     *
     * @param c the character you want to render
     *
     * @return html for rendering the character
     */
    private static String asHTML( char c )
        {
        if ( c < 32 )
            {
            /* control char */
            return "&#x" + toLZ( c + 0x2400, 4, 16 ) + ";";
            }
        else if ( c > 126 )
            {
            /* high ascii */
            // decimal form: "&#" + Integer.toString( c ) + ";";
            return EntifyStrings.entifyHTML( String.valueOf( c ) );
            }
        else
            {
            switch ( c )
                {
                case 32:
                    return "&nbsp;";
                case 38:
                    return "&amp;";
                case 34:
                    return "&quot;";
                case 60:
                    return "&lt;";
                case 62:
                    return "&gt;";
                /* ordinary char */
                default:
                    return String.valueOf( c );
                }
            }
        }

    /**
     * how you display this character in HTML, quoted so you can see the code
     *
     * @param c the character you want to render
     *
     * @return html for rendering the character
     */
    private static String asQuotedHTML( char c )
        {
        String result = asHTML( c );
        if ( result.charAt( 0 ) == '&' )
            {
            return "&amp;" + result.substring( 1 );
            }
        else
            {
            return result;
            }
        }

    /**
     * Get description of a character
     *
     * @param c character to describe
     *
     * @return string destribing character, or &nbsp; if none available.
     */
    @SuppressWarnings( { "OctalInteger" } )
    private static String descs( char c )
        {
        if ( 'A' <= c && c <= 'Z' )
            {
            return "upper case " + String.valueOf( c );
            }
        else if ( 'a' <= c && c <= 'z' )
            {
            return "lower case " + String.valueOf( c );
            }
        else if ( '0' <= c && c <= '9' )
            {
            return "digit " + String.valueOf( c );
            }
        else
            {
            switch ( c )
                {
                case 0:
                    return "^@" +
                           GLUE1 +
                           "NUL" +
                           GLUE2 +
                           "<span class=\"ac2\">nul</span></span>";
                case 1:
                    return "^A" +
                           GLUE1 +
                           "SOH" +
                           GLUE2 +
                           "<span class=\"ac2\">s</span>tart <span class=\"ac2\">o</span>f <span class=\"ac2\">h</span>eader</span>";
                case 2:
                    return "^B" +
                           GLUE1 +
                           "STX" +
                           GLUE2 +
                           "<span class=\"ac2\">s</span>tart of <span class=\"ac2\">t</span>e<span class=\"ac2\">x</span>t</span>";
                case 3:
                    return "^C" +
                           GLUE1 +
                           "ETX" +
                           GLUE2 +
                           "<span class=\"ac2\">e</span>nd of <span class=\"ac2\">t</span>e<span class=\"ac2\">x</span>t</span>";
                case 4:
                    return "^D" +
                           GLUE1 +
                           "EOT" +
                           GLUE2 +
                           "<span class=\"ac2\">e</span>nd <span class=\"ac2\">o</span>f <span class=\"ac2\">t</span>ransmission</span>";
                case 5:
                    return "^E" +
                           GLUE1 +
                           "ENQ" +
                           GLUE2 +
                           "<span class=\"ac2\">e</span><span class=\"ac2\">nq</span>uiry</span>";
                case 6:
                    return "^F" +
                           GLUE1 +
                           "ACK" +
                           GLUE2 +
                           "<span class=\"ac2\">ack</span>nowledege</span>";
                case 7:
                    return "^G" +
                           GLUE1 +
                           "BEL" +
                           GLUE2 +
                           "<span class=\"ac2\">bel</span>l</span>";
                case 8:
                    return "^H" +
                           GLUE1 +
                           "BS" +
                           GLUE2 +
                           "<span class=\"ac2\">b</span>ack <span class=\"ac2\">s</span>pace</span> [\\b]";
                case 9:
                    return "^I" +
                           GLUE1 +
                           "HT" +
                           GLUE2 +
                           "<span class=\"ac2\">h</span>orizonal <span class=\"ac2\">t</span>ab</span> [\\t]";
                case 10:
                    return "^J" +
                           GLUE1 +
                           "LF" +
                           GLUE2 +
                           "<span class=\"ac2\">l</span>ine <span class=\"ac2\">f</span>eed</span> [\\n]";
                case 11:
                    return "^K" +
                           GLUE1 +
                           "VT" +
                           GLUE2 +
                           "<span class=\"ac2\">v</span>ertical <span class=\"ac2\">t</span>ab</span>";
                case 12:
                    return "^L" +
                           GLUE1 +
                           "FF" +
                           GLUE2 +
                           "<span class=\"ac2\">f</span>orm <span class=\"ac2\">f</span>eed</span> [\\f]";
                case 13:
                    return "^M" +
                           GLUE1 +
                           "CR" +
                           GLUE2 +
                           "<span class=\"ac2\">c</span>arriage <span class=\"ac2\">r</span>eturn</span> [\\r]";
                case 14:
                    return "^N" +
                           GLUE1 +
                           "SO" +
                           GLUE2 +
                           "<span class=\"ac2\">s</span>hift <span class=\"ac2\">o</span>ut</span>";
                case 15:
                    return "^O" +
                           GLUE1 +
                           "SI" +
                           GLUE2 +
                           "<span class=\"ac2\">s</span>hift <span class=\"ac2\">i</span>n</span>";
                case 16:
                    return "^P" +
                           GLUE1 +
                           "DLE" +
                           GLUE2 +
                           "<span class=\"ac2\">d</span>ata <span class=\"ac2\">l</span>ink <span class=\"ac2\">e</span>scape</span>";
                case 17:
                    return "^Q" +
                           GLUE1 +
                           "DC1" +
                           GLUE2 +
                           "<span class=\"ac2\">d</span>evice <span class=\"ac2\">c</span>ontrol <span class=\"ac2\">1</span></span>,"
                           + " XON resume transmission";
                case 18:
                    return "^R" +
                           GLUE1 +
                           "DC2" +
                           GLUE2 +
                           "<span class=\"ac2\">d</span>evice <span class=\"ac2\">c</span>ontrol <span class=\"ac2\">2</span></span>";
                case 19:
                    return "^S" +
                           GLUE1 +
                           "DC3" +
                           GLUE2 +
                           "<span class=\"ac2\">d</span>evice <span class=\"ac2\">c</span>ontrol <span class=\"ac2\">3</span></span>,"
                           + " XOFF pause transmission";
                case 20:
                    return "^T" +
                           GLUE1 +
                           "DC4" +
                           GLUE2 +
                           "<span class=\"ac2\">d</span>evice <span class=\"ac2\">c</span>ontrol <span class=\"ac2\">4</span></span>";
                case 21:
                    return "^U" +
                           GLUE1 +
                           "NAK" +
                           GLUE2 +
                           "<span class=\"ac2\">n</span>egative <span class=\"ac2\">a</span>c<span class=\"ac2\">k</span>nowledge</span>";
                case 22:
                    return "^V" +
                           GLUE1 +
                           "SYN" +
                           GLUE2 +
                           "<span class=\"ac2\">syn</span>chronise</span>";
                case 23:
                    return "^W" +
                           GLUE1 +
                           "ETB" +
                           GLUE2 +
                           "<span class=\"ac2\">e</span>nd <span class=\"ac2\">t</span>ext <span class=\"ac2\">b</span>lock</span>";
                case 24:
                    return "^X" +
                           GLUE1 +
                           "CAN" +
                           GLUE2 +
                           "<span class=\"ac2\">can</span>cel</span>";
                case 25:
                    return "^Y" +
                           GLUE1 +
                           "EM" +
                           GLUE2 +
                           "<span class=\"ac2\">e</span>nd <span class=\"ac2\">m</span>essage</span>";
                case 26:
                    return "^Z" +
                           GLUE1 +
                           "SUB" +
                           GLUE2 +
                           "<span class=\"ac2\">sub</span>stitute</span>";
                case 27:
                    return "^[" +
                           GLUE1 +
                           "ESC" +
                           GLUE2 +
                           "<span class=\"ac2\">esc</span>ape</span>";
                case 28:
                    return "^\\" +
                           GLUE1 +
                           "FS" +
                           GLUE2 +
                           "<span class=\"ac2\">f</span>ile <span class=\"ac2\">s</span>eparator</span>,"
                           + " usually used to groups of records.";
                case 29:
                    return "^]" +
                           GLUE1 +
                           "GS" +
                           GLUE2 +
                           "<span class=\"ac2\">g</span>roup <span class=\"ac2\">s</span>eparator</span>,"
                           + " usually used to separate fields.";
                case 30:
                    return "^^" +
                           GLUE1 +
                           "RS" +
                           GLUE2 +
                           "<span class=\"ac2\">r</span>ecord <span class=\"ac2\">s</span>eparator</span>,"
                           + " usually used to separate records.";
                case 31:
                    return "^_" +
                           GLUE1 +
                           "US" +
                           GLUE2 +
                           "<span class=\"ac2\">u</span>nit <span class=\"ac2\">s</span>eparator</span>,"
                           + " usually used to separate subfields.";
                case 32:
                    return "space";
                case 33:
                    return "bang, exclamation";
                case 34:
                    return "quote";
                case 35:
                    return "sharp, number sign";
                case 36:
                    return "dollar sign";
                case 37:
                    return "percent";
                case 38:
                    return "ampersand";
                case 39:
                    return "apostrophe";
                case 40:
                    return "left parenthesis";
                case 41:
                    return "right parenthesis";
                case 42:
                    return "star, asterisk";
                case 43:
                    return "plus";
                case 44:
                    return "comma";
                case 45:
                    return "minus";
                case 46:
                    return "period";
                case 47:
                    return "slash, ,<strong>not backslash!</strong>";
                case 58:
                    return "colon";
                case 59:
                    return "semicolon";
                case 60:
                    return "less than";
                case 61:
                    return "equals";
                case 62:
                    return "greater than";
                case 63:
                    return "question mark";
                case 64:
                    return "at sign";
                case 91:
                    return "left square bracket";
                case 92:
                    return "backslash, <strong>not slash!</strong>";
                case 93:
                    return "right square bracket";
                case 94:
                    return "hat, circumflex";
                case 95:
                    return "underscore";
                case 96:
                    return "grave, rhymes with have";
                case 123:
                    return "left curly brace";
                case 124:
                    return "vertical bar";
                case 125:
                    return "right curly brace";
                case 126:
                    return "tilde";
                case 127:
                    return "<span class=\"acronym\">DEL</span>, <span class=\"means2\"><span class=\"ac2\">del</span>ete</span>";
                // the following are OCTAL because that's  how PostScript specifies them
                // &xxx; codes are for Latin-1
                case 0241:
                    return "PostScript " + "(&iexcl;) exclamdown";
                case 0242:
                    return "PostScript " + "(&cent;) cent";
                case 0243:
                    return "PostScript " + "(&pound;) sterling";
                case 0244:
                    return "PostScript " + "(/) fraction";
                case 0245:
                    return "PostScript " + "(&yen;) yen";
                case 0246:
                    return "PostScript " + "(&#131;) florin";
                case 0247:
                    return "PostScript " + "(&sect;) section";
                case 0250:
                    return "PostScript " + "(&curren;) currency";
                case 0251:
                    return "PostScript " + "(') quotesingle";
                case 0252:
                    return "PostScript " + "(&#147;) quotedblleft";
                case 0253:
                    return "PostScript " + "(&laquo;) guillemotleft";
                case 0254:
                    return "PostScript " + "(&lt;) guilsinglleft";
                case 0255:
                    return "PostScript " + "(&gt;) guilsinglright";
                case 0256:
                    return "PostScript " + "fi ligature";
                case 0257:
                    return "PostScript " + "fl ligature;";
                case 0261:
                    return "PostScript " + "(&#150;) endash";
                case 0262:
                    return "PostScript " + "(&#134;) dagger";
                case 0263:
                    return "PostScript " + "(&middot;) periodcentered";
                case 0266:
                    return "PostScript " + "(&para;) paragraph";
                case 0267:
                    return "PostScript " + "(&#149;) bullet";
                case 0270:
                    return "PostScript " + "(,) quotesinglbase";
                case 0271:
                    return "PostScript " + "(&#132;) quotedblbase";
                case 0272:
                    return "PostScript " + "(&#148;) quotedblright";
                case 0273:
                    return "PostScript " + "(&raquo;) guillemotright";
                case 0274:
                    return "PostScript " + "(&#133;) ellipsis";
                case 0275:
                    return "PostScript " + "(&#137;) perthousand";
                case 0277:
                    return "PostScript " + "(&iquest;) questiondown";
                case 0301:
                    return "PostScript " + "(`) grave";
                case 0302:
                    return "PostScript " + "(&acute;) acute";
                case 0303:
                    return "PostScript " + "(^) circumflex";
                case 0304:
                    return "PostScript " + "(~) tilde";
                case 0305:
                    return "PostScript " + "(&macr;) macron, overbar accent";
                case 0306:
                    return "PostScript "
                           + "(<sup>u</sup>) breve, flattened u-shaped accent";
                case 0307:
                    return "PostScript " + "(&#183;) dotaccent";
                case 0310:
                    return "PostScript " + "(&uml;) dieresis";
                case 0312:
                    return "PostScript " + "(&#176;) ring";
                case 0313:
                    return "PostScript " + "(&cedil;) cedilla";
                case 0315:
                    return "PostScript " + "(&#148;) hungarumlaut";
                case 0316:
                    return "PostScript " + "(,) ogonek, reverse comma";
                case 0317:
                    return "PostScript "
                           + "(<sup>v</sup>) caron, flattened v-shaped accent";
                case 0320:
                    return "PostScript " + "(&#151;) emdash";
                case 0341:
                    return "PostScript " + "(&AElig;) AE";
                case 0343:
                    return "PostScript " + "(&ordf;) ordfeminine";
                case 0350:
                    return "PostScript " + "(L/) Lslash, L with / overstrike";
                case 0351:
                    return "PostScript " + "(&Oslash;) Oslash";
                case 0352:
                    return "PostScript " + "(&#140;) OE";
                case 0353:
                    return "PostScript " + "(&ordm;) ordmasculine";
                case 0361:
                    return "PostScript " + "(&aelig;) ae";
                case 0365:
                    return "PostScript " + "(1) dotlessi, i without dot";
                case 0370:
                    return "PostScript " + "(l/) l with / overstrike";
                case 0371:
                    return "PostScript " + "(&oslash;) oslash";
                case 0372:
                    return "PostScript " + "(&#156;) oe";
                case 0373:
                    return "PostScript " + "(&szlig;) germandbls";
                default:
                    return "&nbsp;";
                }
            }
        }

    /**
     * Generate ascii table
     *
     * @param dir      target dir where generated file goes.
     * @param filename filename where to put the generated table
     */
    @SuppressWarnings( { "SameParameterValue" } )
    static void generateAsciitab( final File dir, final String filename )
        {
        try
            {
            FileWriter p = new FileWriter( new File( dir, filename ) );
            p.write( "<!-- cseignore -->\n" );
            p.write( "<!-- G E N E R A T E D -->\n" );
            p.write( "<table class=\"standard\" summary=\"ASCII and Latin-1 Character Table\">\n" );
            p.write(
                    "<colgroup><col align=\"center\" class=\"behold\"><col span=\"3\" align=\"right\"><col span=\"2\" align=\"left\"></colgroup>\n" );
            p.write(
                    "<thead><tr><th colspan=\"6\">ASCII and Latin-1 Character Table</th></tr>" );
            p.write(
                    "<tr><th>Char</th><th>Dec</th><th>Hex</th><th>Octal</th><th>HTML</th><th>Notes</th></tr></thead>\n<tbody>\n" );
            int everyTenthLine = 0;
            for ( char c = 0; c <= 0xff; c++ )
                {
                if ( everyTenthLine++ % 10 == 0 )
                    {
                    p.write( "<!-- G E N E R A T E D -->\n" );
                    }
                p.write( "<tr>" );
                /* displayable char */
                p.write( "<td class=\"behold\">" );
                p.write( asHTML( c ) );
                p.write( "</td>" );
                /* decimal */
                p.write( "<td class=\"decimal\">" );
                p.write( Integer.toString( c ) );
                p.write( "</td>" );
                /* hex */
                p.write( "<td class=\"hexentity\">" );
                p.write( "0x" + toLZ( c, 2, 16 ) );
                p.write( "</td>" );
                /* octal */
                p.write( "<td class=\"octal\">" );
                p.write( toLZ( c, 4, 8 ) );
                p.write( "</td>" );
                /* HTML entity */
                p.write( "<td class=\"entity\">" );
                p.write( asQuotedHTML( c ) );
                p.write( "</td>" );
                /* notes */
                p.write( "<td>" );
                p.write( descs( c ) );
                p.write( "</td>" );
                p.write( "</tr>\n" );
                }// end for
            p.write( "</tbody></table>" );
            p.write( "<!-- /cseignore -->\n" );
            p.close();
            }
        catch ( IOException e )
            {
            err.println( e.getMessage() );
            }
        }

    /**
     * Convert an integer to a String, with left zeroes.
     *
     * @param i    the integer to be converted
     * @param len  len the length of the resulting string
     * @param base base 10=decimal 16=hex 8=octal
     *
     * @return String representation of the int e.g. 007
     */
    private static String toLZ( int i, int len, int base )
        {
        // Since String is final, we could not add this method there.
        String s = Integer.toString( i, base );
        if ( s.length() > len )
            {
            return s.substring( 0, len );
            }
        else if ( s.length() < len )
            // pad on left with zeros
            {
            return "000000000000000000000000000".substring( 0,
                    len - s.length() )
                   + s;
            }
        else
            {
            return s;
            }
        }// end toLZ
    }

