﻿using System;
using System.IO;
using System.Text;

namespace GostCryptography.Asn1.Ber
{
	public class Asn1BerEncodeBuffer : Asn1EncodeBuffer
	{
		public Asn1BerEncodeBuffer()
		{
			ByteIndex = SizeIncrement - 1;
		}

		public Asn1BerEncodeBuffer(int sizeIncrement)
			: base(sizeIncrement)
		{
			ByteIndex = SizeIncrement - 1;
		}

		public virtual MemoryStream ByteArrayInputStream
		{
			get
			{
				var index = ByteIndex + 1;
				return new MemoryStream(Data, index, Data.Length - index);
			}
		}

		public override byte[] MsgCopy
		{
			get
			{
				var sourceIndex = ByteIndex + 1;
				var length = Data.Length - sourceIndex;
				var destinationArray = new byte[length];

				Array.Copy(Data, sourceIndex, destinationArray, 0, length);

				return destinationArray;
			}
		}

		public override int MsgLength
		{
			get
			{
				var num = ByteIndex + 1;
				return (Data.Length - num);
			}
		}

		public virtual void BinDump()
		{
			BinDump(null);
		}

		public override void BinDump(StreamWriter outs, string varName)
		{
			var buffer = new Asn1BerDecodeBuffer(ByteArrayInputStream);

			try
			{
				buffer.Parse(new Asn1BerMessageDumpHandler(outs));
			}
			catch (Exception exception)
			{
				Console.Out.WriteLine(exception.Message);
				Console.Error.Write(exception.StackTrace);
				Console.Error.Flush();
			}
		}

		protected internal override void CheckSize(int bytesRequired)
		{
			if (bytesRequired > (ByteIndex + 1))
			{
				var num = ((bytesRequired - 1) / SizeIncrement) + 1;
				var num2 = num * SizeIncrement;
				var destinationArray = new byte[Data.Length + num2];
				var destinationIndex = (ByteIndex + num2) + 1;
				var length = Data.Length - (ByteIndex + 1);

				Array.Copy(Data, ByteIndex + 1, destinationArray, destinationIndex, length);

				Data = destinationArray;
				ByteIndex = destinationIndex - 1;
			}
		}

		public override void Copy(byte data)
		{
			if (ByteIndex < 0)
			{
				CheckSize(1);
			}

			Data[ByteIndex--] = data;
		}

		public override void Copy(byte[] data)
		{
			CheckSize(data.Length);
			ByteIndex -= data.Length;

			Array.Copy(data, 0, Data, ByteIndex + 1, data.Length);
		}

		public virtual void Copy(string data)
		{
			var length = data.Length;
			CheckSize(length);
			ByteIndex -= length;

			for (var i = 0; i < length; ++i)
			{
				Data[(ByteIndex + i) + 1] = (byte)data[i];
			}
		}

		public virtual void Copy(byte[] data, int startOffset, int length)
		{
			CheckSize(length);
			ByteIndex -= length;

			Array.Copy(data, startOffset, Data, ByteIndex + 1, length);
		}

		public virtual int EncodeIdentifier(int ident)
		{
			var flag = true;
			var num = 0;
			var num2 = ident;

			do
			{
				if (ByteIndex < 0)
				{
					CheckSize(1);
				}

				Data[ByteIndex] = (byte)(num2 % 0x80);

				if (!flag)
				{
					Data[ByteIndex] = (byte)(Data[ByteIndex] | 0x80);
				}
				else
				{
					flag = false;
				}

				ByteIndex--;
				num2 /= 0x80;
				num++;

			}
			while (num2 > 0);

			return num;
		}

		public virtual int EncodeIntValue(long ivalue)
		{
			long num2;
			long num = ivalue;

			var num3 = 0;

			do
			{
				num2 = num % 0x100L;
				num /= 0x100L;

				if ((num < 0L) && (num2 != 0L))
				{
					num -= 1L;
				}

				Copy((byte)num2);

				num3++;
			}
			while ((num != 0L) && (num != -1L));

			if ((ivalue > 0L) && ((num2 & 0x80L) == 0x80L))
			{
				Copy(0);
				num3++;
				return num3;
			}

			if ((ivalue < 0L) && ((num2 & 0x80L) == 0L))
			{
				Copy(0xff);
				num3++;
			}

			return num3;
		}

		public virtual int EncodeLength(int len)
		{
			var num = 0;

			bool flag;

			if (len >= 0)
			{
				flag = len > 0x7f;

				var num2 = len;

				do
				{
					if (ByteIndex < 0)
					{
						CheckSize(1);
					}

					Data[ByteIndex--] = (byte)(num2 % 0x100);
					num++;
					num2 /= 0x100;
				}
				while (num2 > 0);
			}
			else
			{
				flag = len == Asn1Status.IndefiniteLength;
			}

			if (flag)
			{
				if (ByteIndex < 0)
				{
					CheckSize(1);
				}

				Data[ByteIndex--] = (byte)(num | 0x80);
				num++;
			}

			return num;
		}

		public virtual int EncodeTag(Asn1Tag tag)
		{
			var num = (byte)(((byte)tag.Class) | ((byte)tag.Form));
			var num2 = 0;

			if (tag.IdCode < 0x1f)
			{
				Copy((byte)(num | tag.IdCode));
				num2++;
				return num2;
			}

			num2 += EncodeIdentifier(tag.IdCode);
			Copy((byte)(num | 0x1f));
			num2++;

			return num2;
		}

		public virtual int EncodeTagAndLength(Asn1Tag tag, int len)
		{
			return (EncodeLength(len) + EncodeTag(tag));
		}

		public virtual int EncodeTagAndLength(short tagClass, short tagForm, int tagIdCode, int len)
		{
			var tag = new Asn1Tag(tagClass, tagForm, tagIdCode);
			return EncodeTagAndLength(tag, len);
		}

		public override Stream GetInputStream()
		{
			return ByteArrayInputStream;
		}

		public override void Reset()
		{
			ByteIndex = Data.Length - 1;
		}

		public override string ToString()
		{
			var num = ByteIndex + 1;
			var num2 = Data.Length - num;
			var str = new StringBuilder("").ToString();

			for (var i = 0; i < num2; ++i)
			{
				str = str + Asn1Util.ToHexString(Data[i + num]);
			}

			return str;
		}

		public override void Write(Stream outs)
		{
			var offset = ByteIndex + 1;
			outs.Write(Data, offset, Data.Length - offset);
		}
	}
}