/*
 * Copyright (c) 2023 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.Logging.Contracts;
using SafeExamBrowser.Settings.SystemComponents;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply.Events;
using PowerLineStatus = System.Windows.Forms.PowerLineStatus;
using SystemInformation = System.Windows.Forms.SystemInformation;

namespace SafeExamBrowser.SystemComponents.PowerSupply
{
	public class PowerSupply : IPowerSupply
	{
		private readonly ILogger logger;
		private readonly PowerSupplySettings settings;

		private double critical;
		private double low;
		private DateTime lastStatusLog;
		private Timer timer;

		public event StatusChangedEventHandler StatusChanged;

		public PowerSupply(ILogger logger, PowerSupplySettings settings)
		{
			this.logger = logger;
			this.settings = settings;
		}

		public IPowerSupplyStatus GetStatus()
		{
			var charge = SystemInformation.PowerStatus.BatteryLifePercent;
			var hours = SystemInformation.PowerStatus.BatteryLifeRemaining / 3600;
			var minutes = (SystemInformation.PowerStatus.BatteryLifeRemaining - (hours * 3600)) / 60;
			var status = new PowerSupplyStatus();

			status.BatteryCharge = charge;
			status.BatteryChargeStatus = charge <= low ? (charge <= critical ? BatteryChargeStatus.Critical : BatteryChargeStatus.Low) : BatteryChargeStatus.Okay;
			status.BatteryTimeRemaining = new TimeSpan(hours, minutes, 0);
			status.IsOnline = SystemInformation.PowerStatus.PowerLineStatus == PowerLineStatus.Online;

			if (lastStatusLog < DateTime.Now.AddMinutes(-1))
			{
				logger.Debug($"Power grid is {(status.IsOnline ? "" : "not ")}connected, battery charge at {charge * 100}%{(status.IsOnline ? "" : $" ({status.BatteryTimeRemaining})")}.");
				lastStatusLog = DateTime.Now;
			}

			return status;
		}

		public void Initialize()
		{
			const int TWO_SECONDS = 2000;

			critical = SanitizeThreshold(settings.ChargeThresholdCritical);
			low = SanitizeThreshold(settings.ChargeThresholdLow);

			timer = new Timer(TWO_SECONDS);
			timer.Elapsed += Timer_Elapsed;
			timer.AutoReset = true;
			timer.Start();

			logger.Info($"Started monitoring the power supply (battery charge thresholds: low = {low * 100}%, critical = {critical * 100}%).");
		}

		public void Terminate()
		{
			if (timer != null)
			{
				timer.Stop();
				logger.Info("Stopped monitoring the power supply.");
			}
		}

		private double SanitizeThreshold(double value)
		{
			return value < 0 ? 0 : (value > 1 ? 1 : value);
		}

		private void Timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			StatusChanged?.Invoke(GetStatus());
		}
	}
}