using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Text;

namespace p4api.net.unit.test
{
	class Utilities
	{
		public static void ClobberDirectory( String path )
		{
			DirectoryInfo di = new DirectoryInfo( path );

			ClobberDirectory( di );
		}

		public static void ClobberDirectory(DirectoryInfo di)
		{
//			Process Zapper = Process.Start("rd", "/S /Q " + di.FullName);

			string comSpec = Environment.GetEnvironmentVariable("ComSpec");
			Process Zapper = new Process();
			ProcessStartInfo si = new ProcessStartInfo(comSpec, "/c rd /S /Q " + di.FullName);
			si.WorkingDirectory = Path.GetDirectoryName(di.FullName);
			si.UseShellExecute = true;
			Zapper.StartInfo = si;
			try
			{
				Zapper.Start();
			}
			catch (Exception ex)
			{
				
				LogMessage("In ClobberDirectory, Zapper.Start() failed: {0}", ex.Message);
			}
			if (Zapper.HasExited == false)
			{
				Zapper.WaitForExit();
			}
			if (Directory.Exists(di.FullName))
			{
				bool worked = false;
				int retries = 0;
				do
				{
					if (!di.Exists)
						return;

					try
					{
						FileInfo[] files = di.GetFiles();

						foreach (FileInfo fi in files)
						{
							if (fi.IsReadOnly)
								fi.IsReadOnly = false;
							fi.Delete();
						}
						DirectoryInfo[] subDirs = di.GetDirectories();
						foreach (DirectoryInfo sdi in subDirs)
						{
							ClobberDirectory(sdi);
						}

						di.Delete();

						worked = true;
					}
					catch (Exception)
					{
						System.Threading.Thread.Sleep(100);
					}
					retries++;
				}
				while (!worked && retries < 2);
			}
		}

        static String p4d_cmd = "-p localhost:6666 -Id UnitTestServer -r {0}";
        static String restore_cmd = "-r {0} -jr checkpoint.{1}";
        static String upgrade_cmd = "-r {0} -xu";
        static String generate_key_cmd = string.Empty;

		static string rubbishBin = "c:\\MyTestDir-rubbish-bin";

        public static Process DeploySSLP4TestServer(string path, bool Unicode)
        {
            System.Environment.SetEnvironmentVariable("P4SSLDIR", path);
            string test = System.Environment.GetEnvironmentVariable("P4SSLDIR");
            p4d_cmd = "-p ssl:6666 -Id UnitTestServer -r {0}";
            generate_key_cmd = "-Gc";
            return DeployP4TestServer(path, 1, Unicode);
        }

        public static Process DeployIPv6P4TestServer(string path, string tcp, bool Unicode)
        {
            p4d_cmd = "-p " + tcp + ":::1:6666 -Id UnitTestServer -r {0}";
            return DeployP4TestServer(path, 1, Unicode);
        }

		public static Process DeployP4TestServer(string path, bool Unicode)
		{
			return DeployP4TestServer(path, 1, Unicode);
		}

		public static Process DeployP4TestServer(string path, int checkpointRev, bool Unicode)
		{
			String zippedFile = "a.exe";
			if (Unicode)
			{
				zippedFile = "u.exe";
			}

			return DeployP4TestServer(path, checkpointRev, zippedFile);
		}

		public static Process DeployP4TestServer(string path, int checkpointRev, string zippedFile)
		{
            return DeployP4TestServer(path, checkpointRev, zippedFile, null);
		}

		public static Process DeployP4TestServer(string path, int checkpointRev, string zippedFile, string P4DCmd  )
		{
			if (Directory.Exists(rubbishBin) == false)
			{
				Directory.CreateDirectory(rubbishBin);
			}
			
			//			path = Path.Combine(path, DateTime.Now.Ticks.ToString());
			int retries = 10;
			string rubbishDirectory = Path.Combine(rubbishBin, DateTime.Now.Ticks.ToString());

			try
			{
				retries = 2;
				while ((Directory.Exists(path)) && (retries > 0))
				{
					try
					{
						// Try to rename it
						Directory.Move(path, rubbishDirectory);
						//must have worked
						break;
					}
					catch
					{
						retries--;
						System.Threading.Thread.Sleep(1000);
						if (retries <= 0)
						{
							throw;
							//throw new Exception("Couldn.t move test directory after 10 tries");
						}
					}
				}
			}

			catch (Exception ex)
			{
				LogMessage("In DeployP4TestServer, Directory.Move failed: {0}", ex.Message);
				// rename failed, try to clobber it (can be slow so last resort)
				Utilities.ClobberDirectory(path);
			}

			string assemblyFile = typeof(Utilities).Assembly.CodeBase;
			String unitTestDir = Path.GetDirectoryName(assemblyFile);

			String EnvPath = Environment.GetEnvironmentVariable("path");
			String CurWDir = Environment.CurrentDirectory;

            retries = 10;
            while ((Directory.Exists(path) == false) && (retries > 0))
            {
                try
                {
                    Directory.CreateDirectory(path);
                    if (Directory.Exists(path))
                    {
                        break;
                    }
                    retries--;
                    System.Threading.Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    retries--;
                    bool dirExists = Directory.Exists(path);
                    Trace.WriteLine(ex.Message);
                    if (dirExists)
                    {
                        break;
                    }
                    System.Threading.Thread.Sleep(200);
                }
            }
            try
            {
                //Directory.CreateDirectory(path);
			Environment.CurrentDirectory = path;
            }
            catch (Exception ex)
            {
                bool dirExists = Directory.Exists(path);
                Trace.WriteLine(ex.Message);
                return null;
            }
			using (StreamWriter sw = new StreamWriter("CmdLog.txt", false))
			{
				int idx;
				if (unitTestDir.ToLower().StartsWith("file:\\"))
				{
					// cut off the file:\\
					idx = unitTestDir.IndexOf("\\") + 1;
					unitTestDir = unitTestDir.Substring(idx);
				}
				if ((idx = unitTestDir.IndexOf("TestResults")) > 0)
				{
					unitTestDir = unitTestDir.Substring(0, idx);
					if (unitTestDir.ToLower().Contains("bin\\debug") == false)
					{
						unitTestDir = Path.Combine(unitTestDir, "bin\\debug");
					}
				}

				string unitTestZip = Path.Combine(unitTestDir, zippedFile);
				string targetTestZip = Path.Combine(path, zippedFile);

				retries = 3;
				int delay = 1000; // initial delay 1 second
				while (retries > 0)
				{
					try
					{
						File.Copy(unitTestZip, targetTestZip);
						break; //success
					}
					catch (Exception ex)
					{
						System.Threading.Thread.Sleep(delay);
						delay *= 2; // wait twice as long next time
						retries--;
					}
				}
				FileInfo fi = new FileInfo(targetTestZip);

				Process Unzipper = new Process();

				// unpack the zip
				ProcessStartInfo si = new ProcessStartInfo(zippedFile);
				si.WorkingDirectory = path;
                si.Arguments="-y";
				String msg = String.Format("{0} {1}", si.FileName, si.Arguments);
				sw.WriteLine(msg);

				Unzipper.StartInfo = si;
				Unzipper.Start();
				Unzipper.WaitForExit();

				if (p4d_cmd.Contains("ssl:"))
				{
					Process GenKeyandCert = new Process();

					// generate private key and certificate 
					si = new ProcessStartInfo("p4d");
					si.Arguments = generate_key_cmd;
					si.WorkingDirectory = path;
					si.UseShellExecute = false;

					msg = si.Arguments;
					sw.WriteLine(msg);

					GenKeyandCert.StartInfo = si;
					GenKeyandCert.Start();
					GenKeyandCert.WaitForExit();
				}

                Process RestoreCheckPoint = new Process();

				// restore the checkpoint
				si = new ProcessStartInfo("p4d");
				si.Arguments = String.Format(restore_cmd, path, checkpointRev);
				si.WorkingDirectory = path;
				si.UseShellExecute = false;

				msg = String.Format("{0} {1}", si.FileName, si.Arguments);
				sw.WriteLine(msg);

				RestoreCheckPoint.StartInfo = si;
				RestoreCheckPoint.Start();
				RestoreCheckPoint.WaitForExit();

				Process UpgradeTables = new Process();

				// upgrade the db tables
				si = new ProcessStartInfo("p4d");
				si.Arguments = String.Format(upgrade_cmd, path);
				si.WorkingDirectory = path;
				si.UseShellExecute = false;

				msg = String.Format("{0} {1}", si.FileName, si.Arguments);
				sw.WriteLine(msg);

				UpgradeTables.StartInfo = si;
				UpgradeTables.Start();
				UpgradeTables.WaitForExit();

				Process p4d = new Process();

				if (P4DCmd != null)
				{
					string P4DCmdSrc = Path.Combine(unitTestDir, P4DCmd);
					string P4DCmdTarget = Path.Combine(path, P4DCmd);
					File.Copy(P4DCmdSrc, P4DCmdTarget);

					// run the command to start p4d
					si = new ProcessStartInfo(P4DCmdTarget);
					si.Arguments = String.Format(path);
					si.WorkingDirectory = path;
					si.UseShellExecute = false;

					msg = String.Format("{0} {1}", si.FileName, si.Arguments);
					sw.WriteLine(msg);

					p4d.StartInfo = si;
					p4d.Start();
				}
				else
				{
					//start p4d
					si = new ProcessStartInfo("p4d");
					si.Arguments = String.Format(p4d_cmd, path);
					si.WorkingDirectory = path;
					si.UseShellExecute = false;

					msg = String.Format("{0} {1}", si.FileName, si.Arguments);
					sw.WriteLine(msg);

					p4d.StartInfo = si;
					p4d.Start();
				}
				Environment.CurrentDirectory = CurWDir;

				return p4d;
			}
		}

		public static Process DeployP4TestServerZip(string path, bool Unicode)
		{
			if (Directory.Exists(rubbishBin) == false)
			{
				Directory.CreateDirectory(rubbishBin);
			}
			if (Directory.Exists(path))
			{
				//test directory exists
				try
				{
					// try to delete it
					Directory.Delete(path, true);
				}
				catch
				{
					try
					{
						// delete failed, try to rename it
						Directory.Move(path, string.Format("{0}-{1}", path, DateTime.Now.Ticks));
					}
					catch
					{
						// rename failed, try to clobber it (can be slow so last resort)
						Utilities.ClobberDirectory(path);
					}
				}
			}
			Directory.CreateDirectory(path);

			string assemblyFile = typeof(Utilities).Assembly.CodeBase;
			String unitTestDir = Path.GetDirectoryName(assemblyFile);

			int idx;
			if (unitTestDir.ToLower().StartsWith("file:\\"))
			{
				// cut off the file:\\
				idx = unitTestDir.IndexOf("\\") + 1;
				unitTestDir = unitTestDir.Substring(idx);
			}
			if ((idx = unitTestDir.IndexOf("TestResults")) > 0)
			{
				unitTestDir = Path.Combine(unitTestDir.Substring(0, idx), "bin\\debug");
			}

			String zippedFile = "a.exe";
			if (Unicode)
			{
				zippedFile = "u.exe";
			}
			string unitTestZip = Path.Combine(unitTestDir, zippedFile);
			string targetTestZip = Path.Combine(path, zippedFile);
			File.Copy(unitTestZip, targetTestZip);

			FileInfo fi = new FileInfo(targetTestZip);

			Process Unzipper = new Process();

			// unpack the zip
			ProcessStartInfo si = new ProcessStartInfo(zippedFile);
			si.WorkingDirectory = path;

			Unzipper.StartInfo = si;
			Unzipper.Start();
			Unzipper.WaitForExit();

			Process p4d = new Process();

			//start p4d
			si = new ProcessStartInfo("p4d");
			si.Arguments = p4d_cmd;
			si.WorkingDirectory = path;
			si.UseShellExecute = false;

			p4d.StartInfo = si;
			p4d.Start();

			return p4d;
		}

		public static void RemoveTestServer(Process p, String path)
		{
			if (p != null)
			{
				if (!p.HasExited)
					p.Kill();
				p.WaitForExit();

				// sleep for a second to let the system clean up
				System.Threading.Thread.Sleep(100);
			}
			string rubbishDirectory = Path.Combine(rubbishBin, DateTime.Now.Ticks.ToString());
			// Rename the directory to make room for the next test
			try
			{
				int retries = 2;
				while ((Directory.Exists(path)) && (retries > 0))
				{
					try
					{
						// Try to rename it
						Directory.Move(path, rubbishDirectory);
						//must have worked
						break;
					}
					catch
					{
						retries--;
						System.Threading.Thread.Sleep(1000);
						if (retries <= 0)
						{
							throw;
							//throw new Exception("Couldn.t move test directory after 10 tries");
						}
					}
				}
			}

			catch (Exception ex)
			{
				LogMessage("In DeployP4TestServer, Directory.Move failed: {0}", ex.Message);
				// rename failed, try to clobber it (can be slow so last resort)
				Utilities.ClobberDirectory(path);
			}
			//try to delete all old test directories
			string[] rubbishDirectories = Directory.GetDirectories(rubbishBin);
			foreach (string rubbish in rubbishDirectories)
			{
				//test directory exists
				try
				{
					// try to delete it
					Directory.Delete(rubbish, true);
				}
				catch
				{
					// simple delete failed, try to rename it
					// rename failed, try to clobber it (can be slow so last resort)
					Utilities.ClobberDirectory(rubbish);
				}
			}
		}

        public static void LogMessage(string format, params object[] args )
        {  
            String LogDir = @"C:\MyTestLog\";
            String LogFile = "log.txt";
            if ( !Directory.Exists( LogDir ) )
            {
                Directory.CreateDirectory( LogDir );
            }
			string message = format;
			if ((args != null) && (args.Length > 0))
			{
				message = string.Format(format, args);
			}
            if ( File.Exists( LogDir + LogFile ) )
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(LogDir + LogFile, true))
                {
                    file.WriteLine(message);
                }
            }
            else
            {
                System.IO.File.WriteAllText(LogDir + LogFile, message);
            }
        }

	}
}