package railo.runtime.coder;

import java.text.StringCharacterIterator;

/**
 * Unix Coding for java
 */
public final class UUCoder {

    /**
     * encodes a byte array to a String
     * @param barr
     * @return encoded String
     */
    public static String encode(byte barr[]) {
        StringBuffer rtn = new StringBuffer();
        int len = barr.length;
        int read = 0;
        boolean stop = false;
        byte b = 0;
        int offset = 0;
        
        do {
            int left=len-read;
            if(left==0) stop=true;
            
            if(left<=45) b=(byte)left;
            else b=45;
            
            rtn.append(_enc(b));
            for(int i = 0; i < b; i += 3) {
                if(len - offset < 3) {
                    byte padding[] = new byte[3];
                    for(int z = 0; offset + z < len; z++) padding[z] = barr[offset + z];
                    encodeBytes(padding, 0, rtn);
                } else {
                    encodeBytes(barr, offset, rtn);
                }
                offset += 3;
            }

            rtn.append('\n');
            read += b;
            if(b < 45) stop=true;
        } 
        while(!stop);
        return rtn.toString();
    }

    /**
     * decodes back a String to a byte array
     * @param b
     * @return decoded byte array
     */
    public static byte[] decode(String str) throws CoderException {
        byte out[] = new byte[str.length()];
        int len = 0;
        int offset = 0;
        //int current = 0;
        byte b = 0;
        boolean stop = false;
        StringCharacterIterator it = new StringCharacterIterator(str);
        do {
            b = _dec(it.current());
            it.next();
            if(b>45) throw new CoderException("can't decode string ["+str+"]");
            if(b<45) stop=true;
            len += b;
            for(; b > 0; b -= 3) {
                decodeChars(it, out, offset);
                offset += 3;
            }
            it.next();
        } 
        while(!stop);
        byte rtn[] = new byte[len];
        for(int i = 0; i < len; i++)
            rtn[i] = out[i];

        return rtn;
    }

    private static void encodeBytes(byte in[], int off, StringBuffer out) {
        out.append(_enc((byte)(in[off] >>> 2)));
        out.append(_enc((byte)(in[off] << 4 & 0x30 | in[off + 1] >>> 4 & 0xf)));
        out.append(_enc((byte)(in[off + 1] << 2 & 0x3c | in[off + 2] >>> 6 & 3)));
        out.append(_enc((byte)(in[off + 2] & 0x3f)));
    }

    private static void decodeChars(StringCharacterIterator it, byte out[], int off) {
        byte b1 = _dec(it.current());
        byte b2 = _dec(it.next());
        byte b3 = _dec(it.next());
        byte b4 = _dec(it.next());
        it.next();
        byte b5 = (byte)(b1 << 2 | b2 >> 4);
        byte b6 = (byte)(b2 << 4 | b3 >> 2);
        byte b7 = (byte)(b3 << 6 | b4);
        out[off] = b5;
        out[off + 1] = b6;
        out[off + 2] = b7;
    }

    private static char _enc(byte c) {
        return (char)((c & 0x3f) + 32);
    }

    private static byte _dec(char c) {
        return (byte)(c - 32 & 0x3f);
    }
}