/*
 * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package build.tools.generatecharacter;

import java.util.regex.*;
import java.util.*;
import java.io.*;

public class CharacterScript {

    // generate the code needed for j.l.C.UnicodeScript
    static void fortest(String fmt, Object... o) {
        //System.out.printf(fmt, o);
    }

    static void print(String fmt, Object... o) {
        System.out.printf(fmt, o);
    }

    static void debug(String fmt, Object... o) {
        //System.out.printf(fmt, o);
    }

    public static void main(String args[]){
        try {
            if (args.length != 1) {
                System.out.println("java CharacterScript script.txt out");
                System.exit(1);
            }

            int i, j;
            BufferedReader sbfr = new BufferedReader(new FileReader(args[0]));
            HashMap<String,Integer> scriptMap = new HashMap<String,Integer>();
            String line = null;

            Matcher m = Pattern.compile("(\\p{XDigit}+)(?:\\.{2}(\\p{XDigit}+))?\\s+;\\s+(\\w+)\\s+#.*").matcher("");

            int prevS = -1;
            int prevE = -1;
            String prevN = null;
            int[][] scripts = new int[1024][3];
            int scriptSize = 0;

            while ((line = sbfr.readLine()) != null) {
                if (line.length() <= 1 || line.charAt(0) == '#') {
                    continue;
                }
                m.reset(line);
                if (m.matches()) {
                    int start = Integer.parseInt(m.group(1), 16);
                    int end = (m.group(2)==null)?start
                              :Integer.parseInt(m.group(2), 16);
                    String name = m.group(3);
                    if (name.equals(prevN) && start == prevE + 1) {
                        prevE = end;
                    } else {
                        if (prevS != -1) {
                            if (scriptMap.get(prevN) == null) {
                                scriptMap.put(prevN, scriptMap.size());
                            }
                            scripts[scriptSize][0] = prevS;
                            scripts[scriptSize][1] = prevE;
                            scripts[scriptSize][2] = scriptMap.get(prevN);
                            scriptSize++;
                        }
                        debug("%x-%x\t%s%n", prevS, prevE, prevN);
                        prevS = start; prevE = end; prevN = name;
                    }
                } else {
                    debug("Warning: Unrecognized line <%s>%n", line);
                }
            }

            //last one.
            if (scriptMap.get(prevN) == null) {
                scriptMap.put(prevN, scriptMap.size());
            }
            scripts[scriptSize][0] = prevS;
            scripts[scriptSize][1] = prevE;
            scripts[scriptSize][2] = scriptMap.get(prevN);
            scriptSize++;

            debug("%x-%x\t%s%n", prevS, prevE, prevN);
            debug("-----------------%n");
            debug("Total scripts=%s%n", scriptMap.size());
            debug("-----------------%n%n");

            String[] names = new String[scriptMap.size()];
            for (String name: scriptMap.keySet()) {
                names[scriptMap.get(name).intValue()] = name;
            }

            for (j = 0; j < scriptSize; j++) {
                for (int cp = scripts[j][0]; cp <= scripts[j][1]; cp++) {
                    String name = names[scripts[j][2]].toUpperCase(Locale.ENGLISH);
                    if (cp > 0xffff)
                        System.out.printf("%05X    %s%n", cp, name);
                    else
                        System.out.printf("%05X    %s%n", cp, name);
                }
            }

            Arrays.sort(scripts, 0, scriptSize,
                        new Comparator<int[]>() {
                            public int compare(int[] a1, int[] a2) {
                                return a1[0] - a2[0];
                            }
                            public boolean compare(Object obj) {
                                return obj == this;
                            }
                         });



            // Consolidation: there are lots of "reserved" code points
            // embedded in those otherwise "sequential" blocks.
            // To make the lookup table smaller, we combine those
            // separated segments with the assumption that the lookup
            // implementation checks
            //    Character.getType() !=  Character.UNASSIGNED
            // first (return UNKNOWN for unassigned)

            ArrayList<int[]> list = new ArrayList<>();
            list.add(scripts[0]);

            int[] last = scripts[0];
            for (i = 1; i < scriptSize; i++) {
                if (scripts[i][0] != (last[1] + 1)) {

                    boolean isNotUnassigned = false;
                    for (int cp = last[1] + 1; cp < scripts[i][0]; cp++) {
                        if (Character.getType(cp) != Character.UNASSIGNED) {
                            isNotUnassigned = true;
                            debug("Warning: [%x] is ASSIGNED but in NON script%n", cp);
                            break;
                        }
                    }
                    if (isNotUnassigned) {
                        // surrogates only?
                        int[] a = new int[3];
                        a[0] = last[1] + 1;
                        a[1] = scripts[i][0] - 1;
                        a[2] = -1;  // unknown
                        list.add(a);
                    } else {
                        if (last[2] == scripts[i][2]) {
                            //combine
                            last[1] = scripts[i][1];
                            continue;
                        } else {
                            // expand last
                            last[1] = scripts[i][0] - 1;
                        }
                    }
                }
                list.add(scripts[i]);
                last = scripts[i];
            }

            for (i = 0; i < list.size(); i++) {
                int[] a = list.get(i);
                String name = "UNKNOWN";
                if (a[2] != -1)
                    name = names[a[2]].toUpperCase(Locale.US);
                debug("0x%05x, 0x%05x  %s%n", a[0], a[1], name);
            }
            debug("--->total=%d%n", list.size());


            //////////////////OUTPUT//////////////////////////////////
            print("public class Scripts {%n%n");
            print("    public static enum UnicodeScript {%n");
            for (i = 0; i < names.length; i++) {
                print("        /**%n         * Unicode script \"%s\".%n         */%n", names[i]);
                print("        %s,%n%n",  names[i].toUpperCase(Locale.US));
            }
            print("        /**%n         * Unicode script \"Unknown\".%n         */%n        UNKNOWN;%n%n");


            // lookup table
            print("        private static final int[] scriptStarts = {%n");
            for (int[] a : list) {
                String name = "UNKNOWN";
                if (a[2] != -1)
                    name = names[a[2]].toUpperCase(Locale.US);
                if (a[0] < 0x10000)
                    print("            0x%04X,   // %04X..%04X; %s%n",
                          a[0], a[0], a[1], name);
                else
                    print("            0x%05X,  // %05X..%05X; %s%n",
                          a[0], a[0], a[1], name);
            }
            last = list.get(list.size() -1);
            if (last[1] != Character.MAX_CODE_POINT)
                print("            0x%05X   // %05X..%06X; %s%n",
                      last[1] + 1, last[1] + 1, Character.MAX_CODE_POINT,
                      "UNKNOWN");
            print("%n        };%n%n");

            print("        private static final UnicodeScript[] scripts = {%n");
            for (int[] a : list) {
                String name = "UNKNOWN";
                if (a[2] != -1)
                    name = names[a[2]].toUpperCase(Locale.US);
                print("            %s,%n", name);
            }

            if (last[1] != Character.MAX_CODE_POINT)
                print("            UNKNOWN%n");
            print("        };%n");
            print("    }%n");
            print("}%n");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
