package org.xbib.content.xml.util;

/**
 *
 */
public class ISO9075 {
    private static final int MASK = (1 << 4) - 1;

    private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private ISO9075() {
    }

    public static String encode(String toEncode) {
        if ((toEncode == null)
                || (toEncode.length() == 0)
                || (XML11Char.isXML11ValidName(toEncode) && (!toEncode.contains("_x")))) {
            return toEncode;
        } else {
            StringBuilder builder = new StringBuilder(toEncode.length());
            for (int i = 0; i < toEncode.length(); i++) {
                char c = toEncode.charAt(i);
                if (i == 0) {
                    if (XML11Char.isXML11NCNameStart(c)) {
                        if (matchesEncodedPattern(toEncode, i)) {
                            encode('_', builder);
                        } else {
                            builder.append(c);
                        }
                    } else {
                        encode(c, builder);
                    }
                } else if (!XML11Char.isXML11NCName(c)) {
                    encode(c, builder);
                } else {
                    if (matchesEncodedPattern(toEncode, i)) {
                        encode('_', builder);
                    } else {
                        builder.append(c);
                    }
                }
            }
            return builder.toString();
        }
    }

    private static boolean matchesEncodedPattern(String string, int position) {
        return (string.length() >= position + 6)
                && (string.charAt(position) == '_') && (string.charAt(position + 1) == 'x')
                && isHexChar(string.charAt(position + 2)) && isHexChar(string.charAt(position + 3))
                && isHexChar(string.charAt(position + 4)) && isHexChar(string.charAt(position + 5))
                && (string.charAt(position + 6) == '_');
    }

    private static boolean isHexChar(char c) {
        switch (c) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                return true;
            default:
                return false;
        }
    }

    public static String decode(String toDecode) {
        if ((toDecode == null) || (toDecode.length() < 7) || (!toDecode.contains("_x"))) {
            return toDecode;
        }
        StringBuilder decoded = new StringBuilder();
        for (int i = 0, l = toDecode.length(); i < l; i++) {
            if (matchesEncodedPattern(toDecode, i)) {
                decoded.append((char) Integer.parseInt(toDecode.substring(i + 2, i + 6), 16));
                i += 6;
            } else {
                decoded.append(toDecode.charAt(i));
            }
        }
        return decoded.toString();
    }

    private static void encode(char ch, StringBuilder builder) {
        char c = ch;
        char[] buf = new char[]{'_', 'x', '0', '0', '0', '0', '_'};
        int charPos = 6;
        do {
            buf[--charPos] = DIGITS[c & MASK];
            c >>>= 4;
        } while (c != 0);
        builder.append(buf);
    }
}
