package nckd.jimin.jyyy.fi.plugin.operate;

import java.io.*;
import java.nio.ByteBuffer;
//import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class SRMBase64Util
{
    private static class DecInputStream extends InputStream
    {

        public int read()
            throws IOException
        {
            return read(sbBuf, 0, 1) != -1 ? sbBuf[0] & 255 : -1;
        }

        public int read(byte abyte0[], int i, int j)
            throws IOException
        {
            if(closed)
                throw new IOException("Stream is closed");
            if(eof && nextout < 0)
                return -1;
            if(i < 0 || j < 0 || j > abyte0.length - i)
                throw new IndexOutOfBoundsException();
            int k = i;
            if(nextout >= 0)
            {
                do
                {
                    if(j == 0)
                        return i - k;
                    abyte0[i++] = (byte)(bits >> nextout);
                    j--;
                    nextout -= 8;
                } while(nextout >= 0);
                bits = 0;
            }
            do
            {
                if(j <= 0)
                    break;
                int l = is.read();
                if(l == -1)
                {
                    eof = true;
                    if(nextin != 18)
                    {
                        if(nextin == 12)
                            throw new IOException("Base64 stream has one un-decoded dangling byte.");
                        abyte0[i++] = (byte)(bits >> 16);
                        j--;
                        if(nextin == 0)
                            if(j == 0)
                            {
                                bits >>= 8;
                                nextout = 0;
                            } else
                            {
                                abyte0[i++] = (byte)(bits >> 8);
                            }
                    }
                    if(i == k)
                        return -1;
                    else
                        return i - k;
                }
                if(l == 61)
                {
                    if(nextin == 18 || nextin == 12 || nextin == 6 && is.read() != 61)
                        throw new IOException((new StringBuilder()).append("Illegal base64 ending sequence:").append(nextin).toString());
                    abyte0[i++] = (byte)(bits >> 16);
                    j--;
                    if(nextin == 0)
                        if(j == 0)
                        {
                            bits >>= 8;
                            nextout = 0;
                        } else
                        {
                            abyte0[i++] = (byte)(bits >> 8);
                        }
                    eof = true;
                    break;
                }
                if((l = base64[l]) == -1)
                {
                    if(!isMIME)
                        throw new IOException((new StringBuilder()).append("Illegal base64 character ").append(Integer.toString(l, 16)).toString());
                    continue;
                }
                bits |= l << nextin;
                if(nextin == 0)
                {
                    nextin = 18;
                    for(nextout = 16; nextout >= 0;)
                    {
                        abyte0[i++] = (byte)(bits >> nextout);
                        j--;
                        nextout -= 8;
                        if(j == 0 && nextout >= 0)
                            return i - k;
                    }

                    bits = 0;
                    continue;
                }
                nextin -= 6;
            } while(true);
            return i - k;
        }

        public int available()
            throws IOException
        {
            if(closed)
                throw new IOException("Stream is closed");
            else
                return is.available();
        }

        public void close()
            throws IOException
        {
            if(!closed)
            {
                closed = true;
                is.close();
            }
        }

        private final InputStream is;
        private final boolean isMIME;
        private final int base64[];
        private int bits;
        private int nextin;
        private int nextout;
        private boolean eof;
        private boolean closed;
        private byte sbBuf[];

        DecInputStream(InputStream inputstream, int ai[], boolean flag)
        {
            bits = 0;
            nextin = 18;
            nextout = -8;
            eof = false;
            closed = false;
            sbBuf = new byte[1];
            is = inputstream;
            base64 = ai;
            isMIME = flag;
        }
    }

    public static class Decoder
    {

        public byte[] decode(byte abyte0[])
        {
            byte abyte1[] = new byte[outLength(abyte0, 0, abyte0.length)];
            int i = decode0(abyte0, 0, abyte0.length, abyte1);
            if(i != abyte1.length)
                abyte1 = Arrays.copyOf(abyte1, i);
            return abyte1;
        }

        public byte[] decode(String s) throws UnsupportedEncodingException
        {
            return decode(s.getBytes("ISO-8859-1"));//StandardCharsets.ISO_8859_1
        }

        public int decode(byte abyte0[], byte abyte1[])
        {
            int i = outLength(abyte0, 0, abyte0.length);
            if(abyte1.length < i)
                throw new IllegalArgumentException("Output byte array is too small for decoding all input bytes");
            else
                return decode0(abyte0, 0, abyte0.length, abyte1);
        }

        public ByteBuffer decode(ByteBuffer bytebuffer)
        {
            int i = bytebuffer.position();
            try
            {
                byte abyte0[];
                int j;
                int k;
                if(bytebuffer.hasArray())
                {
                    abyte0 = bytebuffer.array();
                    j = bytebuffer.arrayOffset() + bytebuffer.position();
                    k = bytebuffer.arrayOffset() + bytebuffer.limit();
                    bytebuffer.position(bytebuffer.limit());
                } else
                {
                    abyte0 = new byte[bytebuffer.remaining()];
                    bytebuffer.get(abyte0);
                    j = 0;
                    k = abyte0.length;
                }
                byte abyte1[] = new byte[outLength(abyte0, j, k)];
                return ByteBuffer.wrap(abyte1, 0, decode0(abyte0, j, k, abyte1));
            }
            catch(IllegalArgumentException illegalargumentexception)
            {
                bytebuffer.position(i);
                throw illegalargumentexception;
            }
        }

        public InputStream wrap(InputStream inputstream)
        {
            requireNonNull(inputstream);
            return new DecInputStream(inputstream, isURL ? fromBase64URL : fromBase64, isMIME);
        }

        private int outLength(byte abyte0[], int i, int j)
        {
            int ai[] = isURL ? fromBase64URL : fromBase64;
            int k = 0;
            int l = j - i;
            if(l == 0)
                return 0;
            if(l < 2)
                if(isMIME && ai[0] == -1)
                    return 0;
                else
                    throw new IllegalArgumentException("Input byte[] should at least have 2 bytes for base64 bytes");
            if(isMIME)
            {
                int i1 = 0;
                do
                {
                    if(i >= j)
                        break;
                    int j1 = abyte0[i++] & 255;
                    if(j1 == 61)
                    {
                        l -= (j - i) + 1;
                        break;
                    }
                    if((j1 = ai[j1]) == -1)
                        i1++;
                } while(true);
                l -= i1;
            } else
            if(abyte0[j - 1] == 61)
            {
                k++;
                if(abyte0[j - 2] == 61)
                    k++;
            }
            if(k == 0 && (l & 3) != 0)
                k = 4 - (l & 3);
            return 3 * ((l + 3) / 4) - k;
        }

        private int decode0(byte abyte0[], int i, int j, byte abyte1[])
        {
            int ai[] = isURL ? fromBase64URL : fromBase64;
            int k = 0;
            int l = 0;
            int i1 = 18;
            do
            {
                if(i >= j)
                    break;
                int j1 = abyte0[i++] & 255;
                if((j1 = ai[j1]) < 0)
                {
                    if(j1 == -2)
                    {
                        if(i1 == 6 && (i == j || abyte0[i++] != 61) || i1 == 18)
                            throw new IllegalArgumentException("Input byte array has wrong 4-byte ending unit");
                        break;
                    }
                    if(!isMIME)
                        throw new IllegalArgumentException((new StringBuilder()).append("Illegal base64 character ").append(Integer.toString(abyte0[i - 1], 16)).toString());
                } else
                {
                    l |= j1 << i1;
                    if((i1 -= 6) < 0)
                    {
                        abyte1[k++] = (byte)(l >> 16);
                        abyte1[k++] = (byte)(l >> 8);
                        abyte1[k++] = (byte)l;
                        i1 = 18;
                        l = 0;
                    }
                }
            } while(true);
            if(i1 == 6)
                abyte1[k++] = (byte)(l >> 16);
            else
            if(i1 == 0)
            {
                abyte1[k++] = (byte)(l >> 16);
                abyte1[k++] = (byte)(l >> 8);
            } else
            if(i1 == 12)
                throw new IllegalArgumentException("Last unit does not have enough valid bits");
            while(i < j) 
                if(!isMIME || ai[abyte0[i++]] >= 0)
                    throw new IllegalArgumentException((new StringBuilder()).append("Input byte array has incorrect ending byte at ").append(i).toString());
            return k;
        }

        private final boolean isURL;
        private final boolean isMIME;
        private static final int fromBase64[];
        private static final int fromBase64URL[];
        static final Decoder RFC4648 = new Decoder(false, false);
        static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
        static final Decoder RFC2045 = new Decoder(false, true);

        static 
        {
            fromBase64 = new int[256];
            Arrays.fill(fromBase64, -1);
            for(int i = 0; i < Encoder.toBase64.length; i++)
                fromBase64[Encoder.toBase64[i]] = i;

            fromBase64[61] = -2;
            fromBase64URL = new int[256];
            Arrays.fill(fromBase64URL, -1);
            for(int j = 0; j < Encoder.toBase64URL.length; j++)
                fromBase64URL[Encoder.toBase64URL[j]] = j;

            fromBase64URL[61] = -2;
        }


        private Decoder(boolean flag, boolean flag1)
        {
            isURL = flag;
            isMIME = flag1;
        }
    }

    private static class EncOutputStream extends FilterOutputStream
    {

        public void write(int i)
            throws IOException
        {
            byte abyte0[] = new byte[1];
            abyte0[0] = (byte)(i & 255);
            write(abyte0, 0, 1);
        }

        private void checkNewline()
            throws IOException
        {
            if(linepos == linemax)
            {
                out.write(newline);
                linepos = 0;
            }
        }

        public void write(byte abyte0[], int i, int j)
            throws IOException
        {
            if(closed)
                throw new IOException("Stream is closed");
            if(i < 0 || j < 0 || j > abyte0.length - i)
                throw new ArrayIndexOutOfBoundsException();
            if(j == 0)
                return;
            if(leftover != 0)
            {
                if(leftover == 1)
                {
                    b1 = abyte0[i++] & 255;
                    if(--j == 0)
                    {
                        leftover++;
                        return;
                    }
                }
                b2 = abyte0[i++] & 255;
                j--;
                checkNewline();
                out.write(base64[b0 >> 2]);
                out.write(base64[b0 << 4 & 63 | b1 >> 4]);
                out.write(base64[b1 << 2 & 63 | b2 >> 6]);
                out.write(base64[b2 & 63]);
                linepos += 4;
            }
            int k = j / 3;
            leftover = j - k * 3;
            while(k-- > 0) 
            {
                checkNewline();
                int l = (abyte0[i++] & 255) << 16 | (abyte0[i++] & 255) << 8 | abyte0[i++] & 255;
                out.write(base64[l >>> 18 & 63]);
                out.write(base64[l >>> 12 & 63]);
                out.write(base64[l >>> 6 & 63]);
                out.write(base64[l & 63]);
                linepos += 4;
            }
            if(leftover == 1)
                b0 = abyte0[i++] & 255;
            else
            if(leftover == 2)
            {
                b0 = abyte0[i++] & 255;
                b1 = abyte0[i++] & 255;
            }
        }

        public void close()
            throws IOException
        {
            if(!closed)
            {
                closed = true;
                if(leftover == 1)
                {
                    checkNewline();
                    out.write(base64[b0 >> 2]);
                    out.write(base64[b0 << 4 & 63]);
                    if(doPadding)
                    {
                        out.write(61);
                        out.write(61);
                    }
                } else
                if(leftover == 2)
                {
                    checkNewline();
                    out.write(base64[b0 >> 2]);
                    out.write(base64[b0 << 4 & 63 | b1 >> 4]);
                    out.write(base64[b1 << 2 & 63]);
                    if(doPadding)
                        out.write(61);
                }
                leftover = 0;
                out.close();
            }
        }

        private int leftover;
        private int b0;
        private int b1;
        private int b2;
        private boolean closed;
        private final char base64[];
        private final byte newline[];
        private final int linemax;
        private final boolean doPadding;
        private int linepos;

        EncOutputStream(OutputStream outputstream, char ac[], byte abyte0[], int i, boolean flag)
        {
            super(outputstream);
            leftover = 0;
            closed = false;
            linepos = 0;
            base64 = ac;
            newline = abyte0;
            linemax = i;
            doPadding = flag;
        }
    }

    public static class Encoder
    {

        private final int outLength(int i)
        {
            int j = 0;
            if(doPadding)
            {
                j = 4 * ((i + 2) / 3);
            } else
            {
                int k = i % 3;
                j = 4 * (i / 3) + (k != 0 ? k + 1 : 0);
            }
            if(linemax > 0)
                j += ((j - 1) / linemax) * newline.length;
            return j;
        }

        public byte[] encode(byte abyte0[])
        {
            int i = outLength(abyte0.length);
            byte abyte1[] = new byte[i];
            int j = encode0(abyte0, 0, abyte0.length, abyte1);
            if(j != abyte1.length)
                return Arrays.copyOf(abyte1, j);
            else
                return abyte1;
        }

        public int encode(byte abyte0[], byte abyte1[])
        {
            int i = outLength(abyte0.length);
            if(abyte1.length < i)
                throw new IllegalArgumentException("Output byte array is too small for encoding all input bytes");
            else
                return encode0(abyte0, 0, abyte0.length, abyte1);
        }

        public String encodeToString(byte abyte0[])
        {
            byte abyte1[] = encode(abyte0);
            return new String(abyte1, 0, 0, abyte1.length);
        }

        public ByteBuffer encode(ByteBuffer bytebuffer)
        {
            int i = outLength(bytebuffer.remaining());
            byte abyte0[] = new byte[i];
            int j = 0;
            if(bytebuffer.hasArray())
            {
                j = encode0(bytebuffer.array(), bytebuffer.arrayOffset() + bytebuffer.position(), bytebuffer.arrayOffset() + bytebuffer.limit(), abyte0);
                bytebuffer.position(bytebuffer.limit());
            } else
            {
                byte abyte1[] = new byte[bytebuffer.remaining()];
                bytebuffer.get(abyte1);
                j = encode0(abyte1, 0, abyte1.length, abyte0);
            }
            if(j != abyte0.length)
                abyte0 = Arrays.copyOf(abyte0, j);
            return ByteBuffer.wrap(abyte0);
        }

        public OutputStream wrap(OutputStream outputstream)
        {
            requireNonNull(outputstream);
            return new EncOutputStream(outputstream, isURL ? toBase64URL : toBase64, newline, linemax, doPadding);
        }

        public Encoder withoutPadding()
        {
            if(!doPadding)
                return this;
            else
                return new Encoder(isURL, newline, linemax, false);
        }

        private int encode0(byte abyte0[], int i, int j, byte abyte1[])
        {
            char ac[] = isURL ? toBase64URL : toBase64;
            int k = i;
            int l = ((j - i) / 3) * 3;
            int i1 = i + l;
            if(linemax > 0 && l > (linemax / 4) * 3)
                l = (linemax / 4) * 3;
            int j1 = 0;
            do
            {
                if(k >= i1)
                    break;
                int k1 = Math.min(k + l, i1);
                int i2 = k;
                int k2 = j1;
                while(i2 < k1) 
                {
                    int l2 = (abyte0[i2++] & 255) << 16 | (abyte0[i2++] & 255) << 8 | abyte0[i2++] & 255;
                    abyte1[k2++] = (byte)ac[l2 >>> 18 & 63];
                    abyte1[k2++] = (byte)ac[l2 >>> 12 & 63];
                    abyte1[k2++] = (byte)ac[l2 >>> 6 & 63];
                    abyte1[k2++] = (byte)ac[l2 & 63];
                }
                i2 = ((k1 - k) / 3) * 4;
                j1 += i2;
                k = k1;
                if(i2 == linemax && k < j)
                {
                    byte abyte2[] = newline;
                    int i3 = abyte2.length;
                    int j3 = 0;
                    while(j3 < i3) 
                    {
                        byte byte0 = abyte2[j3];
                        abyte1[j1++] = byte0;
                        j3++;
                    }
                }
            } while(true);
            if(k < j)
            {
                int l1 = abyte0[k++] & 255;
                abyte1[j1++] = (byte)ac[l1 >> 2];
                if(k == j)
                {
                    abyte1[j1++] = (byte)ac[l1 << 4 & 63];
                    if(doPadding)
                    {
                        abyte1[j1++] = 61;
                        abyte1[j1++] = 61;
                    }
                } else
                {
                    int j2 = abyte0[k++] & 255;
                    abyte1[j1++] = (byte)ac[l1 << 4 & 63 | j2 >> 4];
                    abyte1[j1++] = (byte)ac[j2 << 2 & 63];
                    if(doPadding)
                        abyte1[j1++] = 61;
                }
            }
            return j1;
        }

        private final byte newline[];
        private final int linemax;
        private final boolean isURL;
        private final boolean doPadding;
        private static final char toBase64[] = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
            'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 
            'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 
            '8', '9', '+', '/'
        };
        private static final char toBase64URL[] = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
            'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 
            'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 
            '8', '9', '-', '_'
        };
        private static final int MIMELINEMAX = 76;
        private static final byte CRLF[] = {
            13, 10
        };
        static final Encoder RFC4648 = new Encoder(false, null, -1, true);
        static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
        static final Encoder RFC2045 = new Encoder(false, CRLF, 76, true);




        private Encoder(boolean flag, byte abyte0[], int i, boolean flag1)
        {
            isURL = flag;
            newline = abyte0;
            linemax = i;
            doPadding = flag1;
        }

    }


    private SRMBase64Util()
    {
    }

    public static Encoder getEncoder()
    {
        return Encoder.RFC4648;
    }

    public static Encoder getUrlEncoder()
    {
        return Encoder.RFC4648_URLSAFE;
    }

    public static Encoder getMimeEncoder()
    {
        return Encoder.RFC2045;
    }

    public static Encoder getMimeEncoder(int i, byte abyte0[])
    {
        requireNonNull(abyte0);
        int ai[] = Decoder.fromBase64;
        byte abyte1[] = abyte0;
        int j = abyte1.length;
        for(int k = 0; k < j; k++)
        {
            byte byte0 = abyte1[k];
            if(ai[byte0 & 255] != -1)
                throw new IllegalArgumentException((new StringBuilder()).append("Illegal base64 line separator character 0x").append(Integer.toString(byte0, 16)).toString());
        }

        if(i <= 0)
            return Encoder.RFC4648;
        else
            return new Encoder(false, abyte0, (i >> 2) << 2, true);
    }
    public static Object requireNonNull(Object obj)
    {
        if(obj == null)
            throw new NullPointerException();
        else
            return obj;
    }
    public static Decoder getDecoder()
    {
        return Decoder.RFC4648;
    }

    public static Decoder getUrlDecoder()
    {
        return Decoder.RFC4648_URLSAFE;
    }

    public static Decoder getMimeDecoder()
    {
        return Decoder.RFC2045;
    }
}
