package com.tomastc.anickd.lib.common;

import org.json.JSONArray;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtil {

    public static String[] toStringArray(JSONArray array) {
        if(array==null)
            return new String[0];

        String[] arr=new String[array.length()];
        for(int i=0; i<arr.length; i++) {
            arr[i]=array.optString(i);
        }
        return arr;
    }

    public static long hash(String string) {
        long h = 1125899906842597L; // prime
        int len = string.length();

        for (int i = 0; i < len; i++) {
            h = 31 * h + string.charAt(i);
        }
        return h;
    }

    public static String ASCII2Unicode(String txt) {
        String buff = new String();

        buff = "";
        for (int i = 0; i < txt.length(); i++) {
            byte hi = (byte) (txt.charAt(i) >>> '\b');
            byte lo = (byte) (txt.charAt(i) & 0xFF);
            int c = hi << 8 | lo;
            buff = String.format("%s%c", new Object[]{buff, Integer.valueOf(c)});
        }
        return buff;
    }

    public static String ASCII2UnicodeHexString(String txt) {
        String buff = new String();

        for (int i = 0; i < txt.length(); i++) {
            int code = txt.charAt(i);
            if (161 <= code) {
                String cstr = ASCII2HexString(txt.substring(i, i + 1));
                cstr = "%" + cstr.substring(0, 2) + "%" + cstr.substring(2, 4) + "%" + cstr.substring(4, 6);
                buff = String.format("%s%s", new Object[]{buff, cstr});
            } else {
                buff = String.format("%s%c", new Object[]{buff, Character.valueOf(txt.charAt(i))});
            }
        }
        return buff;
    }

    public static String byteToHex(byte b) {
        char[] hexDigit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        char[] array = {hexDigit[(b >> 4 & 0xF)], hexDigit[(b & 0xF)]};

        return String.format("%01X%01X", new Object[]{Integer.valueOf(b >> 4 & 0xF), Integer.valueOf(b & 0xF)});
    }

    public static String ASCII2HexString(String str) {
        String ret = new String();
        try {
            byte[] binAttr = str.getBytes("UTF8");
            for (int i = 0; i < binAttr.length; i++) {
                ret = ret + byteToHex((byte) ((char) binAttr[i] & 0xFF));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        ret = ret.replaceAll("253236", "26");
        ret = ret.replaceAll("253246", "2F");
        ret = ret.replaceAll("253346", "3F");
        ret = ret.replaceAll("253344", "3D");

        return ret;
    }

    public static int lineCount(StringBuffer buff) {
        int pos = 0;
        int line = 1;
        while (pos < buff.toString().length()) {
            try {
                if (buff.toString().charAt(pos) == '\r') {
                    if (buff.toString().charAt(pos) == '\n') {
                        line++;
                        pos++;
                    }
                } else if (buff.toString().charAt(pos) == '\n') {
                    line++;
                }
            } catch (StringIndexOutOfBoundsException e) {
            }
            pos++;
        }

        return line;
    }

    public static String getFileContent(String filename) throws Exception {
        String content = "";
        try {
            StringBuilder text = new StringBuilder();
            String NL = System.getProperty("line.separator");
            Scanner scanner = new Scanner(new File(filename), "UTF-8");
            try {
                while (scanner.hasNextLine()) {
                    text.append(scanner.nextLine() + NL);
                }
            } finally {
                scanner.close();
            }
        } catch (Exception e) {
            throw e;
        }

        return content;
    }

    public static String grep(String input, String regexp) {
        StringBuffer buff = new StringBuffer();
        try {
            BufferedReader reader = new BufferedReader(new StringReader(input));
            try {
                String str;
                while ((str = reader.readLine()) != null) {
                    if (str.matches(regexp)) {
                        buff.append(str).append("\r\n");
                    }
                }
            } catch (IOException e) {
                throw e;
            }
        } catch (Exception e) {
            return null;
        }

        return buff.toString();
    }

    public static String vgrep(String input, String regexp) {
        StringBuffer buff = new StringBuffer();
        try {
            BufferedReader reader = new BufferedReader(new StringReader(input));
            try {
                String str;
                while ((str = reader.readLine()) != null) {
                    if (!str.matches(regexp)) {
                        buff.append(str).append("\r\n");
                    }
                }
            } catch (IOException e) {
                throw e;
            }
        } catch (Exception e) {
            return null;
        }

        return buff.toString();
    }

    public static String[] doInlineScanner(String input, String patternStr) {
        List list = new ArrayList();
        try {
            String line = "";
            BufferedReader reader = new BufferedReader(new StringReader(input));
            try {
                while ((line = reader.readLine()) != null) {
                    Scanner s = new Scanner(line);
                    Pattern pattern = Pattern.compile(patternStr, 2);
                    s.findInLine(pattern);
                    try {
                        MatchResult result = s.match();
                        for (int i = 1; i <= result.groupCount(); i++) {
                            list.add(result.group(i));
                        }
                    } catch (IllegalStateException e) {
                    }

                    s.close();
                }
            } catch (IOException e) {
                throw e;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return (String[]) list.toArray(new String[0]);
    }

    public static String[] doScanner(String input, String patternStr) {
        //String line = "12251-59B -0040   (0043)";
        //String pat = "(\\d{5})-([\\d\\w]{3})\\s-([\\d\\w]{4})\\s+\\(([\\w\\d\\-]{4,})\\)";
        List<String> arry = new ArrayList<>();
        Pattern p = Pattern.compile(patternStr);
        Matcher m = p.matcher(input);
        if(m.matches()) {
            for (int i=0;i<m.groupCount();i++) {
                System.out.println(String.format("Match %d: %s", i, m.group(i+1)));
                arry.add(m.group(i+1));
            }
        }

        return (String[]) arry.toArray(new String[0]);
    }

    public static String getMd5Sum(String filename) {
        String md5sum = null;
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            File f = new File(filename);
            InputStream is = new FileInputStream(f);
            byte[] buffer = new byte[8192];
            int read = 0;
            try {
                while ((read = is.read(buffer)) > 0) {
                    digest.update(buffer, 0, read);
                }
                byte[] bytes = digest.digest();
                BigInteger bigInt = new BigInteger(1, bytes);
                md5sum = bigInt.toString(16);
            } catch (IOException e) {
                throw new RuntimeException("Unable to process file for MD5", e);
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    throw new RuntimeException("Unable to close input stream for MD5 calculation", e);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return md5sum;
    }

    public static boolean compare(String a, String b) {
        boolean ret = false;
        ret = ((a == null) && (b == null)) || ((a != null) && (a.equals(b))) || ((b != null) && (b.equals(a)));

        return ret;
    }

    public static String repeatChar(char c, int n) {
        if (n < 0) {
            return "";
        }
        char[] chars = new char[n];
        Arrays.fill(chars, c);
        return new String(chars);
    }

    public static void dump(String input) {
        int char_per_line = 16;
        int line_size = 50;
        byte[] b = input.getBytes();

        StringBuffer buffer = new StringBuffer();
        StringBuffer hex = new StringBuffer();
        StringBuffer ascii = new StringBuffer();

        buffer.append(repeatChar('*', 42)).append(" /Dump ").append(repeatChar('*', 42)).append('\n');
        int i = 0;
        for (byte bb : b) {
            if (i % 16 == 0) {
                hex.setLength(0);
                ascii.setLength(0);
            }

            hex.append(String.format("%02X ", new Object[]{Byte.valueOf(bb)}));
            ascii.append(String.format("%c ", new Object[]{Integer.valueOf((bb > 32) && (bb < 151) ? bb : 46)}));

            if (((i + 1) % 8 == 0) && ((i + 1) % 16 > 0)) {
                hex.append("  ");
                ascii.append("  ");
            }

            if (((i + 1) % 16 == 0) || (i + 1 == b.length)) {
                buffer.append(hex);
                buffer.append(repeatChar('.', 50 - hex.length()));
                buffer.append("\t");
                buffer.append(ascii).append('\n');
            }

            i++;
        }
        buffer.append(repeatChar('*', 42)).append(" /End  ").append(repeatChar('*', 42)).append('\n');
        System.out.println(buffer);
    }

    public static int byteArrayToInt(byte[] b, int offset) {
        int value = 0;
        for (int i = 0; i < 4; i++) {
            int shift = (3 - i) * 8;
            value += ((b[(i + offset)] & 0xFF) << shift);
        }
        return value;
    }

    public static String byteToStr(byte[] byte_arr) {
        StringBuffer internal_buffer = new StringBuffer();

        for (int index = 0; index < byte_arr.length - 1; index++) {
            internal_buffer.append(String.valueOf(byte_arr[index]) + ".");
        }

        internal_buffer.append(String.valueOf(byte_arr.length));

        return internal_buffer.toString();
    }

    private static String fillMessageVariables(Map<String, String> map, String message, ANNOTATE annotate) {
        String buff = "";

        if (message == null) {
            return message;
        }

        if (message.indexOf(annotate.toString()) < 0) {
            return message;
        }
        try {
            String tmp = "";

            int pos = 0;
            int spos = 0;
            int epos = 0;
            int len = message.length();

            while ((pos < len) && (message.indexOf(annotate.toString(), pos) >= 0)) {
                spos = message.indexOf(annotate.toString(), pos);
                epos = message.indexOf(ANNOTATE.GENERAL_ANNOTATE_END.toString(), spos);

                buff = buff + message.substring(pos, spos);

                tmp = message.substring(spos, epos + ANNOTATE.GENERAL_ANNOTATE_END.toString().length());

                Scanner sc = new Scanner(tmp);
                try {
                    sc.findInLine("(\\s*[\\w\\.]+\\s*)");
                    MatchResult token = sc.match();
                    String key = token.group(1).trim();

                    String value = (String) map.get(key);
                    if (value != null) {
                        buff = buff + value;
                    } else {
                        if (tmp.indexOf(annotate.toString(), annotate.toString().length()) >= 0) {
                            tmp = annotate.toString() + fillMessageVariables(map, tmp.substring(annotate.toString().length()));
                        }
                        buff = buff + tmp;
                    }
                } catch (IllegalStateException e) {
                    buff = buff + tmp;
                } finally {
                    sc.close();
                }

                pos = epos + 1;
            }

            buff = buff + message.substring(pos);
        } catch (Exception e) {
            e.printStackTrace();
            buff = message;
        }

        return buff;
    }

    public static String fillMessageVariables(Map<String, String> map, String message) {
        return fillMessageVariables(map, message, ANNOTATE.GENERAL_ANNOTATE_START);
    }

    private static int findEndTagPosition(String msg, ANNOTATE annotate, int pos) {
        int open_count = 0;
        int len = msg.length();
        try {
            while (pos < len) {
                String tmp = msg.substring(pos);
                if (tmp.startsWith(annotate.toString())) {
                    open_count++;
                } else if (tmp.startsWith(ANNOTATE.GENERAL_ANNOTATE_END.toString())) {
                    open_count--;
                    if (open_count <= 0) {
                        return pos;
                    }
                }

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

        return pos;
    }

    public static enum ANNOTATE {

        GENERAL_ANNOTATE_START(0, "${"),
        SQL_ANNOTATE_START(1, "$SQL{"),
        GENERAL_ANNOTATE_END(2, "}");
        int id;
        String sym;

        private ANNOTATE(int id, String sym) {
            this.id = id;
            this.sym = sym;
        }

        public String toString() {
            return this.sym;
        }

        public int getId() {
            return this.id;
        }
    }

    public static List splitText(String s, String d, String encap) {
        boolean isEncapOpen = false;
        boolean isWordStarted = false;
        List<String> list = new ArrayList<String>();
        StringBuilder tmp = new StringBuilder();

        char type; // 0: normal, 1:eow, 2:soe

        for (int i = 0; i < s.length(); i++) {
            String a = s.substring(i, i + 1);
            if (a.equals(encap)) {
                if (!isEncapOpen && !isWordStarted) {
                    type = 2; // start encap
                } else if (isEncapOpen) {
                    type = 1; // end of encapsulate
                } else {
                    type = 0;
                }
            } else if (a.equals(d) && !isEncapOpen) { // end of token
                type = 1;
            } else {
                type = 0;
            }

            switch (type) {
                case 0:
                    tmp.append(a);
                    isWordStarted = true;
                    break;
                case 1:
                    if (!tmp.toString().trim().isEmpty()) {
                        list.add(tmp.toString().trim());
                    }
                    tmp = new StringBuilder();
                    isWordStarted = false;
                    isEncapOpen = false;
                    break;
                case 2:
                    isEncapOpen = true;
                    break;
            }
        }

        if (!tmp.toString().trim().isEmpty()) {
            list.add(tmp.toString().trim());
        }

        return list;
    }
}
