#if !UNITY_WINRT || UNITY_EDITOR || (UNITY_WP8 &&  !UNITY_WP_8_1)
#if !((UNITY_WP8 || UNITY_WP_8_1) || (UNITY_WINRT && !UNITY_EDITOR) || UNITY_IPHONE || UNITY_IOS || UNITY_ANDROID)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Text;
using System.Threading;
using System.Globalization;

namespace Newtonsoft.Json.Utilities
{
	internal class DynamicWrapperBase
	{
		internal protected object UnderlyingObject;
	}

	internal static class DynamicWrapper
	{
		private static readonly object _lock = new object();
		private static readonly WrapperDictionary _wrapperDictionary = new WrapperDictionary();

		private static ModuleBuilder _moduleBuilder;

		private static ModuleBuilder ModuleBuilder
		{
			get
			{
				Init();
				return _moduleBuilder;
			}
		}

		private static void Init()
		{
			if (_moduleBuilder == null)
			{
				lock (_lock)
				{
					if (_moduleBuilder == null)
					{
						AssemblyName assemblyName = new AssemblyName("Newtonsoft.Json.Dynamic");
						assemblyName.KeyPair = new StrongNameKeyPair(GetStrongKey());

						AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
						_moduleBuilder = assembly.DefineDynamicModule("Newtonsoft.Json.DynamicModule", false);
					}
				}
			}
		}

		private static byte[] GetStrongKey()
		{
			string name = "Newtonsoft.Json.Dynamic.snk";

			using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
			{
				if (stream == null)
					throw new MissingManifestResourceException("Should have " + name + " as an embedded resource.");

				int length = (int)stream.Length;
				byte[] buffer = new byte[length];
				stream.Read(buffer, 0, length);

				return buffer;
			}
		}

		public static Type GetWrapper(Type interfaceType, Type realObjectType)
		{
			Type wrapperType = _wrapperDictionary.GetType(interfaceType, realObjectType);

			if (wrapperType == null)
			{
				lock (_lock)
				{
					wrapperType = _wrapperDictionary.GetType(interfaceType, realObjectType);

					if (wrapperType == null)
					{
						wrapperType = GenerateWrapperType(interfaceType, realObjectType);
						_wrapperDictionary.SetType(interfaceType, realObjectType, wrapperType);
					}
				}
			}

			return wrapperType;
		}

		public static object GetUnderlyingObject(object wrapper)
		{
			DynamicWrapperBase wrapperBase = wrapper as DynamicWrapperBase;
			if (wrapperBase == null)
				throw new ArgumentException("Object is not a wrapper.", "wrapper");

			return wrapperBase.UnderlyingObject;
		}

		private static Type GenerateWrapperType(Type interfaceType, Type underlyingType)
		{
			TypeBuilder wrapperBuilder = ModuleBuilder.DefineType(
				"{0}_{1}_Wrapper".FormatWith(CultureInfo.InvariantCulture, interfaceType.Name, underlyingType.Name),
				TypeAttributes.NotPublic | TypeAttributes.Sealed,
				typeof(DynamicWrapperBase),
				new[] { interfaceType });

			WrapperMethodBuilder wrapperMethod = new WrapperMethodBuilder(underlyingType, wrapperBuilder);

			foreach (MethodInfo method in interfaceType.AllMethods())
			{
				wrapperMethod.Generate(method);
			}

			return wrapperBuilder.CreateType();
		}

		public static T CreateWrapper<T>(object realObject) where T : class
		{
			var dynamicType = GetWrapper(typeof(T), realObject.GetType());
			var dynamicWrapper = (DynamicWrapperBase)Activator.CreateInstance(dynamicType);

			dynamicWrapper.UnderlyingObject = realObject;

			return dynamicWrapper as T;
		}
	}

	internal class WrapperMethodBuilder
	{
		private readonly Type _realObjectType;
		private readonly TypeBuilder _wrapperBuilder;

		public WrapperMethodBuilder(Type realObjectType, TypeBuilder proxyBuilder)
		{
			_realObjectType = realObjectType;
			_wrapperBuilder = proxyBuilder;
		}

		public void Generate(MethodInfo newMethod)
		{
			if (newMethod.IsGenericMethod)
				newMethod = newMethod.GetGenericMethodDefinition();

			FieldInfo srcField = typeof(DynamicWrapperBase).GetField("UnderlyingObject", BindingFlags.Instance | BindingFlags.NonPublic);

			var parameters = newMethod.GetParameters();
			var parameterTypes = parameters.Select(parameter => parameter.ParameterType).ToArray();

			MethodBuilder methodBuilder = _wrapperBuilder.DefineMethod(
				newMethod.Name,
				MethodAttributes.Public | MethodAttributes.Virtual,
				newMethod.ReturnType,
				parameterTypes);

			if (newMethod.IsGenericMethod)
			{
				methodBuilder.DefineGenericParameters(
					newMethod.GetGenericArguments().Select(arg => arg.Name).ToArray());
			}

			ILGenerator ilGenerator = methodBuilder.GetILGenerator();

			LoadUnderlyingObject(ilGenerator, srcField);
			PushParameters(parameters, ilGenerator);
			ExecuteMethod(newMethod, parameterTypes, ilGenerator);
			Return(ilGenerator);
		}

		private static void Return(ILGenerator ilGenerator)
		{
			ilGenerator.Emit(OpCodes.Ret);
		}

		private void ExecuteMethod(MethodBase newMethod, Type[] parameterTypes, ILGenerator ilGenerator)
		{
			MethodInfo srcMethod = GetMethod(newMethod, parameterTypes);

			if (srcMethod == null)
				throw new MissingMethodException("Unable to find method " + newMethod.Name + " on " + _realObjectType.FullName);

			ilGenerator.Emit(OpCodes.Call, srcMethod);
		}

		private MethodInfo GetMethod(MethodBase realMethod, Type[] parameterTypes)
		{
			if (realMethod.IsGenericMethod)
				return _realObjectType.GetGenericMethod(realMethod.Name, parameterTypes);

			return _realObjectType.GetMethod(realMethod.Name, parameterTypes);
		}

		private static void PushParameters(ICollection<ParameterInfo> parameters, ILGenerator ilGenerator)
		{
			for (int i = 1; i < parameters.Count + 1; i++)
				ilGenerator.Emit(OpCodes.Ldarg, i);
		}

		private static void LoadUnderlyingObject(ILGenerator ilGenerator, FieldInfo srcField)
		{
			ilGenerator.Emit(OpCodes.Ldarg_0);
			ilGenerator.Emit(OpCodes.Ldfld, srcField);
		}
	}

	internal class WrapperDictionary
	{
		private readonly Dictionary<string, Type> _wrapperTypes = new Dictionary<string, Type>();

		private static string GenerateKey(Type interfaceType, Type realObjectType)
		{
			return interfaceType.Name + "_" + realObjectType.Name;
		}

		public Type GetType(Type interfaceType, Type realObjectType)
		{
			string key = GenerateKey(interfaceType, realObjectType);

			if (_wrapperTypes.ContainsKey(key))
				return _wrapperTypes[key];

			return null;
		}

		public void SetType(Type interfaceType, Type realObjectType, Type wrapperType)
		{
			string key = GenerateKey(interfaceType, realObjectType);

			if (_wrapperTypes.ContainsKey(key))
				_wrapperTypes[key] = wrapperType;
			else
				_wrapperTypes.Add(key, wrapperType);
		}
	}

	internal static class TypeExtensions
	{
		public static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes)
		{
			var methods = type.GetMethods().Where(method => method.Name == name);

			foreach (var method in methods)
			{
				if (method.HasParameters(parameterTypes))
					return method;
			}

			return null;
		}

		public static bool HasParameters(this MethodInfo method, params Type[] parameterTypes)
		{
			var methodParameters = method.GetParameters().Select(parameter => parameter.ParameterType).ToArray();

			if (methodParameters.Length != parameterTypes.Length)
				return false;

			for (int i = 0; i < methodParameters.Length; i++)
				if (methodParameters[i].ToString() != parameterTypes[i].ToString())
					return false;

			return true;
		}

		public static IEnumerable<Type> AllInterfaces(this Type target)
		{
			foreach (var IF in target.GetInterfaces())
			{
				yield return IF;
				foreach (var childIF in IF.AllInterfaces())
				{
					yield return childIF;
				}
			}
		}

		public static IEnumerable<MethodInfo> AllMethods(this Type target)
		{
			var allTypes = target.AllInterfaces().ToList();
			allTypes.Add(target);

			return from type in allTypes
				   from method in type.GetMethods()
				   select method;
		}
	}
}
#endif
#endif