using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Xml;

namespace System.Runtime.Serialization.Json
{
	internal partial class JsonFormatReaderGenerator
	{
		partial class CriticalHelper
		{
			internal JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
			{
				return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDictionaryString, XmlDictionaryString [] memberNames) => new JsonFormatReaderInterpreter (classContract).ReadFromJson (xr, ctx, emptyDictionaryString, memberNames);
			}

			internal JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
			{
				return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDS, XmlDictionaryString inm, CollectionDataContract cc) => new JsonFormatReaderInterpreter (collectionContract, false).ReadCollectionFromJson (xr, ctx, emptyDS, inm, cc);
			}
			
			internal JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
			{
				return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDS, XmlDictionaryString inm, CollectionDataContract cc) => new JsonFormatReaderInterpreter (collectionContract, true).ReadGetOnlyCollectionFromJson (xr, ctx, emptyDS, inm, cc);
			}
		}
	}

	class JsonFormatReaderInterpreter
	{
		public JsonFormatReaderInterpreter (ClassDataContract classContract)
		{
			this.classContract = classContract;
		}

		public JsonFormatReaderInterpreter (CollectionDataContract collectionContract, bool isGetOnly)
		{
			this.collectionContract = collectionContract;
			this.is_get_only_collection = isGetOnly;
		}

		bool is_get_only_collection;

		ClassDataContract classContract;

		CollectionDataContract collectionContract;

		object objectLocal;
		Type objectType;
		XmlReaderDelegator xmlReader;
		XmlObjectSerializerReadContextComplexJson context;

		XmlDictionaryString [] memberNames = null;
		XmlDictionaryString emptyDictionaryString = null;
		XmlDictionaryString itemName = null;
		XmlDictionaryString itemNamespace = null;

		public object ReadFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString[] memberNames)
		{
			// InitArgs()
			this.xmlReader = xmlReader;
			this.context = context;
			this.emptyDictionaryString = emptyDictionaryString;
			this.memberNames = memberNames;
			
			//DemandSerializationFormatterPermission(classContract);
			//DemandMemberAccessPermission(memberAccessFlag);
			CreateObject (classContract);
			
			context.AddNewObject (objectLocal);
			InvokeOnDeserializing (classContract);
            
            string objectId = null;
            
			if (classContract.IsISerializable)
				ReadISerializable (classContract);
			else
				ReadClass (classContract);
			if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom (classContract.UnderlyingType))
				((IDeserializationCallback) objectLocal).OnDeserialization (null);
			InvokeOnDeserialized(classContract);
			if (!InvokeFactoryMethod (classContract)) {

				// Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
				// DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
				// on DateTimeOffset; which does not work in partial trust.

				if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
					objectLocal = DateTimeOffsetAdapter.GetDateTimeOffset ((DateTimeOffsetAdapter) objectLocal);
				// else - do we have to call CodeInterpreter.ConvertValue()? I guess not...
			}
			return objectLocal;
		}
		
		public object ReadCollectionFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract)
		{
			#region GenerateCollectionReaderHelper
			// InitArgs()
			this.xmlReader = xmlReader;
			this.context = context;
			this.emptyDictionaryString = emptyDictionaryString;
			this.itemName = itemName;

			this.collectionContract = collectionContract;

			#endregion

			ReadCollection (collectionContract);

			return objectLocal;
		}
		
		public void ReadGetOnlyCollectionFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract)
		{
			#region GenerateCollectionReaderHelper
			// InitArgs()
			this.xmlReader = xmlReader;
			this.context = context;
			this.emptyDictionaryString = emptyDictionaryString;
			this.itemName = itemName;

			this.collectionContract = collectionContract;

			#endregion

			ReadGetOnlyCollection (collectionContract);
		}

		void CreateObject (ClassDataContract classContract)
		{
			Type type = objectType = classContract.UnderlyingType;
			if (type.IsValueType && !classContract.IsNonAttributedType)
				type = Globals.TypeOfValueType;

			if (classContract.UnderlyingType == Globals.TypeOfDBNull)
				objectLocal = DBNull.Value;
			else if (classContract.IsNonAttributedType) {
				if (type.IsValueType)
					objectLocal = FormatterServices.GetUninitializedObject (type);
				else
					objectLocal = classContract.GetNonAttributedTypeConstructor ().Invoke (new object [0]);
			}
			else
				objectLocal = CodeInterpreter.ConvertValue (XmlFormatReaderGenerator.UnsafeGetUninitializedObject (DataContract.GetIdForInitialization (classContract)), Globals.TypeOfObject, type);
		}

		void InvokeOnDeserializing (ClassDataContract classContract)
		{
			if (classContract.BaseContract != null)
				InvokeOnDeserializing (classContract.BaseContract);
			if (classContract.OnDeserializing != null)
				classContract.OnDeserializing.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
		}

		void InvokeOnDeserialized (ClassDataContract classContract)
		{
			if (classContract.BaseContract != null)
				InvokeOnDeserialized (classContract.BaseContract);
			if (classContract.OnDeserialized != null)
				classContract.OnDeserialized.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
		}

		bool HasFactoryMethod (ClassDataContract classContract)
		{
			return Globals.TypeOfIObjectReference.IsAssignableFrom (classContract.UnderlyingType);
		}

		bool InvokeFactoryMethod (ClassDataContract classContract)
		{
			if (HasFactoryMethod (classContract)) {
				objectLocal = CodeInterpreter.ConvertValue (context.GetRealObject ((IObjectReference) objectLocal, Globals.NewObjectId), Globals.TypeOfObject, classContract.UnderlyingType);
				return true;
			}
			return false;
		}

		void ReadISerializable (ClassDataContract classContract)
		{
			ConstructorInfo ctor = classContract.UnderlyingType.GetConstructor (Globals.ScanAllMembers, null, JsonFormatGeneratorStatics.SerInfoCtorArgs, null);
			if (ctor == null)
				throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError (XmlObjectSerializer.CreateSerializationException (SR.GetString (SR.SerializationInfo_ConstructorNotFound, DataContract.GetClrTypeFullName (classContract.UnderlyingType))));
			context.ReadSerializationInfo (xmlReader, classContract.UnderlyingType);
			ctor.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
		}

		void ReadClass (ClassDataContract classContract)
		{
			if (classContract.HasExtensionData) {
				ExtensionDataObject extensionData = new ExtensionDataObject ();
				ReadMembers (classContract, extensionData);
				ClassDataContract currentContract = classContract;
				while (currentContract != null) {
					MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
					if (extensionDataSetMethod != null)
						extensionDataSetMethod.Invoke (objectLocal, new object [] {extensionData});
					currentContract = currentContract.BaseContract;
				}
			}
			else
				ReadMembers (classContract, null);
		}

		void ReadMembers (ClassDataContract classContract, ExtensionDataObject  extensionData)
		{
			int memberCount = classContract.MemberNames.Length;
			context.IncrementItemCount (memberCount);

			int memberIndex = -1;
			
			// JSON intrinsic part.
			BitFlagsGenerator expectedElements = new BitFlagsGenerator (memberCount);
			byte [] requiredElements = new byte [expectedElements.GetLocalCount ()];
			SetRequiredElements (classContract, requiredElements);
			SetExpectedElements (expectedElements, 0 /*startIndex*/);

			while (XmlObjectSerializerReadContext.MoveToNextElement (xmlReader)) {
				int idx; // used as in "switch (idx)" in the original source.
				idx = context.GetJsonMemberIndex (xmlReader, memberNames, memberIndex, extensionData);

				if (memberCount > 0)
					ReadMembers (idx, classContract, expectedElements, ref memberIndex);
			}

			if (!CheckRequiredElements (expectedElements, requiredElements))
				XmlObjectSerializerReadContextComplexJson.ThrowMissingRequiredMembers (objectLocal, memberNames, expectedElements.LoadArray (), requiredElements);
		}

		int ReadMembers (int index, ClassDataContract classContract, BitFlagsGenerator expectedElements, ref int memberIndex)
		{
			int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers (index, classContract.BaseContract, expectedElements,
			ref memberIndex);
			
			if (memberCount <= index && index < memberCount + classContract.Members.Count) {
				DataMember dataMember = classContract.Members [index - memberCount];
				Type memberType = dataMember.MemberType;
				
				memberIndex = memberCount;
				if (!expectedElements.Load (index))
					XmlObjectSerializerReadContextComplexJson.ThrowDuplicateMemberException (objectLocal, memberNames, memberIndex);

				if (dataMember.IsGetOnlyCollection) {
					var value = CodeInterpreter.GetMember (dataMember.MemberInfo, objectLocal);
					context.StoreCollectionMemberInfo (value);
					ReadValue (memberType, dataMember.Name);
				} else {
					var value = ReadValue (memberType, dataMember.Name);
					CodeInterpreter.SetMember (dataMember.MemberInfo, objectLocal, value);
				}
				memberIndex = index;
				ResetExpectedElements (expectedElements, index);
			}
			return memberCount + classContract.Members.Count;
		}

		bool CheckRequiredElements (BitFlagsGenerator expectedElements, byte [] requiredElements)
		{
			for (int i = 0; i < requiredElements.Length; i++)
				if ((expectedElements.GetLocal(i) & requiredElements[i]) != 0)
					return false;
			return true;
		}

		int SetRequiredElements (ClassDataContract contract, byte [] requiredElements)
		{
			int memberCount = (contract.BaseContract == null) ? 0 :
			SetRequiredElements (contract.BaseContract, requiredElements);
			List<DataMember> members = contract.Members;
			for (int i = 0; i < members.Count; i++, memberCount++) {
				if (members[i].IsRequired)
					BitFlagsGenerator.SetBit (requiredElements, memberCount);
			}
			return memberCount;
		}

		void SetExpectedElements (BitFlagsGenerator expectedElements, int startIndex)
		{
			int memberCount = expectedElements.GetBitCount ();
			for (int i = startIndex; i < memberCount; i++)
				expectedElements.Store (i, true);
		}

		void ResetExpectedElements (BitFlagsGenerator expectedElements, int index)
		{
			expectedElements.Store (index, false);
		}

		object ReadValue (Type type, string name)
		{
			var valueType = type;
			object value = null;
			bool shouldAssignNullableValue = false;
			int nullables = 0;
			while (type.IsGenericType && type.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
				nullables++;
				type = type.GetGenericArguments () [0];
			}
			
			PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (type);
			if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType) {
				context.ReadAttributes (xmlReader);
				string objectId = context.ReadIfNullOrRef (xmlReader, type, DataContract.IsTypeSerializable (type));
				// Deserialize null
                if (objectId == Globals.NullObjectId) {
					
					if (nullables != 0)
						value = Activator.CreateInstance (valueType);
					else if (type.IsValueType)
						throw new SerializationException (SR.GetString (SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName (type)));
					else
						value = null;
				} else if (objectId == string.Empty) {
					// Deserialize value

					// Compare against Globals.NewObjectId, which is set to string.Empty
					
					objectId = context.GetObjectId ();
					
					if (type.IsValueType) {
						if (!string.IsNullOrEmpty (objectId))
							throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
					}
					object innerValueRead = null;
					if (nullables != 0)
						shouldAssignNullableValue = true;

					if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
						value = primitiveContract.XmlFormatReaderMethod.Invoke (xmlReader, new object [0]);
						if (!type.IsValueType)
							context.AddNewObject (value);
					}
					else
							value = InternalDeserialize (type, name);
				} else {
					// Deserialize ref
					if (type.IsValueType)
						throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName (type)));
					else
						value = CodeInterpreter.ConvertValue (context.GetExistingObject (objectId, type, name, string.Empty), Globals.TypeOfObject, type);
				}

				if (shouldAssignNullableValue) {
					if (objectId != Globals.NullObjectId)
						value = WrapNullableObject (type, value, valueType, nullables);
				}
			}
			else
				value = InternalDeserialize (type, name);

			return value;
		}

		object InternalDeserialize (Type type, string name)
		{
			Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
			var obj = context.InternalDeserialize (xmlReader, DataContract.GetId (declaredType.TypeHandle), declaredType.TypeHandle, name, string.Empty);

			if (type.IsPointer)
				// wow, there is no way to convert void* to object in strongly typed way...
				return JsonFormatGeneratorStatics.UnboxPointer.Invoke (null, new object [] {obj});
			else
				return CodeInterpreter.ConvertValue (obj, Globals.TypeOfObject, type);
		}

		object WrapNullableObject (Type innerType, object innerValue, Type outerType, int nullables)
		{
			var outerValue = innerValue;
			for (int i = 1; i < nullables; i++) {
				Type type = Globals.TypeOfNullable.MakeGenericType (innerType);
				outerValue = Activator.CreateInstance (type, new object[] { outerValue });
				innerType = type;
			}
			return Activator.CreateInstance (outerType, new object[] { outerValue });
		}


		void ReadCollection (CollectionDataContract collectionContract)
		{
			Type type = collectionContract.UnderlyingType;
			Type itemType = collectionContract.ItemType;
			bool isArray = (collectionContract.Kind == CollectionKind.Array);

			ConstructorInfo constructor = collectionContract.Constructor;

			if (type.IsInterface) {
				switch (collectionContract.Kind) {
				case CollectionKind.GenericDictionary:
					type = Globals.TypeOfDictionaryGeneric.MakeGenericType (itemType.GetGenericArguments ());
					constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
					break;
				case CollectionKind.Dictionary:
					type = Globals.TypeOfHashtable;
					constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
					break;
				case CollectionKind.Collection:
				case CollectionKind.GenericCollection:
				case CollectionKind.Enumerable:
				case CollectionKind.GenericEnumerable:
				case CollectionKind.List:
				case CollectionKind.GenericList:
					type = itemType.MakeArrayType ();
					isArray = true;
					break;
				}
			}

			if (!isArray) {
				if (type.IsValueType)
					// FIXME: this is not what the original code does.
					objectLocal = FormatterServices.GetUninitializedObject (type);
				else {
					objectLocal = constructor.Invoke (new object [0]);
					context.AddNewObject (objectLocal);
				}
			}

			bool canReadSimpleDictionary = collectionContract.Kind == CollectionKind.Dictionary ||
			collectionContract.Kind == CollectionKind.GenericDictionary;

			bool readSimpleDictionary = canReadSimpleDictionary & context.UseSimpleDictionaryFormat;
			if (readSimpleDictionary)
				ReadSimpleDictionary (collectionContract, itemType);
			else {	 
				string objectId = context.GetObjectId ();

				bool canReadPrimitiveArray = false, readResult = false;
				if (isArray && TryReadPrimitiveArray (itemType, out readResult))
					canReadPrimitiveArray = true;

				if (!canReadPrimitiveArray) {
					object growingCollection = null;
					if (isArray)
						growingCollection = Array.CreateInstance (itemType, 32);

					int i = 0;
					// FIXME: I cannot find i++ part, but without that it won't work as expected.
					for (; i < int.MaxValue; i++) {
						if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
							context.IncrementItemCount (1);
							object value = ReadCollectionItem (collectionContract, itemType);
							if (isArray) {
								MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod (itemType);
								growingCollection = ensureArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
								((Array) growingCollection).SetValue (value, i);
							} else {
								StoreCollectionValue (objectLocal, itemType, value, collectionContract);
							}
						}
						else if (IsEndElement ())
							break;
						else
							HandleUnexpectedItemInCollection (ref i);
					}

					if (isArray) {
						MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod (itemType);
						objectLocal = trimArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
						context.AddNewObjectWithId (objectId, objectLocal);
					}
				}
				else
					context.AddNewObjectWithId (objectId, objectLocal);
			}
		}

		void ReadSimpleDictionary (CollectionDataContract collectionContract, Type keyValueType)
		{
			Type[] keyValueTypes = keyValueType.GetGenericArguments ();
			Type keyType = keyValueTypes [0];
			Type valueType = keyValueTypes [1];

			int keyTypeNullableDepth = 0;
			Type keyTypeOriginal = keyType;
			while (keyType.IsGenericType && keyType.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
				keyTypeNullableDepth++;
				keyType = keyType.GetGenericArguments () [0];
			}

			ClassDataContract keyValueDataContract = (ClassDataContract)collectionContract.ItemContract;
			DataContract keyDataContract = keyValueDataContract.Members [0].MemberTypeContract;

			KeyParseMode keyParseMode = KeyParseMode.Fail;

			if (keyType == Globals.TypeOfString || keyType == Globals.TypeOfObject) {
				keyParseMode = KeyParseMode.AsString;
			} else if (keyType.IsEnum) {
				keyParseMode = KeyParseMode.UsingParseEnum;
			} else if (keyDataContract.ParseMethod != null) {
				keyParseMode = KeyParseMode.UsingCustomParse;
			}

			if (keyParseMode == KeyParseMode.Fail) {
				ThrowSerializationException (
				SR.GetString (
				SR.KeyTypeCannotBeParsedInSimpleDictionary,
				DataContract.GetClrTypeFullName (collectionContract.UnderlyingType),
				DataContract.GetClrTypeFullName (keyType)));
			} else {
				XmlNodeType nodeType;

				while ((nodeType = xmlReader.MoveToContent ()) != XmlNodeType.EndElement) {
					if (nodeType != XmlNodeType.Element)
						ThrowUnexpectedStateException (XmlNodeType.Element);

					context.IncrementItemCount (1);

					var jsonMemberName = XmlObjectSerializerReadContextComplexJson.GetJsonMemberName (xmlReader);
					object key = null;

					if (keyParseMode == KeyParseMode.UsingParseEnum)
						key = Enum.Parse (keyType, jsonMemberName);
					else if (keyParseMode == KeyParseMode.UsingCustomParse)
						key = keyDataContract.ParseMethod.Invoke (null, new object [] {jsonMemberName});

					if (keyTypeNullableDepth > 0) {
						var keyOriginal = WrapNullableObject (keyType, key, valueType, keyTypeNullableDepth);
						key = keyOriginal;
					}

					var value = ReadValue (valueType, String.Empty);
					collectionContract.AddMethod.Invoke (objectLocal, new object[] {key, value});
				}
			}
		}

		void ReadGetOnlyCollection (CollectionDataContract collectionContract)
		{
			Type type = collectionContract.UnderlyingType;
			Type itemType = collectionContract.ItemType;
			bool isArray = (collectionContract.Kind == CollectionKind.Array);
			int size = 0;

			objectLocal = context.GetCollectionMember ();
 
			bool canReadSimpleDictionary = 
				collectionContract.Kind == CollectionKind.Dictionary ||
				collectionContract.Kind == CollectionKind.GenericDictionary;

			bool readSimple = canReadSimpleDictionary && context.UseSimpleDictionaryFormat;
			if (readSimple) {
				if (objectLocal == null)
					XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
				else {
					ReadSimpleDictionary(collectionContract, itemType);
					context.CheckEndOfArray (xmlReader, size, this.itemName, emptyDictionaryString);
				}
			} else {

				//check that items are actually going to be deserialized into the collection
				if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
					if (objectLocal == null)
						XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
					else {
						size = 0;
						if (isArray)
							size = ((Array) objectLocal).Length;
						for (int i = 0; i < int.MaxValue;) {
							if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
								context.IncrementItemCount (1);
								var value = ReadCollectionItem (collectionContract, itemType);
								if (isArray) {
									if (size == i)
										XmlObjectSerializerReadContext.ThrowArrayExceededSizeException (size, type);
									else
										((Array) objectLocal).SetValue (value, i);
								} else {
									StoreCollectionValue (objectLocal, itemType, value, collectionContract);
								}
							}
							else if (IsEndElement())
								break;
							else
								HandleUnexpectedItemInCollection (ref i);
						}
						context.CheckEndOfArray (xmlReader, size, this.itemName, this.emptyDictionaryString);
					}
				}
			}
		}

		bool TryReadPrimitiveArray (Type itemType, out bool readResult)
		{
			readResult = false;
			PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (itemType);
			if (primitiveContract == null)
				return false;

			string readArrayMethod = null;
			switch (Type.GetTypeCode (itemType))
			{
			case TypeCode.Boolean:
				readArrayMethod = "TryReadBooleanArray";
			break;
			case TypeCode.Decimal:
				readArrayMethod = "TryReadDecimalArray";
			break;
			case TypeCode.Int32:
				readArrayMethod = "TryReadInt32Array";
			break;
			case TypeCode.Int64:
				readArrayMethod = "TryReadInt64Array";
			break;
			case TypeCode.Single:
				readArrayMethod = "TryReadSingleArray";
			break;
			case TypeCode.Double:
				readArrayMethod = "TryReadDoubleArray";
				break;
			case TypeCode.DateTime:
				readArrayMethod = "TryReadJsonDateTimeArray";
			break;
			default:
				break;
			}
			if (readArrayMethod != null) {
				var mi = typeof (JsonReaderDelegator).GetMethod (readArrayMethod, Globals.ScanAllMembers);
				var args = new object [] {context, itemName, emptyDictionaryString, -1, objectLocal};
				readResult = (bool) mi.Invoke ((JsonReaderDelegator) xmlReader, args);
				objectLocal = args.Last ();
				return true;
			}
			return false;
		}

		object ReadCollectionItem (CollectionDataContract collectionContract, Type itemType)
		{
			if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) {
				context.ResetAttributes ();
				var revisedContract = XmlObjectSerializerWriteContextComplexJson.GetRevisedItemContract (collectionContract.ItemContract);
				var v = DataContractJsonSerializer.ReadJsonValue (revisedContract, xmlReader, context);
				return CodeInterpreter.ConvertValue (v, Globals.TypeOfObject, itemType);
			}
			else
				return ReadValue (itemType, JsonGlobals.itemString);
		}

		void StoreCollectionValue (object collection, Type valueType, object value, CollectionDataContract collectionContract)
		{
			if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) {
				ClassDataContract keyValuePairContract = DataContract.GetDataContract (valueType) as ClassDataContract;
				if (keyValuePairContract == null)
					Fx.Assert ("Failed to create contract for KeyValuePair type");
				DataMember keyMember = keyValuePairContract.Members [0];
				DataMember valueMember = keyValuePairContract.Members [1];
				object pkey = CodeInterpreter.GetMember (keyMember.MemberInfo, value);
				object pvalue = CodeInterpreter.GetMember (valueMember.MemberInfo, value);
				
				try {
					collectionContract.AddMethod.Invoke (collection, new object [] {pkey, pvalue});
				} catch (TargetInvocationException ex) {
					if (ex.InnerException != null)
						throw ex.InnerException;
					else
						throw;
				}
			}
			else
				collectionContract.AddMethod.Invoke (collection, new object [] {value});
		}

		void HandleUnexpectedItemInCollection (ref int iterator)
		{
			if (IsStartElement ()) {
				context.SkipUnknownElement (xmlReader);
				iterator--;
			}
			else 
				throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (XmlNodeType.Element, xmlReader);
		}

		bool IsStartElement(XmlDictionaryString name, XmlDictionaryString ns)
		{
			return xmlReader.IsStartElement (name, ns);
		}

		bool IsStartElement()
		{
			return xmlReader.IsStartElement ();
		}

		bool IsEndElement ()
		{
			return xmlReader.NodeType == XmlNodeType.EndElement;
		}

		void ThrowUnexpectedStateException (XmlNodeType expectedState)
		{
			throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (expectedState, xmlReader);
		}

		void ThrowSerializationException (string msg, params object [] values)
		{
			if (values != null && values.Length > 0)
				msg = string.Format (msg, values);
			throw new SerializationException (msg);
		}

		enum KeyParseMode
		{
			Fail,
			AsString,
			UsingParseEnum,
			UsingCustomParse
		}
	}
}