using System;

using Nequeo.Cryptography.Key.Crypto.Parameters;

namespace Nequeo.Cryptography.Key.Crypto
{
	public class BufferedStreamCipher
		: BufferedCipherBase
	{
		private readonly IStreamCipher cipher;

		public BufferedStreamCipher(
			IStreamCipher cipher)
		{
			if (cipher == null)
				throw new ArgumentNullException("cipher");

			this.cipher = cipher;
		}

		public override string AlgorithmName
		{
			get { return cipher.AlgorithmName; }
		}

		public override void Init(
			bool				forEncryption,
			ICipherParameters	parameters)
		{
			if (parameters is ParametersWithRandom)
			{
				parameters = ((ParametersWithRandom) parameters).Parameters;
			}

			cipher.Init(forEncryption, parameters);
		}

		public override int GetBlockSize()
		{
			return 0;
		}

		public override int GetOutputSize(
			int inputLen)
		{
			return inputLen;
		}

		public override int GetUpdateOutputSize(
			int inputLen)
		{
			return inputLen;
		}

		public override byte[] ProcessByte(
			byte input)
		{
			return new byte[]{ cipher.ReturnByte(input) };
		}

		public override int ProcessByte(
			byte	input,
			byte[]	output,
			int		outOff)
		{
			if (outOff >= output.Length)
				throw new DataLengthException("output buffer too short");

			output[outOff] = cipher.ReturnByte(input);
			return 1;
		}

		public override byte[] ProcessBytes(
			byte[]	input,
			int		inOff,
			int		length)
		{
			if (length < 1)
				return null;

			byte[] output = new byte[length];
			cipher.ProcessBytes(input, inOff, length, output, 0);
			return output;
		}

		public override int ProcessBytes(
			byte[]	input,
			int		inOff,
			int		length,
			byte[]	output,
			int		outOff)
		{
			if (length < 1)
				return 0;

			if (length > 0)
			{
				cipher.ProcessBytes(input, inOff, length, output, outOff);
			}

			return length;
		}

		public override byte[] DoFinal()
		{
			Reset();

			return EmptyBuffer;
		}

		public override byte[] DoFinal(
			byte[]	input,
			int		inOff,
			int		length)
		{
			if (length < 1)
				return EmptyBuffer;

			byte[] output = ProcessBytes(input, inOff, length);

			Reset();

			return output;
		}

		public override void Reset()
		{
			cipher.Reset();
		}
	}
}
