using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using NUnit.Framework;
using SIL.Reporting;
using SIL.Windows.Forms.Reporting;

namespace SIL.Windows.Forms.Tests.ErrorReporting
{

	[TestFixture]
	[Category("SkipOnTeamCity")]
	public class ErrorReportingTests
	{
		private bool Is64BitProcess;
		private bool HasLargePhysicalMemory;

		[TestFixtureSetUp]
		public void FixtureSetUp()
		{
			Is64BitProcess = IntPtr.Size == 8;
			HasLargePhysicalMemory = MemoryManagement.GetMemoryInformation().TotalPhysicalMemory >= 8192000000L;
		}

		[Test, Ignore("By hand only")]
		public void NotifyUserOfProblem_Message()
		{
			string message = "Oh no! This is quite a long message to see if it will wrap so I will have to keep typing to see if this will work now. And then some more.";
			ErrorReport.NotifyUserOfProblem(message);
		}

		[Test, Ignore("By hand only")]
		public void NotifyUserOfProblem_OncePerSession()
		{
			ShowOncePerSessionBasedOnExactMessagePolicy.Reset();
			string message = "Oh no! This is quite a long message to see if it will wrap so I will have to keep typing to see if this will work now. And then some more.";
			ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), message);

			ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), message);
		}

		[Test, Ignore("By hand only")]
		public void NotifyUserOfProblem_WithAlternateButton()
		{
			ShowOncePerSessionBasedOnExactMessagePolicy.Reset();
			string message = "Oh no! This is quite a long message to see if it will wrap so I will have to keep typing to see if this will work now. And then some more.";
			ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(),
											"&Caller Defined",
											DialogResult.Cancel,
											message);
		}

		[Test, Ignore("By hand only")]
		public void NotifyUserOfProblem_SmallMessage()
		{
			string message = "Oh no!";
			ErrorReport.NotifyUserOfProblem(message);
		}

		[Test, Ignore("By hand only")]
		public void NotifyUserOfProblem_SmallWithAlternateButton()
		{
			ShowOncePerSessionBasedOnExactMessagePolicy.Reset();
			string message = "Oh no!";
			ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(),
											"&Caller Defined",
											DialogResult.Cancel,
											message);
		}

		[Test, Ignore("By hand only")]
		public void NotifyUserOfProblem_ReallyLong()
		{
			string message = "Oh no! This is quite a long message to see if it will wrap so I will have to keep typing to see if this will work now. And then some more." +
				"And then keep going because I want to see what happens for a really long one," +
				"especially what happens with the resizing and if it works or not" + Environment.NewLine +
				Environment.NewLine + "and a newline as well.";
			ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), message);

			ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), message);
		}

		[Test]
		public void CheckMemory_NotMuchUsed_ReturnsFalse()
		{
			GC.Collect(); // In case another test (e.g, CheckMemory_1GUsed_ReturnsTrue) just used a lot
			Assert.That(MemoryManagement.CheckMemory(true, "not much done", false), Is.False);
		}

		[Test]
		public void CheckMemory_1GUsed_ReturnsTrue()
		{
			// We'll grab some big chunks but not demand we can get it all. Keep them small enough
			// to stay out of Large Object Heap
			var desiredNumberOfChunks = Is64BitProcess && HasLargePhysicalMemory ? 42000 : 21000;
			var chunks = new List<byte[]>();
			for (int i = 0; i < desiredNumberOfChunks; i++)
				chunks.Add(new byte[50000]);
			Assert.That(MemoryManagement.CheckMemory(true, "not much done", false), Is.True,
				"CheckMemory didn't detect danger");
		}

		[Test, Ignore("By hand only")]
		public void CheckMemory_1GUsed_DisplaysDialogOnlyOnce()
		{
			// We'll grab some big chunks but not demand we can get it all. Keep them small enough to stay out of Large Object Heap
			var chunks = new List<byte[]>();
			for (int i = 0; i < 21000; i++)
				chunks.Add(new byte[50000]);
			Assert.That(MemoryManagement.CheckMemory(true, "not much done", true), Is.True);
			// Doing it again should still return true, but you should NOT see the dialog twice.
			Assert.That(MemoryManagement.CheckMemory(true, "not much done", true), Is.True);
		}

		[Test]
		public void CheckMemory_WritesPlausibleNumbersToLogger()
		{
			Logger.Init();
			var dummy = Logger.LogText; // counter-intuitive, but the only way I can find to clear out minor events, in case anything else used logger
			MemoryManagement.CheckMemory(true, "this is a test", false); // since not doing GC we really can't predict the return value
			var result = Logger.MinorEventsLog;
			Assert.That(result, Is.StringContaining("this is a test"));
			var re = new Regex(@"\d+,\d+K");
			var matches = re.Matches(result).Cast<Match>().ToArray();
			// This is a pretty weak test; just proves we're outputting at least 3 numbers over 1000K. (Heap memory is sometimes <1M; virtual is unknown on Linux)
			Assert.That(matches, Has.Length.AtLeast(3));
			Logger.ShutDown();
			Logger.Init();
			MemoryManagement.CheckMemory(false, "this is a test", false); // since not doing GC we really can't predict the return value
			result = Logger.LogText;
			Assert.That(result, Is.StringContaining("this is a test"));
			matches = re.Matches(result).Cast<Match>().ToArray();
			// Using WriteEvent means out data is in the eventual output twice.
			Assert.That(matches, Has.Length.AtLeast(6));
		}

		[Test]
		public void GetMemoryInformation_ReturnsMemoryInformation()
		{
			var memInfo = MemoryManagement.GetMemoryInformation();
			Assert.Greater(memInfo.TotalPhysicalMemory, 0);
			Assert.Greater(memInfo.TotalVirtualMemory, 0);
		}
	}
}