/*
  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.Reflection;
using System.Text;
#if !NO_REF_EMIT
using System.Reflection.Emit;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.AccessControl;
using Microsoft.Win32.SafeHandles;
using IKVM.Internal;

static class Java_java_io_Console
{
	public static string encoding()
	{
		int cp = 437;
		try
		{
			cp = Console.InputEncoding.CodePage;
		}
		catch
		{
		}
		if (cp >= 874 && cp <= 950)
		{
			return "ms" + cp;
		}
		return "cp" + cp;
	}

	private const int STD_INPUT_HANDLE = -10;
	private const int ENABLE_ECHO_INPUT = 0x0004;

	[DllImport("kernel32")]
	private static extern IntPtr GetStdHandle(int nStdHandle);

	[DllImport("kernel32")]
	private static extern int GetConsoleMode(IntPtr hConsoleHandle, out int lpMode);

	[DllImport("kernel32")]
	private static extern int SetConsoleMode(IntPtr hConsoleHandle, int dwMode);

	public static bool echo(bool on)
	{
#if !FIRST_PASS
		// HACK the only way to get this to work is by p/invoking the Win32 APIs
		if (Environment.OSVersion.Platform == PlatformID.Win32NT)
		{
			IntPtr hStdIn = GetStdHandle(STD_INPUT_HANDLE);
			if (hStdIn.ToInt64() == 0 || hStdIn.ToInt64() == -1)
			{
				throw new java.io.IOException("The handle is invalid");
			}
			int fdwMode;
			if (GetConsoleMode(hStdIn, out fdwMode) == 0)
			{
				throw new java.io.IOException("GetConsoleMode failed");
			}
			bool old = (fdwMode & ENABLE_ECHO_INPUT) != 0;
			if (on)
			{
				fdwMode |= ENABLE_ECHO_INPUT;
			}
			else
			{
				fdwMode &= ~ENABLE_ECHO_INPUT;
			}
			if (SetConsoleMode(hStdIn, fdwMode) == 0)
			{
				throw new java.io.IOException("SetConsoleMode failed");
			}
			return old;
		}
#endif
		return true;
	}

	public static bool istty()
	{
		// The JDK returns false here if stdin or stdout (not stderr) is redirected to a file
		// or if there is no console associated with the current process.
		// The best we can do is to look at the KeyAvailable property, which
		// will throw an InvalidOperationException if stdin is redirected or not available
		try
		{
			return Console.KeyAvailable || true;
		}
		catch (InvalidOperationException)
		{
			return false;
		}
	}
}

static class Java_java_io_FileDescriptor
{
	private static Converter<int, int> fsync;

	public static Stream open(string name, FileMode fileMode, FileAccess fileAccess)
	{
		if (VirtualFileSystem.IsVirtualFS(name))
		{
			return VirtualFileSystem.Open(name, fileMode, fileAccess);
		}
		else if (fileMode == FileMode.Append)
		{
			// this is the way to get atomic append behavior for all writes
			return new FileStream(name, fileMode, FileSystemRights.AppendData, FileShare.ReadWrite, 1, FileOptions.None);
		}
		else
		{
			return new FileStream(name, fileMode, fileAccess, FileShare.ReadWrite, 1, false);
		}
	}

	[SecuritySafeCritical]
	public static bool flushPosix(FileStream fs)
	{
		if (fsync == null)
		{
			ResolveFSync();
		}
		bool success = false;
		SafeFileHandle handle = fs.SafeFileHandle;
		RuntimeHelpers.PrepareConstrainedRegions();
		try
		{
			handle.DangerousAddRef(ref success);
			return fsync(handle.DangerousGetHandle().ToInt32()) == 0;
		}
		finally
		{
			if (success)
			{
				handle.DangerousRelease();
			}
		}
	}

	[SecurityCritical]
	private static void ResolveFSync()
	{
		// we don't want a build time dependency on this Mono assembly, so we use reflection
		Type type = Type.GetType("Mono.Unix.Native.Syscall, Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756");
		if (type != null)
		{
			fsync = (Converter<int, int>)Delegate.CreateDelegate(typeof(Converter<int, int>), type, "fsync", false, false);
		}
		if (fsync == null)
		{
			fsync = DummyFSync;
		}
	}

	private static int DummyFSync(int fd)
	{
		return 0;
	}
}

static class Java_java_io_ObjectInputStream
{
	public static void bytesToFloats(byte[] src, int srcpos, float[] dst, int dstpos, int nfloats)
	{
		IKVM.Runtime.FloatConverter converter = new IKVM.Runtime.FloatConverter();
		for (int i = 0; i < nfloats; i++)
		{
			int v = src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			dst[dstpos++] = IKVM.Runtime.FloatConverter.ToFloat(v, ref converter);
		}
	}

	public static void bytesToDoubles(byte[] src, int srcpos, double[] dst, int dstpos, int ndoubles)
	{
		IKVM.Runtime.DoubleConverter converter = new IKVM.Runtime.DoubleConverter();
		for (int i = 0; i < ndoubles; i++)
		{
			long v = src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			v = (v << 8) | src[srcpos++];
			dst[dstpos++] = IKVM.Runtime.DoubleConverter.ToDouble(v, ref converter);
		}
	}
}

static class Java_java_io_ObjectOutputStream
{
	public static void floatsToBytes(float[] src, int srcpos, byte[] dst, int dstpos, int nfloats)
	{
		IKVM.Runtime.FloatConverter converter = new IKVM.Runtime.FloatConverter();
		for (int i = 0; i < nfloats; i++)
		{
			int v = IKVM.Runtime.FloatConverter.ToInt(src[srcpos++], ref converter);
			dst[dstpos++] = (byte)(v >> 24);
			dst[dstpos++] = (byte)(v >> 16);
			dst[dstpos++] = (byte)(v >> 8);
			dst[dstpos++] = (byte)(v >> 0);
		}
	}

	public static void doublesToBytes(double[] src, int srcpos, byte[] dst, int dstpos, int ndoubles)
	{
		IKVM.Runtime.DoubleConverter converter = new IKVM.Runtime.DoubleConverter();
		for (int i = 0; i < ndoubles; i++)
		{
			long v = IKVM.Runtime.DoubleConverter.ToLong(src[srcpos++], ref converter);
			dst[dstpos++] = (byte)(v >> 56);
			dst[dstpos++] = (byte)(v >> 48);
			dst[dstpos++] = (byte)(v >> 40);
			dst[dstpos++] = (byte)(v >> 32);
			dst[dstpos++] = (byte)(v >> 24);
			dst[dstpos++] = (byte)(v >> 16);
			dst[dstpos++] = (byte)(v >> 8);
			dst[dstpos++] = (byte)(v >> 0);
		}
	}
}

namespace IKVM.Internal
{
	public static class IOHelpers
	{
		public static void WriteByte(byte[] buf, int offset, byte value)
		{
			buf[offset] = value;
		}

		public static void WriteBoolean(byte[] buf, int offset, bool value)
		{
			buf[offset] = value ? (byte)1 : (byte)0;
		}

		public static void WriteChar(byte[] buf, int offset, char value)
		{
			buf[offset + 0] = (byte)(value >> 8);
			buf[offset + 1] = (byte)(value >> 0);
		}

		public static void WriteShort(byte[] buf, int offset, short value)
		{
			buf[offset + 0] = (byte)(value >> 8);
			buf[offset + 1] = (byte)(value >> 0);
		}

		public static void WriteInt(byte[] buf, int offset, int value)
		{
			buf[offset + 0] = (byte)(value >> 24);
			buf[offset + 1] = (byte)(value >> 16);
			buf[offset + 2] = (byte)(value >> 8);
			buf[offset + 3] = (byte)(value >> 0);
		}

		public static void WriteFloat(byte[] buf, int offset, float value)
		{
#if !FIRST_PASS
			java.io.Bits.putFloat(buf, offset, value);
#endif
		}

		public static void WriteLong(byte[] buf, int offset, long value)
		{
			WriteInt(buf, offset, (int)(value >> 32));
			WriteInt(buf, offset + 4, (int)value);
		}

		public static void WriteDouble(byte[] buf, int offset, double value)
		{
#if !FIRST_PASS
			java.io.Bits.putDouble(buf, offset, value);
#endif
		}

		public static byte ReadByte(byte[] buf, int offset)
		{
			return buf[offset];
		}

		public static bool ReadBoolean(byte[] buf, int offset)
		{
			return buf[offset] != 0;
		}

		public static char ReadChar(byte[] buf, int offset)
		{
			return (char)((buf[offset] << 8) + buf[offset + 1]);
		}

		public static short ReadShort(byte[] buf, int offset)
		{
			return (short)((buf[offset] << 8) + buf[offset + 1]);
		}

		public static int ReadInt(byte[] buf, int offset)
		{
			return (buf[offset + 0] << 24)
				 + (buf[offset + 1] << 16)
				 + (buf[offset + 2] << 8)
				 + (buf[offset + 3] << 0);
		}

		public static float ReadFloat(byte[] buf, int offset)
		{
#if FIRST_PASS
			return 0;
#else
			return java.lang.Float.intBitsToFloat(ReadInt(buf, offset));
#endif
		}

		public static long ReadLong(byte[] buf, int offset)
		{
			long hi = (uint)ReadInt(buf, offset);
			long lo = (uint)ReadInt(buf, offset + 4);
			return lo + (hi << 32);
		}

		public static double ReadDouble(byte[] buf, int offset)
		{
#if FIRST_PASS
			return 0;
#else
			return java.lang.Double.longBitsToDouble(ReadLong(buf, offset));
#endif
		}
	}
}

static class Java_java_io_ObjectStreamClass
{
	public static void initNative()
	{
	}

	public static bool isDynamicTypeWrapper(java.lang.Class cl)
	{
		TypeWrapper wrapper = TypeWrapper.FromClass(cl);
		return !wrapper.IsFastClassLiteralSafe;
	}

	public static bool hasStaticInitializer(java.lang.Class cl)
	{
		TypeWrapper wrapper = TypeWrapper.FromClass(cl);
		try
		{
			wrapper.Finish();
		}
		catch (RetargetableJavaException x)
		{
			throw x.ToJava();
		}
		Type type = wrapper.TypeAsTBD;
		if (!type.IsArray && type.TypeInitializer != null)
		{
			wrapper.RunClassInit();
			return !AttributeHelper.IsHideFromJava(type.TypeInitializer);
		}
		return false;
	}

#if !FIRST_PASS && !NO_REF_EMIT
	private sealed class FastFieldReflector : ikvm.@internal.FieldReflectorBase
	{
		private static readonly MethodInfo ReadByteMethod = typeof(IOHelpers).GetMethod("ReadByte");
		private static readonly MethodInfo ReadBooleanMethod = typeof(IOHelpers).GetMethod("ReadBoolean");
		private static readonly MethodInfo ReadCharMethod = typeof(IOHelpers).GetMethod("ReadChar");
		private static readonly MethodInfo ReadShortMethod = typeof(IOHelpers).GetMethod("ReadShort");
		private static readonly MethodInfo ReadIntMethod = typeof(IOHelpers).GetMethod("ReadInt");
		private static readonly MethodInfo ReadFloatMethod = typeof(IOHelpers).GetMethod("ReadFloat");
		private static readonly MethodInfo ReadLongMethod = typeof(IOHelpers).GetMethod("ReadLong");
		private static readonly MethodInfo ReadDoubleMethod = typeof(IOHelpers).GetMethod("ReadDouble");
		private static readonly MethodInfo WriteByteMethod = typeof(IOHelpers).GetMethod("WriteByte");
		private static readonly MethodInfo WriteBooleanMethod = typeof(IOHelpers).GetMethod("WriteBoolean");
		private static readonly MethodInfo WriteCharMethod = typeof(IOHelpers).GetMethod("WriteChar");
		private static readonly MethodInfo WriteShortMethod = typeof(IOHelpers).GetMethod("WriteShort");
		private static readonly MethodInfo WriteIntMethod = typeof(IOHelpers).GetMethod("WriteInt");
		private static readonly MethodInfo WriteFloatMethod = typeof(IOHelpers).GetMethod("WriteFloat");
		private static readonly MethodInfo WriteLongMethod = typeof(IOHelpers).GetMethod("WriteLong");
		private static readonly MethodInfo WriteDoubleMethod = typeof(IOHelpers).GetMethod("WriteDouble");
		private delegate void ObjFieldGetterSetter(object obj, object[] objarr);
		private delegate void PrimFieldGetterSetter(object obj, byte[] objarr);
		private static readonly ObjFieldGetterSetter objDummy = new ObjFieldGetterSetter(Dummy);
		private static readonly PrimFieldGetterSetter primDummy = new PrimFieldGetterSetter(Dummy);
		private java.io.ObjectStreamField[] fields;
		private ObjFieldGetterSetter objFieldGetter;
		private PrimFieldGetterSetter primFieldGetter;
		private ObjFieldGetterSetter objFieldSetter;
		private PrimFieldGetterSetter primFieldSetter;

		private static void Dummy(object obj, object[] objarr)
		{
		}

		private static void Dummy(object obj, byte[] barr)
		{
		}

		internal FastFieldReflector(java.io.ObjectStreamField[] fields)
		{
			this.fields = fields;
			TypeWrapper tw = null;
			foreach (java.io.ObjectStreamField field in fields)
			{
				FieldWrapper fw = GetFieldWrapper(field);
				if (fw != null)
				{
					if (tw == null)
					{
						tw = fw.DeclaringType;
					}
					else if (tw != fw.DeclaringType)
					{
						// pre-condition is that all fields are from the same Type!
						throw new java.lang.InternalError();
					}
				}
			}
			if (tw == null)
			{
				objFieldGetter = objFieldSetter = objDummy;
				primFieldGetter = primFieldSetter = primDummy;
			}
			else
			{
				try
				{
					tw.Finish();
				}
				catch (RetargetableJavaException x)
				{
					throw x.ToJava();
				}
				DynamicMethod dmObjGetter = DynamicMethodUtils.Create("__<ObjFieldGetter>", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(object[]) });
				DynamicMethod dmPrimGetter = DynamicMethodUtils.Create("__<PrimFieldGetter>", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(byte[]) });
				DynamicMethod dmObjSetter = DynamicMethodUtils.Create("__<ObjFieldSetter>", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(object[]) });
				DynamicMethod dmPrimSetter = DynamicMethodUtils.Create("__<PrimFieldSetter>", tw.TypeAsBaseType, true, null, new Type[] { typeof(object), typeof(byte[]) });
				CodeEmitter ilgenObjGetter = CodeEmitter.Create(dmObjGetter);
				CodeEmitter ilgenPrimGetter = CodeEmitter.Create(dmPrimGetter);
				CodeEmitter ilgenObjSetter = CodeEmitter.Create(dmObjSetter);
				CodeEmitter ilgenPrimSetter = CodeEmitter.Create(dmPrimSetter);

				// we want the getters to be verifiable (because writeObject can be used from partial trust),
				// so we create a local to hold the properly typed object reference
				CodeEmitterLocal objGetterThis = ilgenObjGetter.DeclareLocal(tw.TypeAsBaseType);
				CodeEmitterLocal primGetterThis = ilgenPrimGetter.DeclareLocal(tw.TypeAsBaseType);
				ilgenObjGetter.Emit(OpCodes.Ldarg_0);
				ilgenObjGetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType);
				ilgenObjGetter.Emit(OpCodes.Stloc, objGetterThis);
				ilgenPrimGetter.Emit(OpCodes.Ldarg_0);
				ilgenPrimGetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType);
				ilgenPrimGetter.Emit(OpCodes.Stloc, primGetterThis);

				foreach (java.io.ObjectStreamField field in fields)
				{
					FieldWrapper fw = GetFieldWrapper(field);
					if (fw == null)
					{
						continue;
					}
					fw.ResolveField();
					TypeWrapper fieldType = fw.FieldTypeWrapper;
					try
					{
						fieldType = fieldType.EnsureLoadable(tw.GetClassLoader());
						fieldType.Finish();
					}
					catch (RetargetableJavaException x)
					{
						throw x.ToJava();
					}
					if (fieldType.IsPrimitive)
					{
						// Getter
						ilgenPrimGetter.Emit(OpCodes.Ldarg_1);
						ilgenPrimGetter.EmitLdc_I4(field.getOffset());
						ilgenPrimGetter.Emit(OpCodes.Ldloc, primGetterThis);
						fw.EmitGet(ilgenPrimGetter);
						if (fieldType == PrimitiveTypeWrapper.BYTE)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteByteMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.BOOLEAN)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteBooleanMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.CHAR)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteCharMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.SHORT)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteShortMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.INT)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteIntMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.FLOAT)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteFloatMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.LONG)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteLongMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.DOUBLE)
						{
							ilgenPrimGetter.Emit(OpCodes.Call, WriteDoubleMethod);
						}
						else
						{
							throw new java.lang.InternalError();
						}

						// Setter
						ilgenPrimSetter.Emit(OpCodes.Ldarg_0);
						ilgenPrimSetter.Emit(OpCodes.Castclass, tw.TypeAsBaseType);
						ilgenPrimSetter.Emit(OpCodes.Ldarg_1);
						ilgenPrimSetter.EmitLdc_I4(field.getOffset());
						if (fieldType == PrimitiveTypeWrapper.BYTE)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadByteMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.BOOLEAN)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadBooleanMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.CHAR)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadCharMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.SHORT)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadShortMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.INT)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadIntMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.FLOAT)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadFloatMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.LONG)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadLongMethod);
						}
						else if (fieldType == PrimitiveTypeWrapper.DOUBLE)
						{
							ilgenPrimSetter.Emit(OpCodes.Call, ReadDoubleMethod);
						}
						else
						{
							throw new java.lang.InternalError();
						}
						fw.EmitSet(ilgenPrimSetter);
					}
					else
					{
						// Getter
						ilgenObjGetter.Emit(OpCodes.Ldarg_1);
						ilgenObjGetter.EmitLdc_I4(field.getOffset());
						ilgenObjGetter.Emit(OpCodes.Ldloc, objGetterThis);
						fw.EmitGet(ilgenObjGetter);
						fieldType.EmitConvSignatureTypeToStackType(ilgenObjGetter);
						ilgenObjGetter.Emit(OpCodes.Stelem_Ref);

						// Setter
						ilgenObjSetter.Emit(OpCodes.Ldarg_0);
						ilgenObjSetter.Emit(OpCodes.Ldarg_1);
						ilgenObjSetter.EmitLdc_I4(field.getOffset());
						ilgenObjSetter.Emit(OpCodes.Ldelem_Ref);
						fieldType.EmitCheckcast(ilgenObjSetter);
						fieldType.EmitConvStackTypeToSignatureType(ilgenObjSetter, null);
						fw.EmitSet(ilgenObjSetter);
					}
				}
				ilgenObjGetter.Emit(OpCodes.Ret);
				ilgenPrimGetter.Emit(OpCodes.Ret);
				ilgenObjSetter.Emit(OpCodes.Ret);
				ilgenPrimSetter.Emit(OpCodes.Ret);
				ilgenObjGetter.DoEmit();
				ilgenPrimGetter.DoEmit();
				ilgenObjSetter.DoEmit();
				ilgenPrimSetter.DoEmit();
				objFieldGetter = (ObjFieldGetterSetter)dmObjGetter.CreateDelegate(typeof(ObjFieldGetterSetter));
				primFieldGetter = (PrimFieldGetterSetter)dmPrimGetter.CreateDelegate(typeof(PrimFieldGetterSetter));
				objFieldSetter = (ObjFieldGetterSetter)dmObjSetter.CreateDelegate(typeof(ObjFieldGetterSetter));
				primFieldSetter = (PrimFieldGetterSetter)dmPrimSetter.CreateDelegate(typeof(PrimFieldGetterSetter));
			}
		}

		private static FieldWrapper GetFieldWrapper(java.io.ObjectStreamField field)
		{
			java.lang.reflect.Field f = field.getField();
			return f == null ? null : FieldWrapper.FromField(f);
		}

		public override java.io.ObjectStreamField[] getFields()
		{
			return fields;
		}

		public override void getObjFieldValues(object obj, object[] objarr)
		{
			objFieldGetter(obj, objarr);
		}

		public override void setObjFieldValues(object obj, object[] objarr)
		{
			objFieldSetter(obj, objarr);
		}

		public override void getPrimFieldValues(object obj, byte[] barr)
		{
			primFieldGetter(obj, barr);
		}

		public override void setPrimFieldValues(object obj, byte[] barr)
		{
			primFieldSetter(obj, barr);
		}
	}
#endif // !FIRST_PASS && !NO_REF_EMIT

	public static object getFastFieldReflector(java.io.ObjectStreamField[] fieldsObj)
	{
#if FIRST_PASS || NO_REF_EMIT
		return null;
#else
		return new FastFieldReflector(fieldsObj);
#endif
	}
}

static class Java_java_io_WinNTFileSystem
{
	internal const int ACCESS_READ = 0x04;
	const int ACCESS_WRITE = 0x02;
	const int ACCESS_EXECUTE = 0x01;

	public static string getDriveDirectory(object _this, int drive)
	{
		try
		{
			string path = ((char)('A' + (drive - 1))) + ":";
			return Path.GetFullPath(path).Substring(2);
		}
		catch (ArgumentException)
		{
		}
		catch (SecurityException)
		{
		}
		catch (PathTooLongException)
		{
		}
		return "\\";
	}

	private static string CanonicalizePath(string path)
	{
		try
		{
			FileInfo fi = new FileInfo(path);
			if (fi.DirectoryName == null)
			{
				return path.Length > 1 && path[1] == ':'
					? (Char.ToUpper(path[0]) + ":" + Path.DirectorySeparatorChar)
					: path;
			}
			string dir = CanonicalizePath(fi.DirectoryName);
			string name = fi.Name;
			try
			{
				if (!VirtualFileSystem.IsVirtualFS(path))
				{
					string[] arr = Directory.GetFileSystemEntries(dir, name);
					if (arr.Length == 1)
					{
						name = arr[0];
					}
				}
			}
			catch (UnauthorizedAccessException)
			{
			}
			catch (IOException)
			{
			}
			return Path.Combine(dir, name);
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (SecurityException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return path;
	}

	public static string canonicalize0(object _this, string path)
	{
#if FIRST_PASS
		return null;
#else
		try
		{
			// TODO there is still a known bug here. A dotted path component right after the root component
			// are not removed as they should be. E.g. "c:\..." => "C:\..." or "\\server\..." => IOException
			// Another know issue is that when running under Mono on Windows, the case names aren't converted
			// to the correct (on file system) casing.
			//
			// FXBUG we're appending the directory separator to work around an apparent .NET bug.
			// If we don't do this, "c:\j\." would be canonicalized to "C:\"
			int colon = path.IndexOf(':', 2);
			if (colon != -1)
			{
				return CanonicalizePath(path.Substring(0, colon) + Path.DirectorySeparatorChar) + path.Substring(colon);
			}
			return CanonicalizePath(path + Path.DirectorySeparatorChar);
		}
		catch (ArgumentException x)
		{
			throw new java.io.IOException(x.Message);
		}
#endif
	}

	public static string canonicalizeWithPrefix0(object _this, string canonicalPrefix, string pathWithCanonicalPrefix)
	{
		return canonicalize0(_this, pathWithCanonicalPrefix);
	}

	private static string GetPathFromFile(java.io.File file)
	{
#if FIRST_PASS
		return null;
#else
		return file.getPath();
#endif
	}

	public static int getBooleanAttributes(object _this, java.io.File f)
	{
		try
		{
			string path = GetPathFromFile(f);
			if (VirtualFileSystem.IsVirtualFS(path))
			{
				return VirtualFileSystem.GetBooleanAttributes(path);
			}
			FileAttributes attr = File.GetAttributes(path);
			const int BA_EXISTS = 0x01;
			const int BA_REGULAR = 0x02;
			const int BA_DIRECTORY = 0x04;
			const int BA_HIDDEN = 0x08;
			int rv = BA_EXISTS;
			if ((attr & FileAttributes.Directory) != 0)
			{
				rv |= BA_DIRECTORY;
			}
			else
			{
				rv |= BA_REGULAR;
			}
			if ((attr & FileAttributes.Hidden) != 0)
			{
				rv |= BA_HIDDEN;
			}
			return rv;
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (SecurityException)
		{
		}
		catch (NotSupportedException)
		{
		}
		catch (IOException)
		{
		}
		return 0;
	}

	public static bool checkAccess(object _this, java.io.File f, int access)
	{
		string path = GetPathFromFile(f);
		if (VirtualFileSystem.IsVirtualFS(path))
		{
			return VirtualFileSystem.CheckAccess(path, access);
		}
		bool ok = true;
		if ((access & (ACCESS_READ | ACCESS_EXECUTE)) != 0)
		{
			ok = false;
			try
			{
				// HACK if path refers to a directory, we always return true
				if (!Directory.Exists(path))
				{
					new FileInfo(path).Open(
						FileMode.Open,
						FileAccess.Read,
						FileShare.ReadWrite).Close();
				}
				ok = true;
			}
			catch (SecurityException)
			{
			}
			catch (ArgumentException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
			catch (IOException)
			{
			}
			catch (NotSupportedException)
			{
			}
		}
		if (ok && ((access & ACCESS_WRITE) != 0))
		{
			ok = false;
			try
			{
				// HACK if path refers to a directory, we always return true
				if (Directory.Exists(path))
				{
					ok = true;
				}
				else
				{
					FileInfo fileInfo = new FileInfo(path);
					// Like the JDK we'll only look at the read-only attribute and not
					// the security permissions associated with the file or directory.
					ok = (fileInfo.Attributes & FileAttributes.ReadOnly) == 0;
				}
			}
			catch (SecurityException)
			{
			}
			catch (ArgumentException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
			catch (IOException)
			{
			}
			catch (NotSupportedException)
			{
			}
		}
		return ok;
	}

	private static long DateTimeToJavaLongTime(DateTime datetime)
	{
		return (TimeZone.CurrentTimeZone.ToUniversalTime(datetime) - new DateTime(1970, 1, 1)).Ticks / 10000L;
	}

	private static DateTime JavaLongTimeToDateTime(long datetime)
	{
		return TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(new DateTime(1970, 1, 1).Ticks + datetime * 10000L));
	}

	public static long getLastModifiedTime(object _this, java.io.File f)
	{
		try
		{
			DateTime dt = File.GetLastWriteTime(GetPathFromFile(f));
			if (dt.ToFileTime() == 0)
			{
				return 0;
			}
			else
			{
				return DateTimeToJavaLongTime(dt);
			}
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return 0;
	}

	public static long getLength(object _this, java.io.File f)
	{
		try
		{
			string path = GetPathFromFile(f);
			if (VirtualFileSystem.IsVirtualFS(path))
			{
				return VirtualFileSystem.GetLength(path);
			}
			return new FileInfo(path).Length;
		}
		catch (SecurityException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return 0;
	}

	public static bool setPermission(object _this, java.io.File f, int access, bool enable, bool owneronly)
	{
		if ((access & ACCESS_WRITE) != 0)
		{
			try
			{
				FileInfo file = new FileInfo(GetPathFromFile(f));
				if (enable)
				{
					file.Attributes &= ~FileAttributes.ReadOnly;
				}
				else
				{
					file.Attributes |= FileAttributes.ReadOnly;
				}
				return true;
			}
			catch (SecurityException)
			{
			}
			catch (ArgumentException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
			catch (IOException)
			{
			}
			catch (NotSupportedException)
			{
			}
			return false;
		}
		return enable;
	}

	public static bool createFileExclusively(object _this, string path)
	{
#if !FIRST_PASS
		try
		{
			File.Open(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None).Close();
			return true;
		}
		catch (ArgumentException x)
		{
			throw new java.io.IOException(x.Message);
		}
		catch (IOException x)
		{
			if (!File.Exists(path) && !Directory.Exists(path))
			{
				throw new java.io.IOException(x.Message);
			}
		}
		catch (UnauthorizedAccessException x)
		{
			if (!File.Exists(path) && !Directory.Exists(path))
			{
				throw new java.io.IOException(x.Message);
			}
		}
		catch (NotSupportedException x)
		{
			throw new java.io.IOException(x.Message);
		}
#endif
		return false;
	}

	public static bool delete0(object _this, java.io.File f)
	{
		FileSystemInfo fileInfo = null;
		try
		{
			string path = GetPathFromFile(f);
			if (Directory.Exists(path))
			{
				fileInfo = new DirectoryInfo(path);
			}
			else if (File.Exists(path))
			{
				fileInfo = new FileInfo(path);
			}
			else
			{
				return false;
			}
			// We need to be able to delete read-only files/dirs too, so we clear
			// the read-only attribute, if set.
			if ((fileInfo.Attributes & FileAttributes.ReadOnly) != 0)
			{
				fileInfo.Attributes &= ~FileAttributes.ReadOnly;
			}
			fileInfo.Delete();
			return true;
		}
		catch (SecurityException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return false;
	}

	public static string[] list(object _this, java.io.File f)
	{
		try
		{
			string path = GetPathFromFile(f);
			if (VirtualFileSystem.IsVirtualFS(path))
			{
				return VirtualFileSystem.List(path);
			}
			string[] l = Directory.GetFileSystemEntries(path);
			for (int i = 0; i < l.Length; i++)
			{
				int pos = l[i].LastIndexOf(Path.DirectorySeparatorChar);
				if (pos >= 0)
				{
					l[i] = l[i].Substring(pos + 1);
				}
			}
			return l;
		}
		catch (ArgumentException)
		{
		}
		catch (IOException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return null;
	}

	public static bool createDirectory(object _this, java.io.File f)
	{
		try
		{
			string path = GetPathFromFile(f);
			DirectoryInfo parent = Directory.GetParent(path);
			if (parent == null ||
				!Directory.Exists(parent.FullName) ||
				Directory.Exists(path))
			{
				return false;
			}
			return Directory.CreateDirectory(path) != null;
		}
		catch (SecurityException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return false;
	}

	public static bool rename0(object _this, java.io.File f1, java.io.File f2)
	{
		try
		{
			new FileInfo(GetPathFromFile(f1)).MoveTo(GetPathFromFile(f2));
			return true;
		}
		catch (SecurityException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return false;
	}

	public static bool setLastModifiedTime(object _this, java.io.File f, long time)
	{
		try
		{
			new FileInfo(GetPathFromFile(f)).LastWriteTime = JavaLongTimeToDateTime(time);
			return true;
		}
		catch (SecurityException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return false;
	}

	public static bool setReadOnly(object _this, java.io.File f)
	{
		try
		{
			FileInfo fileInfo = new FileInfo(GetPathFromFile(f));
			fileInfo.Attributes |= FileAttributes.ReadOnly;
			return true;
		}
		catch (SecurityException)
		{
		}
		catch (ArgumentException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (IOException)
		{
		}
		catch (NotSupportedException)
		{
		}
		return false;
	}

	public static int listRoots0()
	{
		try
		{
			int drives = 0;
			foreach (string drive in Environment.GetLogicalDrives())
			{
				char c = Char.ToUpper(drive[0]);
				drives |= 1 << (c - 'A');
			}
			return drives;
		}
		catch (IOException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		catch (SecurityException)
		{
		}
		return 0;
	}

	[SecuritySafeCritical]
	public static long getSpace0(object _this, java.io.File f, int t)
	{
#if !FIRST_PASS
		long freeAvailable;
		long total;
		long totalFree;
		StringBuilder volname = new StringBuilder(256);
		if (GetVolumePathName(GetPathFromFile(f), volname, volname.Capacity) != 0
			&& GetDiskFreeSpaceEx(volname.ToString(), out freeAvailable, out total, out totalFree) != 0)
		{
			switch (t)
			{
				case java.io.FileSystem.SPACE_TOTAL:
					return total;
				case java.io.FileSystem.SPACE_FREE:
					return totalFree;
				case java.io.FileSystem.SPACE_USABLE:
					return freeAvailable;
			}
		}
#endif
		return 0;
	}

	[DllImport("kernel32")]
	private static extern int GetDiskFreeSpaceEx(string directory, out long freeAvailable, out long total, out long totalFree);

	[DllImport("kernel32")]
	private static extern int GetVolumePathName(string lpszFileName, [In, Out] StringBuilder lpszVolumePathName, int cchBufferLength);

	public static void initIDs()
	{
	}
}

static class Java_java_io_UnixFileSystem
{
	public static int getBooleanAttributes0(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.getBooleanAttributes(_this, f);
	}

	public static long getSpace(object _this, java.io.File f, int t)
	{
		// TODO
		return 0;
	}

	public static string canonicalize0(object _this, string path)
	{
		return Java_java_io_WinNTFileSystem.canonicalize0(_this, path);
	}

	public static bool checkAccess(object _this, java.io.File f, int access)
	{
		return Java_java_io_WinNTFileSystem.checkAccess(_this, f, access);
	}

	public static long getLastModifiedTime(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.getLastModifiedTime(_this, f);
	}

	public static long getLength(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.getLength(_this, f);
	}

	public static bool setPermission(object _this, java.io.File f, int access, bool enable, bool owneronly)
	{
		// TODO consider using Mono.Posix
		return Java_java_io_WinNTFileSystem.setPermission(_this, f, access, enable, owneronly);
	}

	public static bool createFileExclusively(object _this, string path)
	{
		return Java_java_io_WinNTFileSystem.createFileExclusively(_this, path);
	}

	public static bool delete0(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.delete0(_this, f);
	}

	public static string[] list(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.list(_this, f);
	}

	public static bool createDirectory(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.createDirectory(_this, f);
	}

	public static bool rename0(object _this, java.io.File f1, java.io.File f2)
	{
		return Java_java_io_WinNTFileSystem.rename0(_this, f1, f2);
	}

	public static bool setLastModifiedTime(object _this, java.io.File f, long time)
	{
		return Java_java_io_WinNTFileSystem.setLastModifiedTime(_this, f, time);
	}

	public static bool setReadOnly(object _this, java.io.File f)
	{
		return Java_java_io_WinNTFileSystem.setReadOnly(_this, f);
	}

	public static void initIDs()
	{
	}
}
