// 
// Decompiled by Procyon v0.6.0
// 

package com.nimbusds.jose.util;

import java.util.Arrays;

final class Base64Codec
{
    static int computeEncodedLength(final int inputLength, final boolean urlSafe) {
        if (inputLength == 0) {
            return 0;
        }
        if (urlSafe) {
            final int fullQuadLength = inputLength / 3 << 2;
            final int remainder = inputLength % 3;
            return (remainder == 0) ? fullQuadLength : (fullQuadLength + remainder + 1);
        }
        return (inputLength - 1) / 3 + 1 << 2;
    }
    
    static int tpSelect(final int bool, final int when_true, final int when_false) {
        final int mask = bool - 1;
        return when_true ^ (mask & (when_true ^ when_false));
    }
    
    static int tpLT(final int a, final int b) {
        return (int)(a - (long)b >>> 63);
    }
    
    static int tpGT(final int a, final int b) {
        return (int)(b - (long)a >>> 63);
    }
    
    static int tpEq(final int a, final int b) {
        final int bit_diff = a ^ b;
        final int msb_iff_zero_diff = bit_diff - 1 & ~bit_diff;
        return msb_iff_zero_diff >>> 63;
    }
    
    static byte encodeDigitBase64(final int digit_idx) {
        assert digit_idx >= 0 && digit_idx <= 63;
        final int is_uppercase = tpLT(digit_idx, 26);
        final int is_lowercase = tpGT(digit_idx, 25) & tpLT(digit_idx, 52);
        final int is_decimal = tpGT(digit_idx, 51) & tpLT(digit_idx, 62);
        final int is_62 = tpEq(digit_idx, 62);
        final int is_63 = tpEq(digit_idx, 63);
        final int as_uppercase = digit_idx - 0 + 65;
        final int as_lowercase = digit_idx - 26 + 97;
        final int as_decimal = digit_idx - 52 + 48;
        final int as_62 = 43;
        final int as_63 = 47;
        final int ascii = tpSelect(is_uppercase, as_uppercase, 0) | tpSelect(is_lowercase, as_lowercase, 0) | tpSelect(is_decimal, as_decimal, 0) | tpSelect(is_62, 43, 0) | tpSelect(is_63, 47, 0);
        return (byte)ascii;
    }
    
    static byte encodeDigitBase64URL(final int digit_idx) {
        assert digit_idx >= 0 && digit_idx <= 63;
        final int is_uppercase = tpLT(digit_idx, 26);
        final int is_lowercase = tpGT(digit_idx, 25) & tpLT(digit_idx, 52);
        final int is_decimal = tpGT(digit_idx, 51) & tpLT(digit_idx, 62);
        final int is_62 = tpEq(digit_idx, 62);
        final int is_63 = tpEq(digit_idx, 63);
        final int as_uppercase = digit_idx - 0 + 65;
        final int as_lowercase = digit_idx - 26 + 97;
        final int as_decimal = digit_idx - 52 + 48;
        final int as_62 = 45;
        final int as_63 = 95;
        final int ascii = tpSelect(is_uppercase, as_uppercase, 0) | tpSelect(is_lowercase, as_lowercase, 0) | tpSelect(is_decimal, as_decimal, 0) | tpSelect(is_62, 45, 0) | tpSelect(is_63, 95, 0);
        return (byte)ascii;
    }
    
    static int decodeDigit(final byte ascii) {
        final int is_uppercase = tpGT(ascii, 64) & tpLT(ascii, 91);
        final int is_lowercase = tpGT(ascii, 96) & tpLT(ascii, 123);
        final int is_decimal = tpGT(ascii, 47) & tpLT(ascii, 58);
        final int is_62 = tpEq(ascii, 45) | tpEq(ascii, 43);
        final int is_63 = tpEq(ascii, 95) | tpEq(ascii, 47);
        final int is_valid = is_uppercase | is_lowercase | is_decimal | is_62 | is_63;
        final int from_uppercase = ascii - 65 + 0;
        final int from_lowercase = ascii - 97 + 26;
        final int from_decimal = ascii - 48 + 52;
        final int from_62 = 62;
        final int from_63 = 63;
        final int digit_idx = tpSelect(is_uppercase, from_uppercase, 0) | tpSelect(is_lowercase, from_lowercase, 0) | tpSelect(is_decimal, from_decimal, 0) | tpSelect(is_62, 62, 0) | tpSelect(is_63, 63, 0) | tpSelect(is_valid, 0, -1);
        assert digit_idx >= -1 && digit_idx <= 63;
        return digit_idx;
    }
    
    public static String encodeToString(final byte[] byteArray, final boolean urlSafe) {
        final int sLen = (byteArray != null) ? byteArray.length : 0;
        if (sLen == 0) {
            return "";
        }
        final int eLen = sLen / 3 * 3;
        final int dLen = computeEncodedLength(sLen, urlSafe);
        final byte[] out = new byte[dLen];
        int s = 0;
        int d = 0;
        while (s < eLen) {
            final int i = (byteArray[s++] & 0xFF) << 16 | (byteArray[s++] & 0xFF) << 8 | (byteArray[s++] & 0xFF);
            if (urlSafe) {
                out[d++] = encodeDigitBase64URL(i >>> 18 & 0x3F);
                out[d++] = encodeDigitBase64URL(i >>> 12 & 0x3F);
                out[d++] = encodeDigitBase64URL(i >>> 6 & 0x3F);
                out[d++] = encodeDigitBase64URL(i & 0x3F);
            }
            else {
                out[d++] = encodeDigitBase64(i >>> 18 & 0x3F);
                out[d++] = encodeDigitBase64(i >>> 12 & 0x3F);
                out[d++] = encodeDigitBase64(i >>> 6 & 0x3F);
                out[d++] = encodeDigitBase64(i & 0x3F);
            }
        }
        final int left = sLen - eLen;
        if (left > 0) {
            final int j = (byteArray[eLen] & 0xFF) << 10 | ((left == 2) ? ((byteArray[sLen - 1] & 0xFF) << 2) : 0);
            if (urlSafe) {
                if (left == 2) {
                    out[dLen - 3] = encodeDigitBase64URL(j >> 12);
                    out[dLen - 2] = encodeDigitBase64URL(j >>> 6 & 0x3F);
                    out[dLen - 1] = encodeDigitBase64URL(j & 0x3F);
                }
                else {
                    out[dLen - 2] = encodeDigitBase64URL(j >> 12);
                    out[dLen - 1] = encodeDigitBase64URL(j >>> 6 & 0x3F);
                }
            }
            else {
                out[dLen - 4] = encodeDigitBase64(j >> 12);
                out[dLen - 3] = encodeDigitBase64(j >>> 6 & 0x3F);
                out[dLen - 2] = (byte)((left == 2) ? encodeDigitBase64(j & 0x3F) : 61);
                out[dLen - 1] = 61;
            }
        }
        return new String(out, StandardCharset.UTF_8);
    }
    
    public static byte[] decode(final String b64String) {
        if (b64String == null || b64String.isEmpty()) {
            return new byte[0];
        }
        final byte[] srcBytes = b64String.getBytes(StandardCharset.UTF_8);
        final int sLen = srcBytes.length;
        final int maxOutputLen = checkedCast(sLen * 6L >> 3);
        final byte[] dstBytes = new byte[maxOutputLen];
        int d = 0;
        int s = 0;
        while (s < srcBytes.length) {
            int i = 0;
            int j;
            for (j = 0; j < 4 && s < sLen; ++j) {
                final int c = decodeDigit(srcBytes[s++]);
                if (c >= 0) {
                    i |= c << 18 - j * 6;
                }
            }
            if (j >= 2) {
                dstBytes[d++] = (byte)(i >> 16);
                if (j < 3) {
                    continue;
                }
                dstBytes[d++] = (byte)(i >> 8);
                if (j < 4) {
                    continue;
                }
                dstBytes[d++] = (byte)i;
            }
        }
        return Arrays.copyOf(dstBytes, d);
    }
    
    private static int checkedCast(final long value) {
        final int result = (int)value;
        if (result != value) {
            throw new IllegalArgumentException(value + " cannot be cast to int without changing its value.");
        }
        return result;
    }
}
