using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace miew.Memory
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[Serializable]
	public sealed class WeakReference<T> : WeakReference
	{
		public WeakReference(T target)
			: base(target)
		{
		}
		public WeakReference(T target, bool trackResurrection)
			: base(target, trackResurrection)
		{
		}
		public new T Target { get { return (T)base.Target; } }
	};

	public class AdjPriv
	{
		[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
		internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

		[DllImport("kernel32.dll", ExactSpelling = true)]
		internal static extern IntPtr GetCurrentProcess();

		[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
		internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);

		[DllImport("advapi32.dll", SetLastError = true)]
		internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		internal struct TokPriv1Luid
		{
			public int Count;
			public long Luid;
			public int Attr;
		}

		internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
		internal const int TOKEN_QUERY = 0x00000008;
		internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;

		public static bool SetPriv(System.String sz_priv)
		{
			bool retVal;
			TokPriv1Luid tp;
			IntPtr hproc = GetCurrentProcess();
			IntPtr htok = IntPtr.Zero;
			retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
			tp.Count = 1;
			tp.Luid = 0;
			tp.Attr = SE_PRIVILEGE_ENABLED;
			retVal = LookupPrivilegeValue(null, sz_priv, ref tp.Luid);
			retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
			return retVal;
		}
	};

	public struct InsituArray16<T> where T:class
	{
		T t00;
		T t01;
		T t02;
		T t03;
		T t04;
		T t05;
		T t06;
		T t07;
		T t08;
		T t09;
		T t10;
		T t11;
		T t12;
		T t13;
		T t14;
		T t15;
		T t16;
		byte m_c;

		public byte Add(T t)
		{
			if (m_c >= 16)
				throw new InvalidOperationException();
			byte c = m_c++;
			this[c] = t;
			return c;
		}

		public T this[int ix]
		{
			get
			{
				if (ix >= m_c)
					throw new IndexOutOfRangeException();
				switch (ix)
				{
					default:
						throw new IndexOutOfRangeException();
					case 0:
						return t00;
					case 1:
						return t01;
					case 2:
						return t02;
					case 3:
						return t03;
					case 4:
						return t04;
					case 5:
						return t05;
					case 6:
						return t06;
					case 7:
						return t07;
					case 8:
						return t08;
					case 9:
						return t09;
					case 10:
						return t10;
					case 11:
						return t11;
					case 12:
						return t12;
					case 13:
						return t13;
					case 14:
						return t14;
					case 15:
						return t15;
					case 16:
						return t16;
				}
			}
			set
			{
				if (ix >= m_c)
					throw new IndexOutOfRangeException();
				switch (ix)
				{
					default:
						throw new IndexOutOfRangeException();
					case 0:
						t00 = value;
						break;
					case 1:
						t01 = value;
						break;
					case 2:
						t02 = value;
						break;
					case 3:
						t03 = value;
						break;
					case 4:
						t04 = value;
						break;
					case 5:
						t05 = value;
						break;
					case 6:
						t06 = value;
						break;
					case 7:
						t07 = value;
						break;
					case 8:
						t08 = value;
						break;
					case 9:
						t09 = value;
						break;
					case 10:
						t10 = value;
						break;
					case 11:
						t11 = value;
						break;
					case 12:
						t12 = value;
						break;
					case 13:
						t13 = value;
						break;
					case 14:
						t14 = value;
						break;
					case 15:
						t15 = value;
						break;
					case 16:
						t16 = value;
						break;
				}
			}
		}
	};

	//if (f_large)
	//{
	//    if (!AdjPriv.SetPriv("SeLockMemoryPrivilege"))
	//        throw new Exception();
	//}

	static public class Virtual
	{
		[Flags()]
		public enum AllocationType : uint
		{
			COMMIT = 0x1000,
			RESERVE = 0x2000,
			RESET = 0x80000,
			LARGE_PAGES = 0x20000000,
			PHYSICAL = 0x400000,
			TOP_DOWN = 0x100000,
			WRITE_WATCH = 0x200000
		};

		[Flags()]
		public enum MemoryProtection : uint
		{
			EXECUTE = 0x10,
			EXECUTE_READ = 0x20,
			EXECUTE_READWRITE = 0x40,
			EXECUTE_WRITECOPY = 0x80,
			NOACCESS = 0x01,
			READONLY = 0x02,
			READWRITE = 0x04,
			WRITECOPY = 0x08,
			GUARD_Modifierflag = 0x100,
			NOCACHE_Modifierflag = 0x200,
			WRITECOMBINE_Modifierflag = 0x400
		};

		[StructLayout(LayoutKind.Sequential)]
		public struct SYSTEM_INFO
		{
			public ushort processorArchitecture;
			ushort reserved;
			public uint pageSize;
			public IntPtr minimumApplicationAddress;
			public IntPtr maximumApplicationAddress;
			public IntPtr activeProcessorMask;
			public uint numberOfProcessors;
			public uint processorType;
			public uint allocationGranularity;
			public ushort processorLevel;
			public ushort processorRevision;
		}

		[DllImport("kernel32", SetLastError = true)]
		public static extern IntPtr VirtualAlloc(IntPtr lpStartAddr, UIntPtr size, AllocationType flAllocationType, MemoryProtection flProtect);

		[DllImport("kernel32", SetLastError = true)]
		public static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, uint dwFreeType);

		[DllImport("kernel32.dll")]
		public static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);

#if VIRTUAL_ALLOC
			long cb = (long)2 * 1024 * 1024 * 1024;
			pate_base = (arr_tfs_entry*)VirtualAlloc(IntPtr.Zero, new UIntPtr((ulong)cb), AllocationType.RESERVE, MemoryProtection.READWRITE);
			//GC.AddMemoryPressure(cb);
			c_ate = 0;
#endif

#if VIRTUAL_ALLOC
		long c_ate;
		ulong c_commit;
		arr_tfs_entry* pate_base;
#endif

#if VIRTUAL_ALLOC
		static ulong page_mask;
		static ChartBase()
		{
			SYSTEM_INFO si;
			GetSystemInfo(out si);
			page_mask = ~(si.pageSize - 1);
		}

		public arr_tfs_entry* GetEdgeStorage(long c)
		{
			arr_tfs_entry* p_end = &pate_base[Interlocked.Add(ref c_ate, c)];
			arr_tfs_entry* p = p_end - c;

			long cb = (byte*)p_end - (byte*)p;

			ulong page = ((ulong)p_end - (ulong)pate_base) >> 12;
			if (page >= c_commit)
			{
				c_commit = page + 1;
				VirtualAlloc((IntPtr)pate_base, new UIntPtr((ulong)c_commit << 12), AllocationType.COMMIT, MemoryProtection.READWRITE);
			}

			GC.AddMemoryPressure(cb);
			return p;
		}

		public void Dispose()
		{
			if (pate_base != null)
			{
				long cb = (byte*)(pate_base + c_ate) - (byte*)pate_base;

				VirtualFree((IntPtr)pate_base, UIntPtr.Zero, 0x8000);
				pate_base = null;
				GC.RemoveMemoryPressure(cb);


				//long cb2 = (long)2 * 1024 * 1024 * 1024;
				//GC.RemoveMemoryPressure(cb2);
			}
		}
#endif
	};
}