context: /*
  * Copyright 2006 Abdulla G. Abdurakhmanov (abdulla.abdurakhmanov@gmail.com).
  * 
  * Licensed under the LGPL, Version 2 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  *      http://www.gnu.org/copyleft/lgpl.html
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * With any your questions welcome to my e-mail 
  * or blog at http://abdulla-a.blogspot.com.
  */
  using System;
  using System.Reflection;
  using System.Collections.Generic;
  using org.bn.utils;
  using org.bn.attributes;
  using org.bn.metadata;
  using org.bn.types;
  
  namespace org.bn.coders.ber
  {
  	
  	public class BERDecoder:Decoder
  	{
          protected internal virtual DecodedObject<int> decodeLength(System.IO.Stream stream)
          {
              return BERCoderUtils.decodeLength(stream);
          }
  
          public override DecodedObject<object> decodeTag(System.IO.Stream stream)
          {
              int result = 0;
              int bt = stream.ReadByte();
              if (bt == -1)
                  return null;
              result = bt;
              int len = 1;
              int tagValue = bt & 31;
              //bool isPrimitive = (bt & 0x20) == 0;
              if (tagValue == UniversalTags.LastUniversal)
              {
                  bt = 0x80;
                  while ((bt & 0x80) != 0 && len < 4)
                  {
                      result <<= 8;
                      bt = stream.ReadByte();
                      if (bt >= 0)
                      {
                          result |= bt;
                          len++;
                      }
                      else
                      {
                          result >>= 8;
                          break;
                      }
                  }
              }
  
              return new DecodedObject<object>(result, len);
          }
  
          protected bool checkTagForObject(DecodedObject<object> decodedTag, int tagClass, int elementType, int universalTag, ElementInfo elementInfo)
          {
              if (decodedTag == null)
                  return false;
              int definedTag = BERCoderUtils.getTagValueForElement(elementInfo, tagClass, elementType, universalTag).Value;
              return definedTag == (int)decodedTag.Value;
          }
  
  		public override DecodedObject<object> decodeSequence(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
  		{
              bool isSet = false;
              if (!CoderUtils.isSequenceSet(elementInfo))
              {
                  if (!checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Constructed, UniversalTags.Sequence, elementInfo))
                  {
                      return null;
                  }
              }
              else
              {
                  if (checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Constructed, UniversalTags.Set, elementInfo))
                  {
                      isSet = true;
                  }
                  else
                      return null;
              }
  			DecodedObject<int> len = decodeLength(stream);
              int saveMaxAvailableLen = elementInfo.MaxAvailableLen;
              elementInfo.MaxAvailableLen = (len.Value);
  
              DecodedObject<object> result = null;
              if(!isSet)
  			    result =base.decodeSequence(decodedTag, objectClass, elementInfo, stream);
              else
                  result = decodeSet(decodedTag, objectClass, elementInfo, len.Value, stream);
  			if (result.Size != len.Value)
                  throw new System.ArgumentException("Sequence '" + objectClass.ToString() + "' size is incorrect!");
  			result.Size = result.Size + len.Size;
              elementInfo.MaxAvailableLen = (saveMaxAvailableLen);
  			return result;
  		}
  
          protected virtual DecodedObject<object> decodeSet(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, int len, System.IO.Stream stream)
          {
              object sequence = createInstanceForElement(objectClass,elementInfo);
              initDefaultValues(sequence);
              DecodedObject<object> fieldTag = null;
              int sizeOfSequence = 0;
              int maxSeqLen = elementInfo.MaxAvailableLen;
  
              if (maxSeqLen == -1 || maxSeqLen > 0)
              {
                  fieldTag = decodeTag(stream);
                  if (fieldTag != null)
                      sizeOfSequence += fieldTag.Size;
              }
              PropertyInfo[] fields =
                  elementInfo.getProperties(objectClass);
  
              bool fieldEncoded = false;
              do
              {
                  for (int i = 0; i < fields.Length; i++)
                  {
                      PropertyInfo field = fields[i];
                      DecodedObject<object> obj = decodeSequenceField(
                          fieldTag, sequence, i, field, stream, elementInfo, false
                      );
                      if (obj != null)
                      {
                          fieldEncoded = true;
                          sizeOfSequence += obj.Size;
  
                          bool isAny = false;
                          if (i + 1 == fields.Length - 1)
                          {
                              ElementInfo info = new ElementInfo();
                              info.AnnotatedClass = (fields[i + 1]);
                              info.MaxAvailableLen = (elementInfo.MaxAvailableLen);
                              if (elementInfo.hasPreparedInfo())
                              {
                                  info.PreparedInfo = (elementInfo.PreparedInfo.getPropertyMetadata(i + 1));
                              }
                              else
                                  info.ASN1ElementInfo = CoderUtils.getAttribute<ASN1Element>(fields[i + 1]);
                              isAny = CoderUtils.isAnyField(fields[i + 1], info);
                          }
  
                          if (maxSeqLen != -1)
                          {
                              elementInfo.MaxAvailableLen = (maxSeqLen - sizeOfSequence);
                          }
  
                          if (!isAny)
                          {
                              if (i < fields.Length - 1)
                              {
                                  if (maxSeqLen == -1 || elementInfo.MaxAvailableLen > 0)
                                  {
                                      fieldTag = decodeTag(stream);
                                      if (fieldTag != null)
                                          sizeOfSequence += fieldTag.Size;
                                      else
                                      {
                                          break;
                                      }
                                  }
                                  else
                                      fieldTag = null;
                              }
                              else
                                  break;
                          }
                      }
                      ;
                  }
              }
              while (sizeOfSequence < len && fieldEncoded);
              return new DecodedObject<object>(sequence, sizeOfSequence);
          }
  
          public override DecodedObject<object> decodeChoice(DecodedObject<object> decodedTag, Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
          {
              if ( (elementInfo.hasPreparedInfo() && elementInfo.hasPreparedASN1ElementInfo() && elementInfo.PreparedASN1ElementInfo.HasTag)
                  || (elementInfo.ASN1ElementInfo != null && elementInfo.ASN1ElementInfo.HasTag)) 
              {
                  if (!checkTagForObject(decodedTag, TagClasses.ContextSpecific, ElementType.Constructed, UniversalTags.LastUniversal, elementInfo))
                      return null;
                  DecodedObject<int> len =  decodeLength(stream);
                  DecodedObject<object> childDecodedTag = decodeTag(stream);
                  DecodedObject<object> result = base.decodeChoice(childDecodedTag, objectClass, elementInfo, stream);
                  result.Size += len.Size + childDecodedTag.Size;
                  return result;
              }
              else
                  return base.decodeChoice(decodedTag, objectClass, elementInfo, stream);
          }
  
  		
  		
  		public override DecodedObject<object> decodeEnumItem(DecodedObject<object> decodedTag, System.Type objectClass, System.Type enumClass, ElementInfo elementInfo, System.IO.Stream stream)
  		{
  			if (!checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Primitive, UniversalTags.Enumerated, elementInfo))
  				return null;
  			return decodeIntegerValue(stream);
  		}
  		
  		public override DecodedObject<object> decodeBoolean(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
  		{
  			if (!checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Primitive, UniversalTags.Boolean, elementInfo))
  				return null;
  			DecodedObject<object> result = decodeIntegerValue(stream);
  			int val = (int) result.Value;
  			if (val != 0)
  				result.Value  = true;
  			else
  				result.Value = false;
  			return result;
  		}
  		
  		public override DecodedObject<object> decodeAny(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
  		{
              int bufSize = elementInfo.MaxAvailableLen;
              if (bufSize == 0)
                  return null; 
              System.IO.MemoryStream anyStream = new System.IO.MemoryStream(1024);
              /*int tagValue = (int)decodedTag.Value;
              for (int i = 0; i < decodedTag.Size; i++)
              {
                  anyStream.WriteByte((byte)tagValue);
                  tagValue = tagValue >> 8;
              }*/
  
              if(bufSize<0)
                  bufSize = 1024;
              int len = 0;
              if (bufSize > 0)
              {
                  byte[] buffer = new byte[bufSize];
                  int readed = stream.Read(buffer, 0, buffer.Length);
                  while (readed > 0)
                  {
                      anyStream.Write(buffer, 0, readed);
                      len += readed;
                      if (elementInfo.MaxAvailableLen > 0)
                          break;
                      readed = stream.Read(buffer, 0, buffer.Length);
                  }
              }
              CoderUtils.checkConstraints(len, elementInfo);
  			return new DecodedObject<object>(anyStream.ToArray(), len);
  		}
  		
  		public override DecodedObject<object> decodeNull(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
  		{
  			if (!checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Primitive, UniversalTags.Null, elementInfo))
  				return null;
  			stream.ReadByte(); // ignore null length
              object obj = createInstanceForElement(objectClass, elementInfo);
  			DecodedObject<object> result = new DecodedObject<object>(obj, 1);
  			return result;
  		}
  		
  		public override DecodedObject<object> decodeInteger(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
  		{
  			if (!checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Primitive, UniversalTags.Integer, elementInfo))
  				return null;
              if (objectClass.Equals(typeof(int)))
              {
                  DecodedObject<object> result = decodeIntegerValue(stream);
                  CoderUtils.checkConstraints((int)result.Value, elementInfo);
                  return result;
              }
              else
              {
                  DecodedObject<object> result = decodeLongValue(stream);
                  CoderUtils.checkConstraints((long)result.Value, elementInfo);
                  return result;
              }
  		}
  
          public override DecodedObject<object> decodeReal(DecodedObject<object> decodedTag, System.Type objectClass, ElementInfo elementInfo, System.IO.Stream stream)
          {
              if (!checkTagForObject(decodedTag, TagClasses.Universal, ElementType.Primitive, UniversalTags.Real, elementInfo))
                  return null;
              DecodedObject<int> len = decodeLength(stream);
              int realPreamble = stream.ReadByte();
  
              Double result = 0.0D;
              int szResult = len.Value;
              if ((realPreamble & 0x40) == 1)
              {
                  // 01000000 Value is PLUS-INFINITY
                  result = Double.PositiveInfinity;
              }
              if ((realPreamble & 0x41) == 1)
              {
                  // 01000001 Value is MINUS-INFINITY
                  result = Double.NegativeInfinity;
                  szResult += 1;
              }
              else
                  if (len.Value > 0)
                  {
  
gt:                     int szOfExp = 1 + (realPreamble & 0x3);