package one.nio.util;

// I know there are standard encoders, but this one is way faster
public final class URLEncoder {
    private static final String SAFE_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!*'()";
    private static final boolean[] IS_SAFE_CHAR = new boolean[128];

    static {
        int length = SAFE_CHARS.length();
        for (int i = 0; i < length; i++) {
            IS_SAFE_CHAR[SAFE_CHARS.charAt(i)] = true;
        }
    }

    public static String encode(String url) {
        int length = url.length();
        int specialChars = 0;
        for (int i = 0; i < length; i++) {
            char c = url.charAt(i);
            if (c >= 128) {
                specialChars += c <= 0xfff ? 5 : 8;
            } else if (!IS_SAFE_CHAR[c]) {
                specialChars += 2;
            }
        }

        if (specialChars == 0) {
            return url;
        }

        char[] chars = new char[length + specialChars];
        int p = 0;

        for (int i = 0; i < length; i++) {
            char c = url.charAt(i);
            if (c >= 128) {
                if (c <= 0xfff) {
                    encodeByte(chars, p,     0xc0 | ((c >>> 6) & 0x1f));
                    encodeByte(chars, p + 3, 0x80 | (c & 0x3f));
                    p += 6;
                } else {
                    encodeByte(chars, p,     0xe0 | ((c >>> 12) & 0x0f));
                    encodeByte(chars, p + 3, 0x80 | ((c >>> 6) & 0x3f));
                    encodeByte(chars, p + 6, 0x80 | (c & 0x3f));
                    p += 9;
                }
            } else if (!IS_SAFE_CHAR[c]) {
                encodeByte(chars, p, c);
                p += 3;
            } else {
                chars[p++] = c;
            }
        }

        return new String(chars);
    }

    public static String decode(String url) {
        int i = url.indexOf('%');
        int j = url.indexOf('+');
        if (i < 0 && j < 0) {
            return url;
        }

        char[] chars = url.toCharArray();

        while (j >= 0) {
            chars[j] = ' ';
            j = url.indexOf('+', j + 1);
        }

        if (i < 0) {
            return new String(chars);
        }

        int p = i;

        while (i + 3 <= chars.length) {
            int c1 = decodeByte(chars, i);
            if (c1 <= 0x7f) {
                chars[p] = (char) c1;
                i += 3;
            } else if ((c1 & 0xe0) == 0xc0 && i + 6 <= chars.length && chars[i + 3] == '%') {
                int c2 = decodeByte(chars, i + 3);
                chars[p] = (char) ((c1 & 0x1f) << 6 | (c2 & 0x3f));
                i += 6;
            } else if (i + 9 <= chars.length && chars[i + 3] == '%' && chars[i + 6] == '%') {
                int c2 = decodeByte(chars, i + 3);
                int c3 = decodeByte(chars, i + 6);
                chars[p] = (char) ((c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | (c3 & 0x3f));
                i += 9;
            } else {
                chars[p] = '%';
                i++;
            }

            p++;
            while (i < chars.length && chars[i] != '%') {
                chars[p++] = chars[i++];
            }
        }

        return new String(chars, 0, p);
    }

    private static void encodeByte(char[] chars, int p, int value) {
        chars[p] = '%';
        chars[p + 1] = Hex.CAPITAL[value >>> 4];
        chars[p + 2] = Hex.CAPITAL[value & 15];
    }

    private static int decodeByte(char[] chars, int p) {
        return Hex.DIGIT_VALUE[chars[p + 1]] << 4 | Hex.DIGIT_VALUE[chars[p + 2]];
    }
}

@@ -2195,32 +2195,50 @@
 rs);%0A        %7D%0A%0A
+        try %7B%0A    
         int p = 
@@ -2241,16 +2241,20 @@
 p = i;%0A%0A
+    
         
@@ -2293,24 +2293,28 @@
             
+ 
+   
 int c1 = dec
@@ -2328,24 +2328,28 @@
 (chars, i);%0A
+    
             
@@ -2374,32 +2374,36 @@
                 
+    
 chars%5Bp%5D = (char
@@ -2428,16 +2428,20 @@
     
+    
 i += 3;%0A
     
@@ -2432,24 +2432,28 @@
     i += 3;%0A
+    
             
@@ -2542,32 +2542,36 @@
                 
+   
+ 
 int c2 = decodeB
@@ -2581,32 +2581,36 @@
 (chars, i + 3);%0A
+    
                 
@@ -2681,16 +2681,20 @@
     
+  
+  
 i += 6;%0A
     
@@ -2685,24 +2685,28 @@
     i += 6;%0A
+    
             
@@ -2795,32 +2795,36 @@
                 
+ 
+   
 int c2 = decodeB
@@ -2834,32 +2834,36 @@
 (chars, i + 3);%0A
+    
                 
@@ -2905,32 +2905,36 @@
                 
+ 
+   
 chars%5Bp%5D = (char
@@ -2981,32 +2981,36 @@
 %7C (c3 & 0x3f));%0A
+    
                 
@@ -3021,32 +3021,36 @@
  9;%0A            
+    
 %7D else %7B%0A       
@@ -3034,32 +3034,36 @@
        %7D else %7B%0A
+    
                 
@@ -3094,16 +3094,20 @@
         
+    
 i++;%0A   
@@ -3107,35 +3107,43 @@
 ++;%0A            
+  
+  
 %7D%0A%0A
+    
             p++;
@@ -3139,24 +3139,28 @@
        p++;%0A
+    
             
@@ -3213,32 +3213,36 @@
                 
+    
 chars%5Bp++%5D = cha
@@ -3258,34 +3258,41 @@
             
+ 
-%7D%0A
+   %7D%0A   
         
 %7D%0A%0A        r
@@ -3271,35 +3271,40 @@
    %7D%0A           
+ 
 %7D%0A%0A
+    
         return n
@@ -3327,16 +3327,136 @@
  0, p);%0A
+        %7D catch (RuntimeException e) %7B%0A            throw new IllegalArgumentException(%22Invalid URL: %22 + url);%0A        %7D%0A
     %7D%0A%0A 

Handle invalid URLs