namespace Mox.Bridge
{
	public class Base64Encoder
	{
		public Base64Encoder(byte[] _input)
		{
			m_aSource = _input;
			m_iLength = _input.Length;

			if (m_iLength % 3 == 0)
			{
				m_iPaddingCount = 0;
				m_iBlockCount = m_iLength / 3;
			}
			else
			{
				m_iPaddingCount = 3 - m_iLength % 3; //need to add padding
				m_iBlockCount = (m_iLength + m_iPaddingCount) / 3;
			}

			m_iLength2 = m_iLength + m_iPaddingCount; //or m_iBlockCount *3
		}

		public string GetEncoded()
		{
			var source2 = new byte[m_iLength2];

			//copy data over insert padding
			for (var x = 0; x < m_iLength2; x++)
			{
				if (x < m_iLength)
				{
					source2[x] = m_aSource[x];
				}
				else
				{
					source2[x] = 0;
				}
			}

			var buffer = new byte[m_iBlockCount * 4];
			string result = "";

			for (var x = 0; x < m_iBlockCount; x++)
			{
				var b1 = source2[x * 3];
				var b2 = source2[x * 3 + 1];
				var b3 = source2[x * 3 + 2];

				var temp1 = (byte) ((b1 & 252) >> 2);

				var temp = (byte) ((b1 & 3) << 4);
				var temp2 = (byte) ((b2 & 240) >> 4);
				temp2 += temp; //second

				temp = (byte) ((b2 & 15) << 2);
				var temp3 = (byte) ((b3 & 192) >> 6);
				temp3 += temp; //third

				var temp4 = (byte) (b3 & 63);

				buffer[x * 4] = temp1;
				buffer[x * 4 + 1] = temp2;
				buffer[x * 4 + 2] = temp3;
				buffer[x * 4 + 3] = temp4;
			}

			for (var x = 0; x < m_iBlockCount * 4; x++)
			{
				result += sixbit2char(buffer[x]);
			}

			//covert last "A"s to "=", based on m_iPaddingCount
			switch (m_iPaddingCount)
			{
			case 1:
				return result.Substring(0, result.Length - 1) + "=";
			case 2:
				return result.Substring(0, result.Length - 2) + "==";
			}

			return result;
		}

		private char sixbit2char(byte b)
		{
			var lookupTable = new []
			{
				'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',
				'+',
				'/'
			};

			if (b <= 63)
			{
				return lookupTable[b];
			}

			//should not happen;
			return ' ';
		}

		private readonly int m_iBlockCount;
		private readonly int m_iLength;
		private readonly int m_iLength2;
		private readonly int m_iPaddingCount;
		private readonly byte[] m_aSource;
	}
}
