using System;
using System.IO;
using System.Security.Cryptography;

namespace Lidgren.Network
{
	public abstract class NetCryptoProviderBase : NetEncryption
	{
		protected SymmetricAlgorithm m_algorithm;

		public NetCryptoProviderBase(NetPeer peer, SymmetricAlgorithm algo)
			: base(peer)
		{
			m_algorithm = algo;
			m_algorithm.GenerateKey();
			m_algorithm.GenerateIV();
		}

		public override void SetKey(byte[] data, int offset, int count)
		{
			int len = m_algorithm.Key.Length;
			var key = new byte[len];
			for (int i = 0; i < len; i++)
				key[i] = data[offset + (i % count)];
			m_algorithm.Key = key;

			len = m_algorithm.IV.Length;
			key = new byte[len];
			for (int i = 0; i < len; i++)
				key[len - 1 - i] = data[offset + (i % count)];
			m_algorithm.IV = key;
		}

		public override bool Encrypt(NetOutgoingMessage msg)
		{
			int unEncLenBits = msg.LengthBits;

			var ms = new MemoryStream();
			var cs = new CryptoStream(ms, m_algorithm.CreateEncryptor(), CryptoStreamMode.Write);
			cs.Write(msg.m_data, 0, msg.LengthBytes);
			cs.Close();

			// get results
			var arr = ms.ToArray();
			ms.Close();

			msg.EnsureBufferSize((arr.Length + 4) * 8);
			msg.LengthBits = 0; // reset write pointer
			msg.Write((uint)unEncLenBits);
			msg.Write(arr);
			msg.LengthBits = (arr.Length + 4) * 8;

			return true;
		}

		public override bool Decrypt(NetIncomingMessage msg)
		{
			int unEncLenBits = (int)msg.ReadUInt32();

			var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
			var cs = new CryptoStream(ms, m_algorithm.CreateDecryptor(), CryptoStreamMode.Read);

			var byteLen = NetUtility.BytesToHoldBits(unEncLenBits);
			var result = m_peer.GetStorage(byteLen);
			cs.Read(result, 0, byteLen);
			cs.Close();

			// TODO: recycle existing msg

			msg.m_data = result;
			msg.m_bitLength = unEncLenBits;
			msg.m_readPosition = 0;

			return true;
		}
	}
}
