import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;

/**
 * Compressed General Category and Numeric Value: Compressor
 * <p>
 * Warranty & Liability
 * To the extent permitted by applicable law and unless explicitly
 * otherwise agreed upon, XLOG Technologies AG makes no warranties
 * regarding the provided information. XLOG Technologies AG assumes
 * no liability that any problems might be solved with the information
 * provided by XLOG Technologies AG.
 * <p>
 * Rights & License
 * All industrial property rights regarding the information - copyright
 * and patent rights in particular - are the sole property of XLOG
 * Technologies AG. If the company was not the originator of some
 * excerpts, XLOG Technologies AG has at least obtained the right to
 * reproduce, change and translate the information.
 * <p>
 * Reproduction is restricted to the whole unaltered document. Reproduction
 * of the information is only allowed for non-commercial uses. Selling,
 * giving away or letting of the execution of the library is prohibited.
 * The library can be distributed as part of your applications and libraries
 * for execution provided this comment remains unchanged.
 * <p>
 * Restrictions
 * Only to be distributed with programs that add significant and primary
 * functionality to the library. Not to be distributed with additional
 * software intended to replace any components of the library.
 * <p>
 * Trademarks
 * Jekejeke is a registered trademark of XLOG Technologies AG.
 */
public class Compress {
    public static final int PARA_BITS = 4;
    public static final int PARA_SIZE = 1 << PARA_BITS;
    static final int MAX_PARA = (Character.MAX_CODE_POINT + 1) / PARA_SIZE;
    public static final int BLOCK_BITS = 6;
    public static final int BLOCK_SIZE = 1 << BLOCK_BITS;
    public static final int MAX_BLOCK = MAX_PARA / BLOCK_SIZE;

    public static final ArrayList<String> pool = new ArrayList<>();
    public static final ArrayList<String> pool2 = new ArrayList<>();
    public static final StringBuilder buf3 = new StringBuilder();

    /**
     * Compress the general category and numeric value.
     */
    public static void compress() {
        pool.clear();
        pool2.clear();
        StringBuilder buf = new StringBuilder();
        StringBuilder buf2 = new StringBuilder();
        buf3.setLength(0);
        for (int k = 0; k < MAX_BLOCK; k++) {
            buf2.setLength(0);
            for (int i = 0; i < BLOCK_SIZE; i++) {
                buf.setLength(0);
                for (int j = 0; j < PARA_SIZE; j++) {
                    int ch = (k * BLOCK_SIZE + i) * PARA_SIZE + j;
                    int type = Character.getType(ch);
                    int val = Character.digit(ch, 36);
                    buf.append((char) (((val + 1) << 5) + type));
                }
                String res = buf.toString();
                int index = register(pool, res);
                buf2.append((char) index);
            }
            String res = buf2.toString();
            int index = register(pool2, res);
            buf3.append((char) index);
        }
    }

    /**
     * Deduplicate an array of usigned 16 bit values.
     *
     * @param pool The pool.
     * @param key  The array.
     * @return The index.
     */
    private static int register(ArrayList<String> pool, String key) {
        int index = pool.indexOf(key);
        if (index == -1) {
            index = pool.size();
            pool.add(key);
        }
        return index;
    }

    /**
     * Print an array of unsigned 16 bit values.
     *
     * @param str The unsigned 16 bit values.
     */
    public static void show(String str, Writer wr) throws IOException {
        wr.write('[');
        for (int i = 0; i < str.length(); i++) {
            if (i != 0)
                wr.write(", ");
            if (i != 0 && i % 16 == 0) {
                wr.write("\n");
                wr.write("    ");
            }
            wr.write(Integer.toString(str.charAt(i)));
        }
        wr.write(']');
    }

    /**
     * <p>Retrieve the specification version.</p>
     *
     * @return The specification version.
     */
    public static double getVersion() {
        String vers = System.getProperties().getProperty("java.specification.version");
        return Double.parseDouble(vers);
    }

    /*
    public static void main(String[] args) {
        int ch = 8322;
        wr.writeln("digit("+ch+")="+Character.digit(ch, 36));
        wr.writeln("numeric("+ch+")="+Character.getNumericValue(ch));

        wr.writeln();

        ch = 9369;
        wr.writeln("digit("+ch+")="+Character.digit(ch, 36));
        wr.writeln("numeric("+ch+")="+Character.getNumericValue(ch));
    }
    */

}