// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.

using System;
using System.IO;
using WixToolset.Dtf.WindowsInstaller;
using WixToolset.Dtf.WindowsInstaller.Package;

namespace WixToolset.Dtf.Tools.DDiff
{
	public class MspDiffEngine : MsiDiffEngine
	{
		public MspDiffEngine()
		{
		}

		private string GetPatchTargetOption(string[] options)
		{
			for(int i = 0; i < options.Length - 1; i++)
			{
				switch(options[i].ToLower())
				{
					case "/p": goto case "-patchtarget";
					case "-p": goto case "-patchtarget";
					case "/patchtarget": goto case "-patchtarget";
					case "-patchtarget": return options[i+1];
				}
			}
			return null;
		}

		public override float GetDiffQuality(string diffInput1, string diffInput2, string[] options, IDiffEngineFactory diffFactory)
		{
			if(diffInput1 != null && File.Exists(diffInput1) &&
				diffInput2 != null && File.Exists(diffInput2) &&
				GetPatchTargetOption(options) != null &&
				(IsMspPatch(diffInput1) && IsMspPatch(diffInput2)))
			{
				return .80f;
			}
			else if(diffInput1 != null && File.Exists(diffInput1) &&
				diffInput2 != null && File.Exists(diffInput2) &&
				GetPatchTargetOption(options) == null &&
				(IsMspPatch(diffInput1) && IsMsiDatabase(diffInput2)) ||
				(IsMsiDatabase(diffInput1) && IsMspPatch(diffInput2)))
			{
				return .75f;
			}
			else
			{
				return 0;
			}
		}

		public override bool GetDiff(string diffInput1, string diffInput2, string[] options, TextWriter diffOutput, string linePrefix, IDiffEngineFactory diffFactory)
		{
			bool difference = false;

			InstallPackage db1, db2;
			if(IsMspPatch(diffInput1))
			{
				string patchTargetDbFile = GetPatchTargetOption(options);
				if(patchTargetDbFile == null) patchTargetDbFile = diffInput2;
				string tempPatchedDbFile = Path.GetTempFileName();
				File.Copy(patchTargetDbFile, tempPatchedDbFile, true);
				File.SetAttributes(tempPatchedDbFile, File.GetAttributes(tempPatchedDbFile) & ~System.IO.FileAttributes.ReadOnly);
				db1 = new InstallPackage(tempPatchedDbFile, DatabaseOpenMode.Direct);
				db1.ApplyPatch(new PatchPackage(diffInput1), null);
				db1.Commit();
			}
			else
			{
				db1 = new InstallPackage(diffInput1, DatabaseOpenMode.ReadOnly);
			}
			if(IsMspPatch(diffInput2))
			{
				string patchTargetDbFile = GetPatchTargetOption(options);
				if(patchTargetDbFile == null) patchTargetDbFile = diffInput1;
				string tempPatchedDbFile = Path.GetTempFileName();
				File.Copy(patchTargetDbFile, tempPatchedDbFile, true);
				File.SetAttributes(tempPatchedDbFile, File.GetAttributes(tempPatchedDbFile) & ~System.IO.FileAttributes.ReadOnly);
				db2 = new InstallPackage(tempPatchedDbFile, DatabaseOpenMode.Direct);
				db2.ApplyPatch(new PatchPackage(diffInput2), null);
				db2.Commit();
			}
			else
			{
				db2 = new InstallPackage(diffInput2, DatabaseOpenMode.ReadOnly);
			}

			if(GetSummaryInfoDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true;
			if(GetDatabaseDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true;
			if(GetStreamsDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true;

			db1.Close();
			db2.Close();

			try
			{
				if(IsMspPatch(diffInput1)) File.Delete(db1.FilePath);
				if(IsMspPatch(diffInput1)) File.Delete(db2.FilePath);
			}
			catch(IOException)
			{
				#if DEBUG
				Console.WriteLine("Could not delete temporary files {0} and {1}", db1.FilePath, db2.FilePath);
				#endif
			}

			if(IsMspPatch(diffInput1) && IsMspPatch(diffInput2))
			{
				Database dbp1 = new Database(diffInput1, DatabaseOpenMode.ReadOnly);
				Database dbp2 = new Database(diffInput2, DatabaseOpenMode.ReadOnly);

				if(GetStreamsDiff(dbp1, dbp2, options, diffOutput, linePrefix, diffFactory)) difference = true;
				dbp1.Close();
				dbp2.Close();
			}

			return difference;
		}

		public override IDiffEngine Clone()
		{
			return new MspDiffEngine();
		}
	}
}