/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tomas.ht_ydt.lib.common;

import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Scanner;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 *
 * @author nack_ki
 */
public class StringConvert {

    public static Character[] toCharacterArray(String s) {

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

        Character[] array = new Character[s.length()];
        for (int i = 0; i < s.length(); i++) {
            array[i] = new Character(s.charAt(i));
        }

        return array;
    }

    public static String Unicode2ASCII(String unicode) {
        StringBuilder ascii = new StringBuilder(unicode);
        int code;
        for (int i = 0; i < unicode.length(); i++) {
            code = (int) unicode.charAt(i);
            if ((0xE01 <= code) && (code <= 0xE5B)) {
                ascii.setCharAt(i, (char) (byte) (code - 0xD60));
            }
        }
        return ascii.toString();
    }

    public static String Unicode2ASCII2(String unicode) throws UnsupportedEncodingException {
        byte[] b = new byte[unicode.length()];
        for (int j = 0; j < unicode.length(); j++) {
            int code = (int) unicode.charAt(j);
            if ((0xE01 <= code) && (code <= 0xE5B)) {
                b[j] = (byte) (char) ((code - 0xD60) & 0xFF);
            } else {
                b[j] = (byte) code;
            }
        }
        return new String(b, "ISO8859_1");
    }

    public static String ASCII2Unicode(String ascii) {
        StringBuilder unicode = new StringBuilder(ascii);
        int code;
        for (int i = 0; i < ascii.length(); i++) {
            code = (int) ascii.charAt(i);
            if ((0xA1 <= code) && (code <= 0xFB)) {
                unicode.setCharAt(i, (char) (code + 0xD60));
            }
        }
        return unicode.toString();
    }

    /**
     * Convert ASCII(Hex) to Unicode
     *
     * @param ustr - input string
     * @param prefix - prefix of any characters e.g. %
     * @return
     */
    public static String HexString2Ascii(String ustr, final char prefix) {
        StringBuilder ascii = new StringBuilder("");
        StringBuilder chex = new StringBuilder("");
        int pos = 0;
        boolean hex = false;
        while (pos < ustr.length()) {
            if (ustr.charAt(pos) == (byte) prefix) {
                if (hex) {
                    chex.append(ustr.charAt(pos));
                }

                // switch to HEX mode
                hex = true;
            } else {
                if (hex) { // HEX
                    // append to chex
                    chex.append(ustr.charAt(pos));
                } else { // ASCII
                    ascii.append(ustr.charAt(pos));
                }

                if (hex && chex.length() == 2) {
                    // convert hex and append to buffer
                    ascii.append((char) Integer.parseInt(chex.toString(), 16));

                    hex = false; // switch to ASCII mode
                    chex = new StringBuilder("");
                }
            }

            pos++;
        }

        return ascii.toString();
    }

    public static String convertToThaiTIS620(String string_context) {
        //initial temporary attribute as String type
        String string_th = "";

        try {
            //convert a string by encoding to 'TIS620' Thai language code based on 'ISO8859_1' in java standard.
            string_th = new String(string_context.getBytes("ISO8859_1"), "TIS620");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }

        return string_th;
    }

    public static String Unicode2UnicodeString(String unicode) {
        return Unicode2UnicodeString(unicode, "");
    }

    public static String Unicode2UnicodeString(String unicode, String prefix) {
        String unicodeStr = "";
        String tmp;
        for (int i = 0; i < unicode.length(); i++) {
            tmp = charToHex(unicode.charAt(i));
            unicodeStr += String.format("%s%s%s%s", prefix, tmp.substring(0, 2), prefix, tmp.substring(2));
            //unicodeStr += String.format("%04X", unicode.charAt(i));
        }
        return unicodeStr;
    }

    public static String UnicodeString2Unicode(String ustr) {
        int unicodelen = ustr.length() / 4;
        StringWriter unicode = new StringWriter();
        for (int i = 0; i < unicodelen; i++) {
            String c = ustr.substring(i * 4, (i * 4) + 4);
            unicode.append((char) Integer.parseInt(c, 16));
        }
        return unicode.toString();
    }

    public static String ASCII2UnicodeString(String ascii) {
        return Unicode2UnicodeString(ASCII2Unicode(ascii));
    }

    public static String UnicodeString2ASCII(String ustr) {
        return Unicode2ASCII(UnicodeString2Unicode(ustr));
    }

    public static String byteToHex(byte b) {
        // Returns hex String representation of 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) & 0x0f], hexDigit[b & 0x0f]};
        //return new String(array);
        return String.format("%01X%01X", (b >> 4) & 0x0F, b & 0x0F);
    }

    public static String charToHex(char c) {
        // Returns hex String representation of char c
        byte hi = (byte) (c >>> 8);
        byte lo = (byte) (c & 0xff);
        return byteToHex(hi) + byteToHex(lo);
    }

    public static String UTF2HexString(String str) {
        return Any2HexString(str, "UTF8", "");
    }

    public static String Any2HexString(String source, String charset, String prefix) {
        String ret = new String();
        //byte []binAttr=str.getBytes();
        try {
            byte[] binAttr = source.getBytes(charset);
            for (int i = 0; i < binAttr.length; i++) {
                ret += prefix + byteToHex((byte) ((char) binAttr[i] & 0xff));
            }

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

        ret = ret.replaceAll("253236", "26"); // replace "253236"(%26)[&] with "26"
        ret = ret.replaceAll("253246", "2F"); // replace "253246"(%2F)[/] with "2F"
        ret = ret.replaceAll("253346", "3F"); // replace "253346"(%3F)[?] with "3F"
        ret = ret.replaceAll("253344", "3D"); // replace "253344"(%3D)[=] with "3D"

        return ret;
    }

    public static String TruemoveUnicode2Unicode(String str) {
        // input N&#xe2d;&#xe34;&#xe08;&#xe09;&#xe32;&#xe04;&#xe38;&#xe13;&#xe18;&#xe35;&#xe23;&#xe30;&#xe08;&#xe31;&#xe07;&#xe40;&#xe25;&#xe22;..

        // first, convert truemove unicode to UnicodeString 0030003100320E320E04
        String tmp = "";
        int len = str.length();
        int pos = 0;
        while (pos < len) {
            switch (str.charAt(pos)) {
                case '&':
                    //System.out.println(String.format("%d[%d], %c%c", pos, len, (int)str.charAt(pos+1), (int)str.charAt(pos+2)));
                    if (pos + 2 < len && str.charAt(pos + 1) == '#' && str.charAt(pos + 2) == 'x') {
                        pos += 3; // skip &#x
                        tmp += "0"; // replace &#x with '0'
                        while (pos < len) {
                            if (str.charAt(pos) == ';') {
                                pos++; // skip ;
                                break;
                            }
                            tmp += String.format("%C", str.charAt(pos));
                            pos++;
                        }
                    } else {
                        tmp += String.format("00%01X", (int) str.charAt(pos));
                        //System.out.println(tmp);
                        pos++;
                    }

                    break;
                default:
                    tmp += String.format("00%01X", (int) str.charAt(pos));
                    //System.out.println(tmp);
                    pos++;
            }
        }

        //System.out.println();
        //System.out.println(tmp);

        // finally, convert UnicodeString to Unicode
        return UnicodeString2Unicode(tmp);
    }

    public static boolean isEnglishText(String txt) {
        boolean ret = true;
        int code;
        for (int i = 0; i < txt.length(); i++) {
            code = (int) txt.charAt(i);
            if ((0xE01 <= code) && (code <= 0xE5B)) {
                ret = false;
                break;
            }
        }

        return ret;
    }

    public static int getEncodingType(String txt) {
        int e = 1; // en
        int c;
        for (int i = 0; i < txt.length(); i++) {
            c = (int) txt.charAt(i);
            if ((0xE01 <= c) && (c <= 0xE5B)) {
                e |= 2;// thai
            } else if (c>0x7F) {
                e |= 4;// other
            }
        }

        return e;
    }

    public static boolean isDigit(String txt) {
        boolean ret = true;
        
        if (txt == null || txt.isEmpty()) {
            return false;
        }

        for (int i = 0; i < txt.length(); i++) {
            if (!Character.isDigit(txt.charAt(i))) {
                ret = false;
                break;
            }
        }

        return ret;
    }

    /**
     * This method ensures that the output String has only valid XML unicode
     * characters as specified by the XML 1.0 standard. For reference, please
     * see <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
     * standard</a>. This method will return an empty String if the input is
     * null or empty.
     *
     * @param in The String whose non-valid characters we want to remove.
     * @return The in String, stripped of non-valid characters.
     */
    public static String stripNonValidXMLCharacters(String in) {
        StringBuilder out = new StringBuilder(); 
        char current; // Used to reference the current character.

        if (in == null || ("".equals(in))) {
            return ""; // vacancy test.
        }
        for (int i = 0; i < in.length(); i++) {
            current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
            if (current == '&') {
                out.append("&amp;"); // we change & -> &amp; for especially XML unmarshaller, after that it should be rollback
            } else if ((current == 0x9)
                    || (current == 0xA)
                    || (current == 0xD)
                    || ((current >= 0x20) && (current <= 0xD7FF))
                    || ((current >= 0xE000) && (current <= 0xFFFD))
                    || ((current >= 0x10000) && (current <= 0x10FFFF))) {
                out.append(current);
            }
        }
        return out.toString();
    }

    public static String replace(String old, String regex, String replacement, boolean all) {
        String ret;

        if (old.indexOf(regex) == -1) {
            return old;
        }

        if (all) {
            ret = old.replaceAll(regex, replacement);
        } else {
            ret = old.replaceFirst(regex, replacement);
        }

        return ret;
    }

    public static String removeNewline(String input) {
        String tmp = "";
        Scanner s = new Scanner(input).useDelimiter("\\s*[\\r\\n|\\n]\\s*");
        int count = 0;
        while (s.hasNext()) {
            tmp += s.next();
        }
        s.close();

        return tmp;
    }

    /**
     * From a msg, a number of iterations and a salt, returns the corresponding
     * digest
     *
     * @param iterationNb int The number of iterations of the algorithm
     * @param msg String The password to encrypt
     * @param salt byte[] The salt
     * @return byte[] The digested password
     * @throws NoSuchAlgorithmException If the algorithm doesn't exist
     * @throws UnsupportedEncodingException
     */
    public static byte[] hash(int iterationNb, String msg, byte[] salt) throws NoSuchAlgorithmException, 
            UnsupportedEncodingException {
        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        digest.reset();
        digest.update(salt);
        byte[] input = digest.digest(msg.getBytes("UTF-8"));
        for (int i = 0; i < iterationNb; i++) {
            digest.reset();
            input = digest.digest(input);
        }
        return input;
    }

    public static byte[] encrypt(String message, String secret) throws Exception {
        final MessageDigest md = MessageDigest.getInstance("md5");
        final byte[] digestOfPassword = md.digest(secret.getBytes("utf-8"));
        final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8;) {
            keyBytes[k++] = keyBytes[j++];
        }

        final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);

        final byte[] plainTextBytes = message.getBytes("utf-8");
        final byte[] cipherText = cipher.doFinal(plainTextBytes);
        // final String encodedCipherText = new sun.misc.BASE64Encoder()
        // .encode(cipherText);

        return cipherText;
    }
    
    public static String decrypt(byte[] message, String secret) throws Exception {
        final MessageDigest md = MessageDigest.getInstance("md5");
        final byte[] digestOfPassword = md.digest(secret.getBytes("utf-8"));
        final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8;) {
            keyBytes[k++] = keyBytes[j++];
        }

        final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        decipher.init(Cipher.DECRYPT_MODE, key, iv);

        // final byte[] encData = new
        // sun.misc.BASE64Decoder().decodeBuffer(message);
        final byte[] plainText = decipher.doFinal(message);

        return new String(plainText, "UTF-8");
    }

    public static final int computeHash(String input)
            throws NoSuchAlgorithmException {
        if (input == null) {
            throw new IllegalArgumentException("Input cannot be null!");
        }

        int hash = 0;
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] raw = md.digest(input.getBytes());

        for (int i = 0; i < raw.length; i++) {
            int c = raw[i];
            if (c < 0) {
                c = Math.abs(c) - 1 ^ 0xFF;
            }
            hash += c;
        }

        return hash;
    }

    public static String randomHex(int length) {
        SecureRandom random = new SecureRandom();
        return new BigInteger(130, random).toString(length);
    }
}
