using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Timers;

using Medivators.Comm;
using Medivators.Common;
using Medivators.Common.Enums;
using Medivators.Common.Logging;
using Medivators.Common.Settings;

namespace Medivators.SerialDevices
{
	public class SerialDeviceCollection : Collection<ISerialDevice>, IDisposable
	{
		#region construct/dispose
		public SerialDeviceCollection(ILogProvider LogProvider)
		{
			Log.GetInstance().Provider = LogProvider;
		}
		//	hide argument-less constructor
		private SerialDeviceCollection() { }

		public void Dispose()
		{
			foreach (ISerialDevice device in this)
			{
				device.Dispose();
			}
			this.Clear();
		}
		#endregion

		public const double ReopenRetryInterval = 30000.0;

		private SupportedSerialDevices _serialDeviceType = SupportedSerialDevices.DSD201;
		private SupportedCommDevices _commPortType = SupportedCommDevices.CommPort;
		private bool _showExceptions = false;
		private bool _timerStarted = false;
		private Timer _retryTimer = null;

		public SupportedSerialDevices SerialDeviceType
		{
			get { return _serialDeviceType; }
			set { _serialDeviceType = value; }
		}
		public SupportedCommDevices CommDeviceType
		{
			get { return _commPortType; }
			set { _commPortType = value; }
		}

		public int LoadDevices()
		{
			return LoadDevices(false);
		}
		public int LoadDevices(bool ShowExceptions)
		{
			_showExceptions = ShowExceptions;
			int rtnVal = 0;

			try
			{
				Log.GetInstance().WriteLog(LogEntryType.Normal, "SerialDeviceCollection loading devices");

				string setting = TextSettings.GetInstance()[SettingTypes.Application, "ActiveCommPorts"];
				string[] ports = setting.Split(Constants.ElementDelimiter.ToCharArray());
				foreach (string port in ports)
				{
					// init devices
					ISerialDevice device = SerialDevices.SerialDevice(_serialDeviceType);
					ICommDevice commPort = CommDevices.CommDevice(_commPortType);

					// acquire and apply settings 
					CommPorts com = (CommPorts)Enum.Parse(typeof(CommPorts), port.Trim());
					string pSet = TextSettings.GetInstance()[SettingTypes.Application, "COMSettings"];
					commPort.Port = com;
					CommPortSettings set = new CommPortSettings();
					commPort.Settings = set.FromString(pSet);

					//	apply comm device 
					device.CommPort = commPort;

					//	apply Log to serial device 
					device.Log = Log.GetInstance();

					//	open ports
					Log.GetInstance().WriteLog(LogEntryType.Normal,
						string.Format("Opening CommPort {0}. Settings = {1}",
						com, pSet));
					device.CommPort.OpenCommPort();

					//	notify world of result
					if (device.CommPort.IsOpen)
					{
						rtnVal += 1;
						Log.GetInstance().WriteLog(LogEntryType.Normal,
							string.Format("CommPort {0} opened.", com));
					}
					else
					{
						//	on failure, start the reopen retry timer
						StartRetryTimer();
						Log.GetInstance().WriteLog(LogEntryType.Normal,
							string.Format("CommPort {0} failed to open!", com));
					}
					//	regardless of success, add to collection
					this.Add(device);
				}
			}
			catch (Exception ex)
			{
				ErrorHandler.GetInstance().LogException(ShowExceptions, "SerialDeviceCollection Initialize reports error", ex);
			}
			return rtnVal;
		}

		public new void Add(ISerialDevice Device)
		{
			Device.RecordReceived += new RecordReceivedEventHandler(TxReceived);
			base.Add(Device);
		}
		private void TxReceived(ISerialDevice sender, RecordReceivedEventArgs e)
		{
			TxQueue.GetInstance().Enqueue(new SerialDeviceTransmission(sender.Device, e.Record));
		}

		private void StartRetryTimer()
		{
			if (_retryTimer == null)
				_retryTimer = new Timer(ReopenRetryInterval);
			if (!_timerStarted)
			{
				_retryTimer.Elapsed += new ElapsedEventHandler(Timer_Elapsed);
				_retryTimer.Start();
				_timerStarted = true;
			}
		}
	
		private void Timer_Elapsed (object sender, ElapsedEventArgs e)
		{
			bool failedOpen = false;

			foreach (ISerialDevice device in this)
			{
				if (!device.CommPort.IsOpen)
				{
					Log.GetInstance().WriteLog(LogEntryType.Normal,
												string.Format("Attempting to reopen failed CommPort {0}. Settings = {1}",
												device.CommPort.Port.ToString(), 
												device.CommPort.Settings.ToString()));
					try
					{
						device.CommPort.OpenCommPort();
						//	notify world of result
						if (device.CommPort.IsOpen)
							Log.GetInstance().WriteLog(LogEntryType.Normal,
								string.Format("CommPort {0} opened.", device.CommPort.Port.ToString()));
						else
						{
							failedOpen = true;
							Log.GetInstance().WriteLog(LogEntryType.Normal,
								string.Format("CommPort {0} failed to open!", device.CommPort.Port.ToString()));
						}
					}
					catch (Exception ex)
					{
						failedOpen = true;
						ErrorHandler.GetInstance().LogException(_showExceptions, "SerialDeviceCollection Reopen Port reports error", ex);
					}
				}
			}
			//	if everything opened this time, clear the timer
			if (!failedOpen)
			{
				_retryTimer.Stop();
				_retryTimer.Dispose();
				_timerStarted = false;
			}
		}
	}
}
