//Copyright (c) 1997 The Open Group
//
//Permission to use, copy, modify, and distribute this software and its documentation for
//any purpose and without fee is hereby granted, provided that the above copyright notice
//appears in all copies and that both the copyright notice and this permission notice
//appear in supporting documentation, and that the name of ("TOG") or The Open Group not
//be used in advertising or publicity pertaining to distribution of the software without
//specific, written prior permission.
//
//THE OPEN GROUP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
//SHALL TOG BE LIABLE FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
//NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
//PERFORMANCE OF THIS SOFTWARE. 

package com.jxml.quick;

/**
 * Base64 is a standard for passing binary data as ASCII. RFC 2045
 * <p>Quick 1.1
 */
public class Base64
{
	static private byte t[]=null;
	static private byte u[]=null;

	static
	{
		int i;
	//build t
		t=new byte[64];
		for (i=0;i<26;++i)
			t[i]=(byte)('A'+i);
		for (i=26;i<52;++i)
			t[i]=(byte)('a'+i-26);
		for (i=52;i<62;++i)
			t[i]=(byte)('0'+i-52);
		t[62]=(byte)'+';
		t[63]=(byte)'/';
	//build u
		u=new byte[128];
		for (i=0;i<128;++i)
			u[i]=-1;
		u[' ']=-2;
		u['\r']=-2;
		u['\n']=-2;
		u['\t']=-2;
		u['=']=-3;
		for (i=0;i<64;++i)
			u[t[i]]=(byte)i;
	}

	static public byte[] ascToBin(byte[] asc)
		throws Invalid64Exception
	{
		int ascLen=asc.length;
		while(ascLen>0 && u[asc[ascLen-1]]<-1)
			--ascLen;
		int binLen=(ascLen*3)/4;
		byte bin[]=new byte[binLen];
		int binNdx=0;
		int ascNdx=0;
		int tmp;
		int rsl;
		for (ascNdx=0;ascNdx<ascLen;)
		{
			do
			{
				tmp=u[asc[ascNdx++]];
			} while (tmp==-2);
			if (tmp<0)
				throw new Invalid64Exception("1");
			rsl=tmp<<2;
			if (ascNdx==ascLen)
				throw new Invalid64Exception("2");
			do
			{
				tmp=u[asc[ascNdx++]];
			} while (tmp==-2);
			if (tmp<0)
				throw new Invalid64Exception("3");
			bin[binNdx++]=(byte)(rsl|(tmp>>4));
			while(ascNdx<ascLen && u[asc[ascNdx]]==-2)
				++ascNdx;
			if (ascNdx<ascLen)
			{
				rsl=(tmp&15)<<4;
				tmp=u[asc[ascNdx++]];
				if (tmp<0)
				{
					throw new Invalid64Exception("4");
				}
				bin[binNdx++]=(byte)(rsl|(tmp>>2));
				while(ascNdx<ascLen && u[asc[ascNdx]]==-2)
					++ascNdx;
				if (ascNdx<ascLen)
				{
					rsl=(tmp&3)<<6;
					tmp=u[asc[ascNdx++]];
					if (tmp<0)
						throw new Invalid64Exception("5");
					bin[binNdx++]=(byte)(rsl|tmp);
				}
			}
		}
		if (binLen==binNdx)
			return bin;
		byte[] sbin=new byte[binNdx];
		System.arraycopy(bin,0,sbin,0,binNdx);
		return sbin;
	}

	/**
	 * Convert an array of binary bytes to an array of ASCII bytes.
	 * <p>
	 * ASCII characters used are A-Z, a-z, 0-9, /, and +. 
	 * The '=' is used to pad the ascii to a multiple of 4 bytes.
	 * @param bin The binary data.
	 * @return The equivalant ASCII data.
	 */
	static public byte binToAsc(byte bin[])[]
	{
		int binLen=bin.length;
		int ascLen=((binLen+2)/3)*4;
//		ascLen+=binLen/36;
//		if (binLen%36!=0)
//			++ascLen;
		byte asc[]=new byte[ascLen];
		int binNdx=0;
		int ascNdx=0;
		int tmp;
		for(binNdx=0;binNdx<binLen;binNdx+=3)
		{
//			if (binNdx!=0 && binNdx%36==0)
//				asc[ascNdx++]=(byte)'\n';
			asc[ascNdx++]=t[(bin[binNdx]>>2)&63];
			tmp=(bin[binNdx]&3)<<4;
			if ((binNdx+1)<binLen)
				tmp|=(bin[binNdx+1]>>4)&15;
			asc[ascNdx++]=t[tmp];
			if ((binNdx+1)<binLen)
			{
				tmp=(bin[binNdx+1]&15)<<2;
				if ((binNdx+2)<binLen)
					tmp|=(bin[binNdx+2]>>6)&3;
				asc[ascNdx++]=t[tmp];
			}
			else
				asc[ascNdx++]=(byte)'=';
			if ((binNdx+2)<binLen)
			{
				asc[ascNdx++]=t[bin[binNdx+2]&63];
			}
			else
				asc[ascNdx++]=(byte)'=';
		}
//		asc[ascNdx++]=(byte)'\n';
		return asc;
	}
}
