/*
  Copyright (C) 2007-2014 Jeroen Frijters

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Jeroen Frijters
  jeroen@frijters.net
  
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Threading;
using IKVM.Internal;

static class Java_java_lang_Class
{
	public static java.lang.Class forName0(string name, bool initialize, java.lang.ClassLoader loader)
	{
#if FIRST_PASS
		return null;
#else
		//Console.WriteLine("forName: " + name + ", loader = " + loader);
		TypeWrapper tw = null;
		if (name.IndexOf(',') > 0)
		{
			// we essentially require full trust before allowing arbitrary types to be loaded,
			// hence we do the "createClassLoader" permission check
			java.lang.SecurityManager sm = java.lang.System.getSecurityManager();
			if (sm != null)
				sm.checkPermission(new java.lang.RuntimePermission("createClassLoader"));
			Type type = Type.GetType(name);
			if (type != null)
			{
				tw = ClassLoaderWrapper.GetWrapperFromType(type);
			}
			if (tw == null)
			{
				java.lang.Throwable.suppressFillInStackTrace = true;
				throw new java.lang.ClassNotFoundException(name);
			}
		}
		else
		{
			try
			{
				ClassLoaderWrapper classLoaderWrapper = ClassLoaderWrapper.GetClassLoaderWrapper(loader);
				tw = classLoaderWrapper.LoadClassByDottedName(name);
			}
			catch (ClassNotFoundException x)
			{
				java.lang.Throwable.suppressFillInStackTrace = true;
				throw new java.lang.ClassNotFoundException(x.Message);
			}
			catch (ClassLoadingException x)
			{
				throw x.InnerException;
			}
			catch (RetargetableJavaException x)
			{
				throw x.ToJava();
			}
		}
		if (initialize && !tw.IsArray)
		{
			try
			{
				tw.Finish();
			}
			catch (RetargetableJavaException x)
			{
				throw x.ToJava();
			}
			tw.RunClassInit();
		}
		return tw.ClassObject;
#endif
	}

	public static byte[] getRawTypeAnnotations(java.lang.Class thisClass)
	{
		return TypeWrapper.FromClass(thisClass).GetRawTypeAnnotations();
	}

#if !FIRST_PASS
	private sealed class ConstantPoolImpl : sun.reflect.ConstantPool
	{
		private readonly object[] constantPool;

		internal ConstantPoolImpl(object[] constantPool)
		{
			this.constantPool = constantPool;
		}

		public override string getUTF8At(int index)
		{
			return (string)constantPool[index];
		}

		public override int getIntAt(int index)
		{
			return (int)constantPool[index];
		}

		public override long getLongAt(int index)
		{
			return (long)constantPool[index];
		}

		public override float getFloatAt(int index)
		{
			return (float)constantPool[index];
		}

		public override double getDoubleAt(int index)
		{
			return (double)constantPool[index];
		}
	}
#endif

    public static object getConstantPool(java.lang.Class thisClass)
	{
#if FIRST_PASS
		return null;
#else
		return new ConstantPoolImpl(TypeWrapper.FromClass(thisClass).GetConstantPool());
#endif
	}

	public static bool isInstance(java.lang.Class thisClass, object obj)
	{
		return TypeWrapper.FromClass(thisClass).IsInstance(obj);
	}

	public static bool isAssignableFrom(java.lang.Class thisClass, java.lang.Class otherClass)
	{
#if !FIRST_PASS
		if (otherClass == null)
		{
			throw new java.lang.NullPointerException();
		}
#endif
		return TypeWrapper.FromClass(otherClass).IsAssignableTo(TypeWrapper.FromClass(thisClass));
	}

	public static bool isInterface(java.lang.Class thisClass)
	{
		return TypeWrapper.FromClass(thisClass).IsInterface;
	}

	public static bool isArray(java.lang.Class thisClass)
	{
		return TypeWrapper.FromClass(thisClass).IsArray;
	}

	public static bool isPrimitive(java.lang.Class thisClass)
	{
		return TypeWrapper.FromClass(thisClass).IsPrimitive;
	}

	public static string getName0(java.lang.Class thisClass)
	{
		TypeWrapper tw = TypeWrapper.FromClass(thisClass);
		if (tw.IsPrimitive)
		{
			if (tw == PrimitiveTypeWrapper.BYTE)
			{
				return "byte";
			}
			else if (tw == PrimitiveTypeWrapper.CHAR)
			{
				return "char";
			}
			else if (tw == PrimitiveTypeWrapper.DOUBLE)
			{
				return "double";
			}
			else if (tw == PrimitiveTypeWrapper.FLOAT)
			{
				return "float";
			}
			else if (tw == PrimitiveTypeWrapper.INT)
			{
				return "int";
			}
			else if (tw == PrimitiveTypeWrapper.LONG)
			{
				return "long";
			}
			else if (tw == PrimitiveTypeWrapper.SHORT)
			{
				return "short";
			}
			else if (tw == PrimitiveTypeWrapper.BOOLEAN)
			{
				return "boolean";
			}
			else if (tw == PrimitiveTypeWrapper.VOID)
			{
				return "void";
			}
		}
		if (tw.IsUnsafeAnonymous)
		{
#if !FIRST_PASS
			// for OpenJDK compatibility and debugging convenience we modify the class name to
			// include the identity hashcode of the class object
			return tw.Name + "/" + java.lang.System.identityHashCode(thisClass);
#endif
		}
		return tw.Name;
	}

	public static string getSigName(java.lang.Class thisClass)
	{
		return TypeWrapper.FromClass(thisClass).SigName;
	}

	public static java.lang.ClassLoader getClassLoader0(java.lang.Class thisClass)
	{
		return TypeWrapper.FromClass(thisClass).GetClassLoader().GetJavaClassLoader();
	}

	public static java.lang.Class getSuperclass(java.lang.Class thisClass)
	{
		TypeWrapper super = TypeWrapper.FromClass(thisClass).BaseTypeWrapper;
		return super != null ? super.ClassObject : null;
	}

	public static java.lang.Class[] getInterfaces0(java.lang.Class thisClass)
	{
#if FIRST_PASS
		return null;
#else
		TypeWrapper[] ifaces = TypeWrapper.FromClass(thisClass).Interfaces;
		java.lang.Class[] interfaces = new java.lang.Class[ifaces.Length];
		for (int i = 0; i < ifaces.Length; i++)
		{
			interfaces[i] = ifaces[i].ClassObject;
		}
		return interfaces;
#endif
	}

	public static java.lang.Class getComponentType(java.lang.Class thisClass)
	{
		TypeWrapper tw = TypeWrapper.FromClass(thisClass);
		return tw.IsArray ? tw.ElementTypeWrapper.ClassObject : null;
	}

	public static int getModifiers(java.lang.Class thisClass)
	{
		// the 0x7FFF mask comes from JVM_ACC_WRITTEN_FLAGS in hotspot\src\share\vm\utilities\accessFlags.hpp
		// masking out ACC_SUPER comes from instanceKlass::compute_modifier_flags() in hotspot\src\share\vm\oops\instanceKlass.cpp
		const int mask = 0x7FFF & (int)~IKVM.Attributes.Modifiers.Super;
		return (int)TypeWrapper.FromClass(thisClass).ReflectiveModifiers & mask;
	}

	public static object[] getSigners(java.lang.Class thisClass)
	{
#if FIRST_PASS
		return null;
#else
		return thisClass.signers;
#endif
	}

	public static void setSigners(java.lang.Class thisClass, object[] signers)
	{
#if !FIRST_PASS
		thisClass.signers = signers;
#endif
	}

	public static object[] getEnclosingMethod0(java.lang.Class thisClass)
	{
		try
		{
			TypeWrapper tw = TypeWrapper.FromClass(thisClass);
			tw.Finish();
			string[] enc = tw.GetEnclosingMethod();
			if (enc == null)
			{
				return null;
			}
			return new object[] { tw.GetClassLoader().LoadClassByDottedName(enc[0]).ClassObject, enc[1], enc[2] == null ? null : enc[2].Replace('.', '/') };
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
	}

	public static java.lang.Class getDeclaringClass0(java.lang.Class thisClass)
	{
		try
		{
			TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
			wrapper.Finish();
			TypeWrapper decl = wrapper.DeclaringTypeWrapper;
			if (decl == null)
			{
				return null;
			}
			if (!decl.IsAccessibleFrom(wrapper))
			{
				throw new IllegalAccessError(string.Format("tried to access class {0} from class {1}", decl.Name, wrapper.Name));
			}
			decl.Finish();
			if (Array.IndexOf(decl.InnerClasses, wrapper) == -1)
			{
				throw new IncompatibleClassChangeError(string.Format("{0} and {1} disagree on InnerClasses attribute", decl.Name, wrapper.Name));
			}
			return decl.ClassObject;
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
	}

	public static java.security.ProtectionDomain getProtectionDomain0(java.lang.Class thisClass)
	{
#if FIRST_PASS
		return null;
#else
		TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
		if (wrapper.IsArray)
		{
			return null;
		}
		java.security.ProtectionDomain pd = wrapper.ClassObject.pd;
		if (pd == null)
		{
			// The protection domain for statically compiled code is created lazily (not at java.lang.Class creation time),
			// to work around boot strap issues.
			AssemblyClassLoader acl = wrapper.GetClassLoader() as AssemblyClassLoader;
			if (acl != null)
			{
				pd = acl.GetProtectionDomain();
			}
			else if (wrapper is AnonymousTypeWrapper)
			{
				// dynamically compiled intrinsified lamdba anonymous types end up here and should get their
				// protection domain from the host class
				pd = ClassLoaderWrapper.GetWrapperFromType(wrapper.TypeAsTBD.DeclaringType).ClassObject.pd;
			}
		}
		return pd;
#endif
	}

	public static java.lang.Class getPrimitiveClass(string name)
	{
		// note that this method isn't used anymore (because it is an intrinsic (during core class library compilation))
		// it still remains for compat because it might be invoked through reflection by evil code
		switch (name)
		{
			case "byte":
				return PrimitiveTypeWrapper.BYTE.ClassObject;
			case "char":
				return PrimitiveTypeWrapper.CHAR.ClassObject;
			case "double":
				return PrimitiveTypeWrapper.DOUBLE.ClassObject;
			case "float":
				return PrimitiveTypeWrapper.FLOAT.ClassObject;
			case "int":
				return PrimitiveTypeWrapper.INT.ClassObject;
			case "long":
				return PrimitiveTypeWrapper.LONG.ClassObject;
			case "short":
				return PrimitiveTypeWrapper.SHORT.ClassObject;
			case "boolean":
				return PrimitiveTypeWrapper.BOOLEAN.ClassObject;
			case "void":
				return PrimitiveTypeWrapper.VOID.ClassObject;
			default:
				throw new ArgumentException(name);
		}
	}

	public static string getGenericSignature0(java.lang.Class thisClass)
	{
		TypeWrapper tw = TypeWrapper.FromClass(thisClass);
		tw.Finish();
		return tw.GetGenericSignature();
	}

	internal static object AnnotationsToMap(ClassLoaderWrapper loader, object[] objAnn)
	{
#if FIRST_PASS
		return null;
#else
		java.util.LinkedHashMap map = new java.util.LinkedHashMap();
		if (objAnn != null)
		{
			foreach (object obj in objAnn)
			{
				java.lang.annotation.Annotation a = obj as java.lang.annotation.Annotation;
				if (a != null)
				{
					map.put(a.annotationType(), FreezeOrWrapAttribute(a));
				}
				else if (obj is IKVM.Attributes.DynamicAnnotationAttribute)
				{
					a = (java.lang.annotation.Annotation)JVM.NewAnnotation(loader.GetJavaClassLoader(), ((IKVM.Attributes.DynamicAnnotationAttribute)obj).Definition);
					if (a != null)
					{
						map.put(a.annotationType(), a);
					}
				}
			}
		}
		return map;
#endif
	}

#if !FIRST_PASS
	internal static java.lang.annotation.Annotation FreezeOrWrapAttribute(java.lang.annotation.Annotation ann)
	{
		ikvm.@internal.AnnotationAttributeBase attr = ann as ikvm.@internal.AnnotationAttributeBase;
		if (attr != null)
		{
#if DONT_WRAP_ANNOTATION_ATTRIBUTES
			attr.freeze();
#else
			// freeze to make sure the defaults are set
			attr.freeze();
			ann = sun.reflect.annotation.AnnotationParser.annotationForMap(attr.annotationType(), attr.getValues());
#endif
		}
		return ann;
	}
#endif

	public static object getDeclaredAnnotationsImpl(java.lang.Class thisClass)
	{
#if FIRST_PASS
		return null;
#else
		TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
		try
		{
			wrapper.Finish();
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
		return AnnotationsToMap(wrapper.GetClassLoader(), wrapper.GetDeclaredAnnotations());
#endif
	}

	public static object getDeclaredFields0(java.lang.Class thisClass, bool publicOnly)
	{
#if FIRST_PASS
		return null;
#else
		Profiler.Enter("Class.getDeclaredFields0");
		try
		{
			TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
			// we need to finish the type otherwise all fields will not be in the field map yet
			wrapper.Finish();
			FieldWrapper[] fields = wrapper.GetFields();
			List<java.lang.reflect.Field> list = new List<java.lang.reflect.Field>();
			for (int i = 0; i < fields.Length; i++)
			{
				if (fields[i].IsHideFromReflection)
				{
					// skip
				}
				else if (publicOnly && !fields[i].IsPublic)
				{
					// caller is only asking for public field, so we don't return this non-public field
				}
				else
				{
					list.Add((java.lang.reflect.Field)fields[i].ToField(false, i));
				}
			}
			return list.ToArray();
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
		finally
		{
			Profiler.Leave("Class.getDeclaredFields0");
		}
#endif
	}

	public static object getDeclaredMethods0(java.lang.Class thisClass, bool publicOnly)
	{
#if FIRST_PASS
		return null;
#else
		Profiler.Enter("Class.getDeclaredMethods0");
		try
		{
			TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
			wrapper.Finish();
			if (wrapper.HasVerifyError)
			{
				// TODO we should get the message from somewhere
				throw new VerifyError();
			}
			if (wrapper.HasClassFormatError)
			{
				// TODO we should get the message from somewhere
				throw new ClassFormatError(wrapper.Name);
			}
			MethodWrapper[] methods = wrapper.GetMethods();
			List<java.lang.reflect.Method> list = new List<java.lang.reflect.Method>();
			for (int i = 0; i < methods.Length; i++)
			{
				// we don't want to expose "hideFromReflection" methods (one reason is that it would
				// mess up the serialVersionUID computation)
				if (!methods[i].IsHideFromReflection
					&& methods[i].Name != "<clinit>" && methods[i].Name != "<init>"
					&& (!publicOnly || methods[i].IsPublic))
				{
					list.Add((java.lang.reflect.Method)methods[i].ToMethodOrConstructor(false));
				}
			}
			return list.ToArray();
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
		finally
		{
			Profiler.Leave("Class.getDeclaredMethods0");
		}
#endif
	}

	public static object getDeclaredConstructors0(java.lang.Class thisClass, bool publicOnly)
	{
#if FIRST_PASS
		return null;
#else
		Profiler.Enter("Class.getDeclaredConstructors0");
		try
		{
			TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
			wrapper.Finish();
			if (wrapper.HasVerifyError)
			{
				// TODO we should get the message from somewhere
				throw new VerifyError();
			}
			if (wrapper.HasClassFormatError)
			{
				// TODO we should get the message from somewhere
				throw new ClassFormatError(wrapper.Name);
			}
			MethodWrapper[] methods = wrapper.GetMethods();
			List<java.lang.reflect.Constructor> list = new List<java.lang.reflect.Constructor>();
			for (int i = 0; i < methods.Length; i++)
			{
				// we don't want to expose "hideFromReflection" methods (one reason is that it would
				// mess up the serialVersionUID computation)
				if (!methods[i].IsHideFromReflection
					&& methods[i].Name == "<init>"
					&& (!publicOnly || methods[i].IsPublic))
				{
					list.Add((java.lang.reflect.Constructor)methods[i].ToMethodOrConstructor(false));
				}
			}
			return list.ToArray();
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
		finally
		{
			Profiler.Leave("Class.getDeclaredConstructors0");
		}
#endif
	}

	public static java.lang.Class[] getDeclaredClasses0(java.lang.Class thisClass)
	{
#if FIRST_PASS
		return null;
#else
		try
		{
			TypeWrapper wrapper = TypeWrapper.FromClass(thisClass);
			// NOTE to get at the InnerClasses we need to finish the type
			wrapper.Finish();
			TypeWrapper[] wrappers = wrapper.InnerClasses;
			java.lang.Class[] innerclasses = new java.lang.Class[wrappers.Length];
			for (int i = 0; i < innerclasses.Length; i++)
			{
				if (wrappers[i].IsUnloadable)
				{
					throw new java.lang.NoClassDefFoundError(wrappers[i].Name);
				}
				if (!wrappers[i].IsAccessibleFrom(wrapper))
				{
					throw new IllegalAccessError(string.Format("tried to access class {0} from class {1}", wrappers[i].Name, wrapper.Name));
				}
				wrappers[i].Finish();
				innerclasses[i] = wrappers[i].ClassObject;
			}
			return innerclasses;
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
#endif
	}

	public static bool desiredAssertionStatus0(java.lang.Class clazz)
	{
		return IKVM.Runtime.Assertions.IsEnabled(TypeWrapper.FromClass(clazz));
	}
}

static class Java_java_lang_ClassLoader
{
	public static java.net.URL getBootstrapResource(string name)
	{
		foreach (java.net.URL url in ClassLoaderWrapper.GetBootstrapClassLoader().GetResources(name))
		{
			return url;
		}
		return null;
	}

	public static java.util.Enumeration getBootstrapResources(string name)
	{
#if FIRST_PASS
		return null;
#else
		return new ikvm.runtime.EnumerationWrapper(ClassLoaderWrapper.GetBootstrapClassLoader().GetResources(name));
#endif
	}

	public static java.lang.Class defineClass0(java.lang.ClassLoader thisClassLoader, string name, byte[] b, int off, int len, java.security.ProtectionDomain pd)
	{
		return defineClass1(thisClassLoader, name, b, off, len, pd, null);
	}

	public static java.lang.Class defineClass1(java.lang.ClassLoader thisClassLoader, string name, byte[] b, int off, int len, java.security.ProtectionDomain pd, string source)
	{
		// it appears the source argument is only used for trace messages in HotSpot. We'll just ignore it for now.
		Profiler.Enter("ClassLoader.defineClass");
		try
		{
			try
			{
				ClassLoaderWrapper classLoaderWrapper = ClassLoaderWrapper.GetClassLoaderWrapper(thisClassLoader);
				ClassFile classFile = new ClassFile(b, off, len, name, classLoaderWrapper.ClassFileParseOptions, null);
				if (name != null && classFile.Name != name)
				{
#if !FIRST_PASS
					throw new java.lang.NoClassDefFoundError(name + " (wrong name: " + classFile.Name + ")");
#endif
				}
				TypeWrapper type = classLoaderWrapper.DefineClass(classFile, pd);
				return type.ClassObject;
			}
			catch (RetargetableJavaException x)
			{
				throw x.ToJava();
			}
		}
		finally
		{
			Profiler.Leave("ClassLoader.defineClass");
		}
	}

	public static java.lang.Class defineClass2(java.lang.ClassLoader thisClassLoader, string name, java.nio.ByteBuffer bb, int off, int len, java.security.ProtectionDomain pd, string source)
	{
#if FIRST_PASS
		return null;
#else
		byte[] buf = new byte[bb.remaining()];
		bb.get(buf);
		return defineClass1(thisClassLoader, name, buf, 0, buf.Length, pd, source);
#endif
	}

	public static void resolveClass0(java.lang.ClassLoader thisClassLoader, java.lang.Class clazz)
	{
		// no-op
	}

	public static java.lang.Class findBootstrapClass(java.lang.ClassLoader thisClassLoader, string name)
	{
#if FIRST_PASS
		return null;
#else
		TypeWrapper tw;
		try
		{
			tw = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedNameFast(name);
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
		return tw != null ? tw.ClassObject : null;
#endif
	}

	public static java.lang.Class findLoadedClass0(java.lang.ClassLoader thisClassLoader, string name)
	{
		if (name == null)
		{
			return null;
		}
		ClassLoaderWrapper loader = ClassLoaderWrapper.GetClassLoaderWrapper(thisClassLoader);
		TypeWrapper tw = loader.FindLoadedClass(name);
		return tw != null ? tw.ClassObject : null;
	}

	public static object retrieveDirectives()
	{
		return IKVM.Runtime.Assertions.RetrieveDirectives();
	}
}

static class Java_java_lang_ClassLoader_00024NativeLibrary
{
	public static void load(object thisNativeLibrary, string name, bool isBuiltin)
	{
#if !FIRST_PASS
		if (VirtualFileSystem.IsVirtualFS(name))
		{
			// we fake success for native libraries loaded from VFS
			((java.lang.ClassLoader.NativeLibrary)thisNativeLibrary).loaded = true;
		}
		else
		{
			doLoad(thisNativeLibrary, name);
		}
#endif
	}

#if !FIRST_PASS
	// we don't want to inline this method, because that would needlessly cause IKVM.Runtime.JNI.dll to be loaded when loading a fake native library from VFS
	[MethodImpl(MethodImplOptions.NoInlining)]
	[SecuritySafeCritical]
	private static void doLoad(object thisNativeLibrary, string name)
	{
		java.lang.ClassLoader.NativeLibrary lib = (java.lang.ClassLoader.NativeLibrary)thisNativeLibrary;
		lib.handle = IKVM.Runtime.JniHelper.LoadLibrary(name, TypeWrapper.FromClass(java.lang.ClassLoader.NativeLibrary.getFromClass()).GetClassLoader());
		lib.loaded = true;
	}
#endif

	public static long find(object thisNativeLibrary, string name)
	{
		// TODO
		throw new NotImplementedException();
	}

	[SecuritySafeCritical]
	public static void unload(object thisNativeLibrary, string name, bool isBuiltin)
	{
#if !FIRST_PASS
		java.lang.ClassLoader.NativeLibrary lib = (java.lang.ClassLoader.NativeLibrary)thisNativeLibrary;
		long handle = Interlocked.Exchange(ref lib.handle, 0);
		if (handle != 0)
		{
			IKVM.Runtime.JniHelper.UnloadLibrary(handle, TypeWrapper.FromClass(java.lang.ClassLoader.NativeLibrary.getFromClass()).GetClassLoader());
		}
#endif
	}

	public static string findBuiltinLib(string name)
	{
		return null;
	}
}

static class Java_java_lang_Compiler
{
	public static void initialize()
	{
	}

	public static void registerNatives()
	{
	}

	public static bool compileClass(object clazz)
	{
		return false;
	}

	public static bool compileClasses(string str)
	{
		return false;
	}

	public static object command(object any)
	{
		return null;
	}

	public static void enable()
	{
	}

	public static void disable()
	{
	}
}

static class Java_java_lang_Double
{
	public static long doubleToRawLongBits(double value)
	{
		IKVM.Runtime.DoubleConverter converter = new IKVM.Runtime.DoubleConverter();
		return IKVM.Runtime.DoubleConverter.ToLong(value, ref converter);
	}

	public static double longBitsToDouble(long bits)
	{
		IKVM.Runtime.DoubleConverter converter = new IKVM.Runtime.DoubleConverter();
		return IKVM.Runtime.DoubleConverter.ToDouble(bits, ref converter);
	}
}

static class Java_java_lang_Float
{
	public static int floatToRawIntBits(float value)
	{
		IKVM.Runtime.FloatConverter converter = new IKVM.Runtime.FloatConverter();
		return IKVM.Runtime.FloatConverter.ToInt(value, ref converter);
	}

	public static float intBitsToFloat(int bits)
	{
		IKVM.Runtime.FloatConverter converter = new IKVM.Runtime.FloatConverter();
		return IKVM.Runtime.FloatConverter.ToFloat(bits, ref converter);
	}
}

static class Java_java_lang_Package
{
	private static Dictionary<string, string> systemPackages;

	private static void LazyInitSystemPackages()
	{
		if (systemPackages == null)
		{
			Dictionary<string, string> dict = new Dictionary<string, string>();
			string path = VirtualFileSystem.GetAssemblyResourcesPath(JVM.CoreAssembly) + "resources.jar";
			foreach (KeyValuePair<string, string[]> pkgs in ClassLoaderWrapper.GetBootstrapClassLoader().GetPackageInfo())
			{
				foreach (string pkg in pkgs.Value)
				{
					dict[pkg.Replace('.', '/') + "/"] = path;
				}
			}
			Interlocked.CompareExchange(ref systemPackages, dict, null);
		}
	}

	public static string getSystemPackage0(string name)
	{
		LazyInitSystemPackages();
		string path;
		systemPackages.TryGetValue(name, out path);
		return path;
	}

	public static string[] getSystemPackages0()
	{
		LazyInitSystemPackages();
		string[] pkgs = new string[systemPackages.Count];
		systemPackages.Keys.CopyTo(pkgs, 0);
		return pkgs;
	}
}

static class Java_java_lang_ProcessEnvironment
{
	public static string environmentBlock()
	{
		StringBuilder sb = new StringBuilder();
		foreach (System.Collections.DictionaryEntry de in Environment.GetEnvironmentVariables())
		{
			sb.Append(de.Key).Append('=').Append(de.Value).Append('\u0000');
		}
		if (sb.Length == 0)
		{
			sb.Append('\u0000');
		}
		sb.Append('\u0000');
		return sb.ToString();
	}
}

static class Java_java_lang_Runtime
{
	public static int availableProcessors(object thisRuntime)
	{
		return Environment.ProcessorCount;
	}

	public static long freeMemory(object thisRuntime)
	{
		// TODO figure out if there is anything meaningful we can return here
		return 10 * 1024 * 1024;
	}

	public static long totalMemory(object thisRuntime)
	{
		// NOTE this really is a bogus number, but we have to return something
		return GC.GetTotalMemory(false) + freeMemory(thisRuntime);
	}

	public static long maxMemory(object thisRuntime)
	{
		// spec says: If there is no inherent limit then the value Long.MAX_VALUE will be returned.
		return Int64.MaxValue;
	}

	public static void gc(object thisRuntime)
	{
		GC.Collect();
	}

	public static void traceInstructions(object thisRuntime, bool on)
	{
	}

	public static void traceMethodCalls(object thisRuntime, bool on)
	{
	}

	public static void runFinalization0()
	{
		GC.WaitForPendingFinalizers();
	}
}

static class Java_java_lang_SecurityManager
{
	// this field is set by code in the JNI assembly itself,
	// to prevent having to load the JNI assembly when it isn't used.
	internal static volatile Assembly jniAssembly;

	public static java.lang.Class[] getClassContext(object thisSecurityManager)
	{
#if FIRST_PASS
		return null;
#else
		List<java.lang.Class> stack = new List<java.lang.Class>();
		StackTrace trace = new StackTrace();
		for (int i = 0; i < trace.FrameCount; i++)
		{
			StackFrame frame = trace.GetFrame(i);
			MethodBase method = frame.GetMethod();
			if (Java_sun_reflect_Reflection.IsHideFromStackWalk(method))
			{
				continue;
			}
			Type type = method.DeclaringType;
			if (type == typeof(java.lang.SecurityManager))
			{
				continue;
			}
			stack.Add(ClassLoaderWrapper.GetWrapperFromType(type).ClassObject);
		}
		return stack.ToArray();
#endif
	}

	public static object currentClassLoader0(object thisSecurityManager)
	{
		java.lang.Class currentClass = currentLoadedClass0(thisSecurityManager);
		if (currentClass != null)
		{
			return TypeWrapper.FromClass(currentClass).GetClassLoader().GetJavaClassLoader();
		}
		return null;
	}

	public static int classDepth(object thisSecurityManager, string name)
	{
		throw new NotImplementedException();
	}

	public static int classLoaderDepth0(object thisSecurityManager)
	{
		throw new NotImplementedException();
	}

	public static java.lang.Class currentLoadedClass0(object thisSecurityManager)
	{
		throw new NotImplementedException();
	}
}

static class Java_java_lang_StrictMath
{
	public static double sin(double d)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.sin(d);
#endif
	}

	public static double cos(double d)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.cos(d);
#endif
	}

	public static double tan(double d)
	{
		return fdlibm.tan(d);
	}

	public static double asin(double d)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.asin(d);
#endif
	}

	public static double acos(double d)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.acos(d);
#endif
	}

	public static double atan(double d)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.atan(d);
#endif
	}

	public static double exp(double d)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.exp(d);
#endif
	}

	public static double log(double d)
	{
		// FPU behavior is correct
		return Math.Log(d);
	}

	public static double log10(double d)
	{
		// FPU behavior is correct
		return Math.Log10(d);
	}

	public static double sqrt(double d)
	{
		// FPU behavior is correct
		return Math.Sqrt(d);
	}

	public static double cbrt(double d)
	{
		return fdlibm.cbrt(d);
	}

	public static double IEEEremainder(double f1, double f2)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.IEEEremainder(f1, f2);
#endif
	}

	public static double atan2(double y, double x)
	{
#if FIRST_PASS
		return 0;
#else
		return ikvm.@internal.JMath.atan2(y, x);
#endif
	}

	public static double pow(double x, double y)
	{
		return fdlibm.__ieee754_pow(x, y);
	}

	public static double sinh(double d)
	{
		return Math.Sinh(d);
	}

	public static double cosh(double d)
	{
		return Math.Cosh(d);
	}

	public static double tanh(double d)
	{
		return Math.Tanh(d);
	}

	public static double hypot(double a, double b)
	{
		return fdlibm.__ieee754_hypot(a, b);
	}

	public static double expm1(double d)
	{
		return fdlibm.expm1(d);
	}

	public static double log1p(double d)
	{
		return fdlibm.log1p(d);
	}
}

static class Java_java_lang_System
{
	public static void arraycopy(object src, int srcPos, object dest, int destPos, int length)
	{
		IKVM.Runtime.ByteCodeHelper.arraycopy(src, srcPos, dest, destPos, length);
	}
}

static class Java_java_lang_Thread
{
	private static readonly object mainThreadGroup;

#if !FIRST_PASS
	static Java_java_lang_Thread()
	{
		mainThreadGroup = new java.lang.ThreadGroup(java.lang.ThreadGroup.createRootGroup(), "main");
	}
#endif

	public static object getMainThreadGroup()
	{
		return mainThreadGroup;
	}

	// this is called from JniInterface.cs
	internal static void WaitUntilLastJniThread()
	{
#if !FIRST_PASS
		int count = java.lang.Thread.currentThread().isDaemon() ? 0 : 1;
		while (Interlocked.CompareExchange(ref java.lang.Thread.nonDaemonCount[0], 0, 0) > count)
		{
			Thread.Sleep(1);
		}
#endif
	}

	// this is called from JniInterface.cs
	internal static void AttachThreadFromJni(object threadGroup)
	{
#if !FIRST_PASS
		if (threadGroup == null)
		{
			threadGroup = mainThreadGroup;
		}
		if (java.lang.Thread.current == null)
		{
			new java.lang.Thread((java.lang.ThreadGroup)threadGroup);
		}
#endif
	}

	public static java.lang.StackTraceElement[] getStackTrace(StackTrace stack)
	{
#if FIRST_PASS
		return null;
#else
		List<java.lang.StackTraceElement> stackTrace = new List<java.lang.StackTraceElement>();
		ExceptionHelper.ExceptionInfoHelper.Append(stackTrace, stack, 0, true);
		return stackTrace.ToArray();
#endif
	}

	public static object getThreads()
	{
#if FIRST_PASS
		return null;
#else
		return java.security.AccessController.doPrivileged(ikvm.runtime.Delegates.toPrivilegedAction(delegate
		{
			java.lang.ThreadGroup root = (java.lang.ThreadGroup)mainThreadGroup;
			for (; ; )
			{
				java.lang.Thread[] threads = new java.lang.Thread[root.activeCount()];
				if (root.enumerate(threads) == threads.Length)
				{
					return threads;
				}
			}
		}));
#endif
	}
}

static class Java_java_lang_ProcessImpl
{
	public static string mapVfsExecutable(string path)
	{
		string unquoted = path;
		if (unquoted.Length > 2 && unquoted[0] == '"' && unquoted[unquoted.Length - 1] == '"')
		{
			unquoted = unquoted.Substring(1, unquoted.Length - 2);
		}
		if (VirtualFileSystem.IsVirtualFS(unquoted))
		{
			return VirtualFileSystem.MapExecutable(unquoted);
		}
		return path;
	}

	public static int parseCommandString(string cmdstr)
	{
		int pos = cmdstr.IndexOf(' ');
		if (pos == -1)
		{
			return cmdstr.Length;
		}
		if (cmdstr[0] == '"')
		{
			int close = cmdstr.IndexOf('"', 1);
			return close == -1 ? cmdstr.Length : close + 1;
		}
		if (Environment.OSVersion.Platform != PlatformID.Win32NT)
		{
			return pos;
		}
		IList<string> path = null;
		for (; ; )
		{
			string str = cmdstr.Substring(0, pos);
			if (IsPathRooted(str))
			{
				if (Exists(str))
				{
					return pos;
				}
			}
			else
			{
				if (path == null)
				{
					path = GetSearchPath();
				}
				foreach (string p in path)
				{
					if (Exists(Path.Combine(p, str)))
					{
						return pos;
					}
				}
			}
			if (pos == cmdstr.Length)
			{
				return cmdstr.IndexOf(' ');
			}
			pos = cmdstr.IndexOf(' ', pos + 1);
			if (pos == -1)
			{
				pos = cmdstr.Length;
			}
		}
	}

	private static List<string> GetSearchPath()
	{
		List<string> list = new List<string>();
		list.Add(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
		list.Add(Environment.CurrentDirectory);
		list.Add(Environment.SystemDirectory);
		string windir = Path.GetDirectoryName(Environment.SystemDirectory);
		list.Add(Path.Combine(windir, "System"));
		list.Add(windir);
		string path = Environment.GetEnvironmentVariable("PATH");
		if (path != null)
		{
			foreach (string p in path.Split(Path.PathSeparator))
			{
				list.Add(p);
			}
		}
		return list;
	}

	private static bool IsPathRooted(string path)
	{
		try
		{
			return Path.IsPathRooted(path);
		}
		catch (ArgumentException)
		{
			return false;
		}
	}

	private static bool Exists(string file)
	{
		try
		{
			if (File.Exists(file))
			{
				return true;
			}
			else if (Directory.Exists(file))
			{
				return false;
			}
			else if (file.IndexOf('.') == -1 && File.Exists(file + ".exe"))
			{
				return true;
			}
			else if (mapVfsExecutable(file) != file)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		catch
		{
			return false;
		}
	}
}
