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

namespace GostCryptography.Asn1.Ber
{
    [Serializable]
    public class Asn1OctetString : Asn1Type, IComparable
    {
        public static readonly Asn1Tag Tag = new Asn1Tag(0, 0, OctetStringTypeCode);

        [NonSerialized]
        public byte[] Value;

        public Asn1OctetString()
        {
            Value = null;
        }

        public Asn1OctetString(byte[] data)
        {
            Value = data;
        }

        public Asn1OctetString(string value)
        {
            Value = string.IsNullOrEmpty(value) ? new byte[0] : Asn1Value.ParseString(value);
        }

        public Asn1OctetString(byte[] data, int offset, int nbytes)
        {
            Value = new byte[nbytes];

            if (data != null)
            {
                Array.Copy(data, offset, Value, 0, nbytes);
            }
        }

        public override int Length
        {
            get { return Value.Length; }
        }

        public virtual int CompareTo(object octstr)
        {
            var value = ((Asn1OctetString)octstr).Value;
            var num = (Value.Length < value.Length) ? Value.Length : value.Length;

            for (var i = 0; i < num; i++)
            {
                var num2 = Value[i] & 0xff;
                var num3 = value[i] & 0xff;

                if (num2 < num3)
                {
                    return -1;
                }

                if (num2 > num3)
                {
                    return 1;
                }
            }

            if (Value.Length == value.Length)
            {
                return 0;
            }

            if (Value.Length < value.Length)
            {
                return -1;
            }

            return 1;
        }

        private void AllocByteArray(int nbytes)
        {
            if (Value == null)
            {
                Value = new byte[nbytes];
            }
        }

        public override void Decode(Asn1BerDecodeBuffer buffer, bool explicitTagging, int implicitLength)
        {
            var elemLength = explicitTagging ? MatchTag(buffer, Tag) : implicitLength;
            var lastTag = buffer.LastTag;

            if ((lastTag == null) || !lastTag.Constructed)
            {
                if (elemLength < 0)
                {
                    throw ExceptionUtility.CryptographicException(Resources.Asn1InvalidLengthException);
                }

                Value = new byte[elemLength];

                if (elemLength != 0)
                {
                    buffer.Read(Value);
                }
            }
            else
            {
                var nbytes = 0;
                var offset = 0;
                var context = new Asn1BerDecodeContext(buffer, elemLength);

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

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

                    nbytes += num2;

                    if (offset == 0)
                    {
                        Value = new byte[nbytes];
                    }
                    else
                    {
                        ReAllocByteArray(nbytes);
                    }

                    buffer.Read(Value, offset, num2);
                    offset = nbytes;
                }

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

            buffer.TypeCode = OctetStringTypeCode;
        }

        public override int Encode(Asn1BerEncodeBuffer buffer, bool explicitTagging)
        {
            if (Value == null)
            {
                Value = new byte[0];
            }

            var length = Value.Length;

            if (length != 0)
            {
                buffer.Copy(Value);
            }

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

            return length;
        }

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

        public bool Equals(byte[] value)
        {
            if (value.Length != Value.Length)
            {
                return false;
            }

            for (var i = 0; i < value.Length; i++)
            {
                if (value[i] != Value[i])
                {
                    return false;
                }
            }

            return true;
        }

        public override bool Equals(object value)
        {
            var str = value as Asn1OctetString;

            return (str != null) && Equals(str.Value);
        }

        public override int GetHashCode()
        {
            return (Value != null) ? Value.GetHashCode() : base.GetHashCode();
        }

        private void ReAllocByteArray(int nbytes)
        {
            var value = Value;
            Value = new byte[nbytes];

            if (value != null)
            {
                Array.Copy(value, 0, Value, 0, value.Length);
            }
        }

        public virtual Stream ToInputStream()
        {
            return new MemoryStream(Value, 0, Value.Length);
        }

        public override string ToString()
        {
            var str = new StringBuilder("").ToString();

            if (Value != null)
            {
                foreach (var b in Value)
                {
                    str = str + Asn1Util.ToHexString(b);
                }
            }

            return str;
        }
    }
}
