using GostCryptography.Properties;
using System;
using System.Text;

namespace GostCryptography.Asn1.Ber
{
    [Serializable]
    public class Asn1BmpString : Asn1CharString
    {
        public static readonly Asn1Tag Tag = new Asn1Tag(0, 0, BmpStringTypeCode);

        public Asn1BmpString()
            : base(BmpStringTypeCode)
        {
        }

        public Asn1BmpString(string data)
            : base(data, BmpStringTypeCode)
        {
        }

        public override void Decode(Asn1BerDecodeBuffer buffer, bool explicitTagging, int implicitLength)
        {
            var elemLength = explicitTagging ? MatchTag(buffer, Tag) : implicitLength;
            var len = elemLength;
            var sb = new StringBuilder();

            var lastTag = buffer.LastTag;

            if ((lastTag == null) || !lastTag.Constructed)
            {
                sb.EnsureCapacity(elemLength / 2);
                ReadSegment(buffer, sb, len);
            }
            else
            {
                var capacity = 0;
                var context = new Asn1BerDecodeContext(buffer, elemLength);

                while (!context.Expired())
                {
                    var num3 = MatchTag(buffer, Asn1OctetString.Tag);

                    if (num3 <= 0)
                    {
                        throw ExceptionUtility.CryptographicException(Resources.Asn1InvalidFormatOfConstructedValue, buffer.ByteCount);
                    }

                    capacity += num3;
                    sb.EnsureCapacity(capacity);
                    ReadSegment(buffer, sb, num3);
                }

                if (elemLength == Asn1Status.IndefiniteLength)
                {
                    MatchTag(buffer, Asn1Tag.Eoc);
                }
            }

            Value = sb.ToString();
            buffer.TypeCode = BmpStringTypeCode;
        }

        public override int Encode(Asn1BerEncodeBuffer buffer, bool explicitTagging)
        {
            var length = Value.Length;

            for (var i = length - 1; i >= 0; --i)
            {
                var num3 = Value[i];
                var num = num3 % 0x100;
                var num2 = num3 / 0x100;

                buffer.Copy((byte)num);
                buffer.Copy((byte)num2);
            }

            length *= 2;

            if (explicitTagging)
            {
                length += buffer.EncodeTagAndLength(Tag, length);
            }

            return length;
        }

        public override void Encode(Asn1BerOutputStream outs, bool explicitTagging)
        {
            outs.EncodeBmpString(Value, explicitTagging, Tag);
        }

        private static void ReadSegment(Asn1DecodeBuffer buffer, StringBuilder sb, int len)
        {
            if ((len % 2) != 0)
            {
                throw ExceptionUtility.CryptographicException(Resources.Asn1InvalidLengthException);
            }

            while (len > 0)
            {
                var num = buffer.Read();

                if (num == -1)
                {
                    throw ExceptionUtility.CryptographicException(Resources.Asn1EndOfBufferException, buffer.ByteCount);
                }

                var num2 = num * 0x100;
                len--;
                num = buffer.Read();

                if (num == -1)
                {
                    throw ExceptionUtility.CryptographicException(Resources.Asn1EndOfBufferException, buffer.ByteCount);
                }

                num2 += num;
                len--;
                sb.Append((char)num2);
            }
        }
    }
}
