/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.text;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Collection;
import net.ranides.assira.generic.ValueUtils;
import net.ranides.assira.trace.ExceptionInspector;

/**
 *
 * @author ranides
 */
public final class StringEncoder {

    private StringEncoder() { }

    private static final char[] HEX_CHAR_TABLE = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };

    private static final byte[] EMPTY_TABLE = {};

    public static String bytesToHex(byte[] raw, String separator) {
        StringBuilder hex = new StringBuilder();
        boolean isFirst = true;
        for (byte b : raw) {
            int value = b & 0xFF;
            if( !isFirst ) { hex.append(separator); }
            hex.append( HEX_CHAR_TABLE[value >>> 4] );
            hex.append( HEX_CHAR_TABLE[value & 0xF] );
            isFirst = false;
        }
        return hex.toString();
    }

    public static String bytesToHex(byte[] raw) {
        return bytesToHex(raw, "");
    }

    public static String bytesToHex(ByteBuffer buffer) {
        int size = buffer.remaining();
        byte[] dst = new byte[size];
        System.arraycopy(buffer.array(), 0, dst, 0, size);
        return bytesToHex(dst);
    }

    @Deprecated
    public static String bytesToHex(Collection<Byte> values, String charset) {
        byte[] raw = new byte[values.size()];
        int i=0;
        for(Byte value : values) { raw[i] = value; i++; }
        return new String(raw, Charset.forName(charset));
    }

    public static byte[] hexToBytes(char[] hex) {
        return hexToBytes(hex, null, null);
    }

    public static byte[] hexToBytes(char[] hex, byte[] _prefix, byte[] _suffix) {
        final byte[] prefix = ValueUtils.or(_prefix, EMPTY_TABLE);
        final byte[] suffix = ValueUtils.or(_suffix, EMPTY_TABLE);
        final int    N      = hex.length / 2;
        final int    length = N + prefix.length + suffix.length;
        final byte[] raw    = new byte[length];

        System.arraycopy(prefix, 0, raw, 0, prefix.length);

        for (int i = 0; i<N; i++) {
            int high    = Character.digit(hex[i * 2], 16);
            int low     = Character.digit(hex[i * 2 + 1], 16);
            int value   = (high << 4) | low;
            if (value > 127) {
                value -= 256;
            }
            raw[prefix.length+i]= (byte) value;
        }
        System.arraycopy(suffix, 0, raw, prefix.length+N, suffix.length);
        return raw;
    }

    public static byte[] hexToBytes(String hex) {
        return hexToBytes(hex.toCharArray());
    }

    public static byte[] hexToBytes(String hex, byte[] prefix, byte[] postfix) {
        return hexToBytes(hex.toCharArray(), prefix, postfix);
    }
    
    public static String xor(String value, int key) {
        if(null == value) {
            return null;
        }
        StringBuilder result = new StringBuilder( value.length() );
        for(int i=0, n=value.length(); i<n; i++) {
            result.append( (char)(value.charAt(i) ^ key) );
        }
        return result.toString();
    }

    public static String decodeURL(String input) {
        return decodeURL(input, TextEncoding.NIO_UTF8);
    }
    
    public static String decodeURL(String input, Charset charset) {
        try {
            return URLDecoder.decode(input, charset.name());
        } catch(UnsupportedEncodingException cause) {
            throw ExceptionInspector.assertError("Unsupported charset: " + charset, cause);
        }
    }

}
