package net.proteanit.util;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import java.util.Random;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Description of the Class
 *
 * @author CEHJ
 * @created 10 March 2004
 */
public class StringUtils {
    
    public static final String HEXCHARS = "0123456789abcdef";
    /**
     * The main program for the StringUtils class
     *
     * @param args
     *          The command line arguments
     */
    public static void main(String[] args) throws Exception {
        // System.out.println(randomIso88591String(Integer.parseInt(args[0])));
        // System.out.println(toUnicodeEscaped((char)Integer.parseInt(args[0],
        // 16)));
        System.out.println(StringUtils.toHexString(Byte.parseByte(args[0])));
        System.out.println(StringUtils.toHexString(Short.parseShort(args[0])));
        System.out.println(StringUtils.toHexString(Integer.parseInt(args[0])));
        System.out.println(StringUtils.toHexString(Long.parseLong(args[0])));

        System.out.println(StringUtils.toBinaryString(Byte.parseByte(args[0])));
        System.out.println(StringUtils.toBinaryString(Short.parseShort(args[0])));
        System.out.println(StringUtils.toBinaryString(Integer.parseInt(args[0])));
        System.out.println(StringUtils.toBinaryString(Long.parseLong(args[0])));

    }

    /**
     * Various integral methods returning a full binary representation
     */
    public static String toBinaryString(byte n) {
        StringBuilder sb = new StringBuilder("00000000");

        for (int bit = 0; bit < 8; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(7 - bit, '1');
            }
        }

        return sb.toString();
    }

    /**
     * Join a String array with a delimiter. If the
     * delimiter is null, then just concatenate
     */


    public static String join(String[] atoms, String delimiter) {
	StringBuilder sb = new StringBuilder();
	for(int i = 0;i < atoms.length - 1;i++) {	
	    sb.append(atoms[i]);
	    if (delimiter != null) {
		sb.append(delimiter);
	    }
	    sb.append(atoms[atoms.length - 1]);
	}
	return sb.toString();
    }
	    

    public static String toHexString(byte n) {
        StringBuilder sb = new StringBuilder("00");
        for (int nybble = 0; nybble < 2; nybble++) {
                sb.setCharAt(1 - nybble, HEXCHARS.charAt((n >> nybble * 4) & 0x0F));
        }
        return sb.toString();
    }

    public static String toBinaryString(short n) {
        StringBuilder sb = new StringBuilder("0000000000000000");

        for (int bit = 0; bit < 15; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(15 - bit, '1');
            }
        }

        return sb.toString();
    }

    public static String toHexString(short n) {
        StringBuilder sb = new StringBuilder("0000");
        for (int nybble = 0; nybble < 4; nybble++) {
                sb.setCharAt(3 - nybble, HEXCHARS.charAt((n >> nybble * 4) & 0x0F));
        }
        return sb.toString();
    }

    public static String toBinaryString(int n) {
        StringBuilder sb = new StringBuilder("00000000000000000000000000000000");

        for (int bit = 0; bit < 32; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(31 - bit, '1');
            }
        }

        return sb.toString();
    }

    public static String toHexString(int n) {
        StringBuilder sb = new StringBuilder("00000000");
        for (int nybble = 0; nybble < 8; nybble++) {
                sb.setCharAt(7 - nybble, HEXCHARS.charAt((n >> nybble * 4) & 0x0F));
        }
        return sb.toString();
    }

    public static String toBinaryString(long n) {
        StringBuilder sb = new StringBuilder(
                "0000000000000000000000000000000000000000000000000000000000000000");

        for (int bit = 0; bit < 64; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(63 - bit, '1');
            }
        }

        return sb.toString();
    }

    public static String toHexString(long n) {
        StringBuilder sb = new StringBuilder("0000000000000000");
        for (int nybble = 0; nybble < 16; nybble++) {
                sb.setCharAt(15 - nybble, HEXCHARS.charAt((int)(n >> nybble * 4) & 0x0F));
        }
        return sb.toString();
    }

    public static String randomString(int numChars) {
	int numBits = (int)(numChars * Math.log(36) / Math.log(2));
	return new java.math.BigInteger(numBits, new java.util.Random()).toString(36);
    }

    /**
     * Return a random String with characters from the full range
     *
     * @param length
     *          Length of String needed
     * @return The String generated
     */
    public static String randomIso88591String(int length) {
        char[] iso88591chars = new char[(0xFF - 0xA1 + 1 + 0x7E) - 0x20 + 1];
        int ix = 0;

        for (int i = 0x20; i <= 0x7E; i++) {
            iso88591chars[ix++] = (char) i;
        }

        for (int i = 0xA1; i <= 0xFF; i++) {
            iso88591chars[ix++] = (char) i;
        }

        Random rand = new Random();
        StringBuilder sb = new StringBuilder(length);

        while (sb.length() < length) {
            char c = iso88591chars[rand.nextInt(iso88591chars.length)];
            sb.append(c);
        }

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param nodeName
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    static String makeOpenTag(String nodeName) {
        String lt = "<";
        String gt = ">";
        StringBuilder sb = new StringBuilder();
        sb.append(lt).append(nodeName).append(gt);

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param nodeName
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    static String makeCloseTag(String nodeName) {
        String lt = "<";
        String gt = ">";
        StringBuilder sb = new StringBuilder();
        sb.append(lt).append('/').append(nodeName).append(gt);

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param nodeName
     *          Description of the Parameter
     * @param nodeValue
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    static String makeNode(String nodeName, String nodeValue) {
        return makeOpenTag(nodeName) + nodeValue + makeCloseTag(nodeName);
    }

    /**
     * Description of the Method
     *
     * @param s
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    final static String removeAllWhitespace(String s) {
        String temp = s.trim();
        StringBuilder sb = new StringBuilder(temp);

        for (int i = sb.length() - 1; i > -1; i--) {
            char c = sb.charAt(i);

            if (Character.isISOControl(c) || (c == ' ')) {
                sb.deleteCharAt(i);
            }
        }

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param source
     *          Description of the Parameter
     * @param toReplace
     *          Description of the Parameter
     * @param replacement
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String findReplace(String source, String toReplace,
        String replacement) {
        if (source.indexOf(toReplace) > -1) {
            StringBuilder sb = new StringBuilder();
            int ix = -1;

            while ((ix = source.indexOf(toReplace)) >= 0) {
                sb.append(source.substring(0, ix)).append(replacement);
                source = source.substring(ix + toReplace.length());
            }

            if (source.length() > 1) {
                sb.append(source);
            }

            return sb.toString();
        } else {
            return source;
        }
    }

    /**
     * Gets the numbers attribute of the StringUtils class
     *
     * @param s
     *          Description of the Parameter
     * @return The numbers value
     */
    static int[] getNumbers(String s) {
        int[] numbers = null;
        StringTokenizer st = new StringTokenizer(s, ".");
        int numberCount = st.countTokens();

        if (numberCount > 0) {
            numbers = new int[numberCount];

            int ix = 0;

            while (st.hasMoreTokens()) {
                try {
                    numbers[ix] = Integer.parseInt(st.nextToken());
                } catch (Throwable e) {
                    e.printStackTrace();

                    return null;
                }

                ix++;
            }
        }

        return numbers;
    }

    /**
     * Description of the Method
     *
     * @param s
     *          Description of the Parameter
     * @param x
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    final static String removeChar(String s, char x) {
        StringBuilder sb = new StringBuilder(s);

        for (int i = s.length() - 1; i >= 0; i--) {
            if (sb.charAt(i) == x) {
                sb.deleteCharAt(i);
            }
        }

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param buff
     *          Description of the Parameter
     */
    public static void dumpAsHex(byte[] buff) {
        sun.misc.HexDumpEncoder e = new sun.misc.HexDumpEncoder();

        // System.out.println(e.encode(buff));
    }

    /**
     * Unlike Integer.toHexString, this returns a String with leading zeros if
     * necessary
     *
     * @param rawBytes
     *          Description of the Parameter
     * @return Description of the Return Value
     */

    /*
     * public static String byteArrayToHexString(byte[] rawBytes) { StringBuilder
     * sb = new StringBuilder(rawBytes.length * 2); for (int i = 0; i <
     * rawBytes.length; i++) { String s = Integer.toHexString(rawBytes[i] & 0xFF);
     * if (s.length() == 1) { // leading zero sb.append('0'); } sb.append(s); }
     * return sb.toString(); }
     */

    /*
     * public static byte[] hexStringToByteArray(String s) { int len = s.length();
     * if (len % 2 != 0) { return null; } StringBuilder sb = new StringBuilder(s);
     * byte[] bytes = new byte[len / 2]; int bytesIx = 0; for (int i = 0; i < len;
     * i += 2) { String temp = "" + sb.charAt(i) + sb.charAt(i + 1);
     * bytes[bytesIx] = (byte) Integer.parseInt(temp, 16); bytesIx++; } return
     * bytes; }
     */
    public static String byteArrayToHexString(byte[] rawBytes) {
        StringBuilder sb = new StringBuilder(rawBytes.length * 2);

        for (int i = 0; i < rawBytes.length; i++) {
            int ix = (rawBytes[i] >> 4) & 0xF;
            sb.append(HEXCHARS.charAt(ix));
            ix = rawBytes[i] & 0xF;
            sb.append(HEXCHARS.charAt(ix));
        }

        return sb.toString();
    }

    public static byte[] hexStringToByteArray(String s) {
        int stringIndex = 0;
        byte[] bytes = new byte[s.length() / 2];
        s = s.toLowerCase();

        for (int i = 0; i < bytes.length; i++) {
            stringIndex = i * 2;

            byte n = (byte) HEXCHARS.indexOf(s.charAt(stringIndex));
            n <<= 4;
            bytes[i] |= n;
            n = (byte) HEXCHARS.indexOf(s.charAt(stringIndex + 1));
            bytes[i] |= n;
        }

        return bytes;
    }

    /**
     * Gets the platformEncoding attribute of the StringUtils class
     *
     * @param args
     *          Description of the Parameter
     * @return The platformEncoding value
     */
    public static String getPlatformEncoding(String[] args) {
        InputStream inputStream = new ByteArrayInputStream(new byte[0]);
        InputStreamReader reader = new InputStreamReader(inputStream);

        return reader.getEncoding();
    }

    /**
     * Description of the Method
     *
     * @param s
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String toUnicodeEscaped(String s) {
        StringBuilder sb = new StringBuilder(s.length() * 6);
        char[] chars = s.toCharArray();
        int len = chars.length;

        for (int i = 0; i < len; i++) {
            sb.append(toUnicodeEscaped(chars[i]));
        }

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param c
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String toUnicodeEscaped(int c) {
        return toUnicodeEscaped((char) c);
    }

    /**
     * Description of the Method
     *
     * @param c
     *          Description of the Parameter
     * @return Description of the Return Value
     */

    /*
     * public static String toUnicodeEscaped(char c) { String s =
     * Integer.toHexString(c); while (s.length() < 4) { s = "0" + s; } return
     * "\\u" + s; }
     */

    /**
     * @param c
     *          The char to present in Java Unicode escaped format
     * @return A String in Java Unicode escaped format
     */
    public static String toUnicodeEscaped(char c) {
        StringBuilder sb = new StringBuilder(6);
        sb.append("\\u");

        for (int i = 0; i < 4; i++) {
            int index = (c >> (i * 4)) & 0xF;
            sb.insert(2, HEXCHARS.charAt(index));
        }

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param escaped
     *          A String in Unicode escaped format, e.g. "\u45ed"
     * @return The decoded character
     */
    public static char fromUnicodeEscaped(String escaped) {
        escaped = escaped.toLowerCase();

        return (char) Integer.parseInt(escaped.substring(escaped.indexOf("\\u") +
                2), 16);
    }

    /**
     * Write a String to file in a given encoding
     *
     * @param stringToWrite
     *          What are we writing?
     * @param fileName
     *          Where are we writing it to?
     * @param encoding
     *          The encoding to use
     * @exception IOException
     *              Description of the Exception
     */
    public static void writeEncoded(String stringToWrite, String fileName,
        String encoding) throws IOException {
        OutputStreamWriter out = null;

        try {
            out = new OutputStreamWriter(new FileOutputStream(fileName),
                    encoding);
            out.write(stringToWrite);
        } finally {
            out.close();
        }
    }

    /**
     * Description of the Method
     *
     * @param c
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String toHexEntity(char c) {
        String s = Integer.toHexString(c);

        return "&#x" + s + ";";
    }

    /**
     * Description of the Method
     *
     * @param chars
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String toHexEntities(char[] chars) {
        StringBuilder sb = new StringBuilder(chars.length * 8);

        for (int i = 0; i < chars.length; i++) {
            sb.append(toHexEntity(chars[i]));
        }

        return sb.toString();
    }

    /**
     * Description of the Method
     *
     * @param s
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String toHexEntities(String s) {
        return toHexEntities(s.toCharArray());
    }

    public static String fromEntities(String s, int base) {
        StringBuffer sb = new StringBuffer();
        final String RE;

        switch (base) {
        case 10:
            RE = "(?:&#(\\d+);)";

            break;

        case 16:
            RE = "(?:&#[Xx]([\\dA-Fa-f]+);)";

            break;

        default:
            throw new IllegalArgumentException("Illegitimate base " + base);
        }

        Pattern p = Pattern.compile(RE);
        Matcher m = p.matcher(s);
        int pos = 0;

        while (m.find()) {
            m.appendReplacement(sb,
                "" + (char) Integer.parseInt(m.group(1), base));
        }

        m.appendTail(sb);

        return sb.toString();
    }

    /**
     * Produce an obfuscated email address for anti-spam purposes, turning the
     * characters into hex entities
     *
     * @param email
     *          The email address
     * @return A space-separated mnemonic version of the address + hex entity
     *         version
     */

    /*
     * public static String obfuscateEmailAddress(String email) { String
     * spacedMail = email.replaceAll("(.)", "$1 "); return spacedMail + "\n" +
     * toHexEntities(email.toCharArray()); }
     */

    /**
     * Description of the Method
     *
     * @param file
     *          The file to be turned into a String
     * @return  The file as String encoded in the platform
     * default encoding
     */
    public static String fileToString(String file) {
	return StringUtils.fileToString(file, System.getProperty("file.encoding"));
    }

    /**
     * Description of the Method
     *
     * @param file
     *          The file to be turned into a String
     * @return  The file as String encoded in the platform
     * default encoding
     */
    public static String fileToString(String file, String encoding) {
        String result = null;
        DataInputStream in = null;

        try {
            File f = new File(file);
            byte[] buffer = new byte[(int) f.length()];
            in = new DataInputStream(new FileInputStream(f));
            in.readFully(buffer);
            result = new String(buffer, encoding);
        } catch (IOException e) {
            throw new RuntimeException("IO problem in fileToString", e);
        } finally {
            try {
                in.close();
            } catch (IOException e) { /* ignore it */
            }
        }

        return result;
    }

    /**
     *
     * @param list The List to be represented as a String
     * @param header The String prepended to the String representation
     * @param separator The String separating each List element
     * @param footer The String appended to the String representation
     *
     * @return The String representation of the List
     */
    public static String listToString(List<?extends Object> list,
        String header, String separator, String footer) {
        String delim = "";
        StringBuilder sb = new StringBuilder(header);

        for (int i = 0; i < list.size(); i++) {
            sb.append(delim).append("" + list.get(i));
            delim = separator;
        }

        return sb.append(footer).toString();
    }

    /**
     * Description of the Method
     *
     * @param fileName
     *          Description of the Parameter
     * @return Description of the Return Value
     */
    public static String toDosFileName(String fileName) {
        File f = new File(fileName);
        String origName = f.getName();
        StringTokenizer st = new StringTokenizer(origName, ".");

        switch (st.countTokens()) {
        case 2:

            String namePart = st.nextToken();
            String extnPart = st.nextToken();

            if (namePart.length() > 8) {
                // Truncate first part
                namePart = namePart.substring(0, 6) + "~1";
            }

            if (extnPart.length() > 3) {
                // Truncate extension
                extnPart = extnPart.substring(0, 3);
            }

            return (namePart + "." + extnPart).toUpperCase();

        case 1:

            // no extension
            String name = st.nextToken();

            if (name.length() > 8) {
                return (name.substring(0, 6) + "~1").toUpperCase();
            }

        default:

            // more than one extension - what to do?
            throw new IllegalArgumentException("Invalid file name: " +
                fileName);
        }
    }

    /**
     * Description of the Method
     *
     * @param s
     *          Description of the Parameter
     * @return Description of the Return Value
     * @exception NullPointerException
     *              Description of the Exception
     */
    public static String toTitleCase(String s) throws NullPointerException {
        StringBuilder temp = new StringBuilder(s);

        for (int count = 0; count < temp.length(); count++) {
            char c = temp.charAt(count);

            if (((count == 0) || (temp.charAt(count - 1) == ' ')) &&
                    !Character.isWhitespace(c)) {
                temp.setCharAt(count, Character.toUpperCase(c));
            }
        }

        return temp.toString();
    }

    /**
     * Description of the Class
     *
     * @author CEHJ
     * @created 10 March 2004
     */
    static class HTMLEscape {
        private static String[] htmlchars = new String[256];

        static {
            String[] entry = {
                    "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar",
                    "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg",
                    "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro",
                    "para", "middot", "cedil", "sup1", "ordm", "raquo", "frac14",
                    "frac12", "frac34", "iquest", "Agrave", "Aacute", "Acirc",
                    "Atilde", "Auml", "Aring", "AElig", "CCedil", "Egrave",
                    "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc",
                    "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc",
                    "Otilde", "Ouml", "times", "Oslash", "Ugrave", "Uacute",
                    "Ucirc", "Uuml", "Yacute", "THORN", "szlig", "agrave",
                    "aacute", "acirc", "atilde", "auml", "aring", "aelig",
                    "ccedil", "egrave", "eacute", "ecirc", "euml", "igrave",
                    "iacute", "icirc", "iuml", "eth", "ntilde", "ograve",
                    "oacute", "ocirc", "otilde", "ouml", "divid", "oslash",
                    "ugrave", "uacute", "ucirc", "uuml", "yacute", "thorn",
                    "yuml"
                };

            htmlchars['&'] = "&amp;";
            htmlchars['<'] = "&lt;";
            htmlchars['>'] = "&gt;";

            for (int c = '\u00A0', i = 0; c <= '\u00FF'; c++, i++) {
                htmlchars[c] = "&" + entry[i] + ";";
            }

            for (int c = '\u0083', i = 131; c <= '\u009f'; c++, i++) {
                htmlchars[c] = "&#" + i + ";";
            }

            htmlchars['\u0088'] = htmlchars['\u008D'] = htmlchars['\u008E'] = null;
            htmlchars['\u008F'] = htmlchars['\u0090'] = htmlchars['\u0098'] = null;
            htmlchars['\u009D'] = null;
        }

        /**
         * Description of the Method
         *
         * @param s
         *          Description of the Parameter
         * @return Description of the Return Value
         */
        public static String escape(String s) {
            int len = s.length();
            StringBuilder sb = new StringBuilder((len * 5) / 4);

            for (int i = 0; i < len; i++) {
                char c = s.charAt(i);
                String elem = htmlchars[c & 0xff];

                sb.append((elem == null) ? ("" + c) : elem);
            }

            return sb.toString();
        }
    }
}

