/*
 * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
 * 
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

using System;
using System.Timers;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.WindowsApi.Contracts;

namespace SafeExamBrowser.Applications
{
	internal class ExternalApplicationInstance : IApplicationInstance
	{
		private const int ONE_SECOND = 1000;

		private ILogger logger;
		private string name;
		private IProcess process;
		private Timer timer;

		public InstanceIdentifier Id { get; }
		public string Name { get; }

		public event IconChangedEventHandler IconChanged { add { } remove { } }
		public event NameChangedEventHandler NameChanged;
		public event InstanceTerminatedEventHandler Terminated;

		public ExternalApplicationInstance(InstanceIdentifier id, ILogger logger, IProcess process)
		{
			this.Id = id;
			this.logger = logger;
			this.process = process;
		}

		public void Activate()
		{
			var success = process.TryActivate();

			if (!success)
			{
				logger.Warn("Failed to activate instance!");
			}
		}

		public void Initialize()
		{
			process.Terminated += Process_Terminated;

			timer = new Timer(ONE_SECOND);
			timer.Elapsed += Timer_Elapsed;
			timer.Start();

			logger.Info("Initialized application instance.");
		}

		public void Terminate()
		{
			const int MAX_ATTEMPTS = 5;
			const int TIMEOUT_MS = 500;

			timer.Elapsed -= Timer_Elapsed;
			timer?.Stop();

			var terminated = process.HasTerminated;

			for (var attempt = 0; attempt < MAX_ATTEMPTS && !terminated; attempt++)
			{
				terminated = process.TryClose(TIMEOUT_MS);
			}

			for (var attempt = 0; attempt < MAX_ATTEMPTS && !terminated; attempt++)
			{
				terminated = process.TryKill(TIMEOUT_MS);
			}

			if (terminated)
			{
				logger.Info("Successfully terminated application instance.");
			}
			else
			{
				logger.Warn("Failed to terminate application instance!");
			}
		}

		private void Process_Terminated(int exitCode)
		{
			logger.Info($"Application instance has terminated with exit code {exitCode}.");
			Terminated?.Invoke(Id);
		}

		private void Timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			var success = process.TryGetWindowTitle(out var title);
			var hasChanged = name?.Equals(title, StringComparison.Ordinal) != true;

			// TODO: Use this and ensure log window doesn't hang on shutdown (if it is open)!
			// logger.Warn($"Success: {success} HasChanged: {hasChanged}");

			if (success && hasChanged)
			{
				name = title;
				NameChanged?.Invoke(name);
			}

			timer.Start();
		}
	}
}