using System;
using System.Diagnostics;
using System.Timers;
using System.Text;
using System.Collections.Generic;
using System.Windows.Forms;

using Medivators.BusinessObjects;
using Medivators.Common;
using Medivators.Common.Enums;
using Medivators.Common.Logging;
using Medivators.Common.Settings;
using Medivators.SerialDevices;

namespace Medivators.Parser
{
	public class ParseDispatcher : IDisposable
	{

		private System.Timers.Timer _timer = null;
		private bool _init = false;
		private object _parseLock = new Object();
		private List<IDeviceParser> _parsers = null;
		private DateTime _lastHeartbeatMessage = DateTime.Now;

		#region singleton construct/instance/init/dispose
		private static ParseDispatcher _instance;

		private ParseDispatcher()
		{
			_parsers = new List<IDeviceParser>();
		}
		public static ParseDispatcher GetInstance()
		{
			if (_instance == null)
				_instance = new ParseDispatcher();
			return _instance;
		}
		private void Initialize()
		{
			if (!_init)
			{
                //  init timer
				_timer = new System.Timers.Timer();
				_timer.Interval = Convert.ToDouble(TextSettings.GetInstance()[SettingTypes.Application, "ParseInterval"]);
				_timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
		        _timer.Enabled = true;

                // insure DB available
                if ( Medivators.Data.ConnectionTest.ConnectTest() )
                    _init = true;

				Log.GetInstance().WriteLog(LogEntryType.DebugTrace, "Parse Dispatcher initialization: " + _init.ToString());
			}
		}
		public void Dispose()
		{
			_timer.Enabled = false;
			_timer.Dispose();
			_parsers.Clear();
		}
		#endregion

		#region starting and stopping parsing

		public void Start()
		{
			Log.GetInstance().WriteLog(
				LogEntryType.DebugTrace,
				"parse Timer Start",
				EventLogEntryType.Information);
            try
            {
                if (!_init)
                    Initialize();
                this._timer.Enabled = true;
                // send one timer pulse right away
                this.timer_Elapsed(null, null);
            }
            catch (Exception ex)
            {
                ErrorHandler.GetInstance().LogException("ParseDispatcher.Start: ", ex);
            }
		}
		public void Stop()
		{
			Log.GetInstance().WriteLog(
				LogEntryType.DebugTrace,
				"parse Timer Stop",
				EventLogEntryType.Information);

			if (_init)
				this._timer.Enabled = false;
		}

		#endregion

		public bool IsRunning
		{
			get { return _timer != null && _timer.Enabled == true; }
		}

		private void timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			//	only log a heartbeat pulse every 30 seconds or so
			if (DateTime.Compare(DateTime.Now, _lastHeartbeatMessage.AddSeconds(30.0)) >= 0)
			{
				Log.GetInstance().WriteLog(
					LogEntryType.DebugTrace,
					"Parse Timer Elapsed",
					EventLogEntryType.Information);
				_lastHeartbeatMessage = DateTime.Now;
			}

			//	whenever the timer goes off, this guy
			//  takes a transmisson off of the transmission
			//	queue, validates and parses it, and then 
			//	adds the record to the EventReportAccumulator

			try
			{
				ParseOneTransmission();
			}
			catch (Exception ex)
			{
				ErrorHandler.GetInstance().LogException(LogEntryType.Critical, ex.ToString());
			}

		}

		#region private helpers

		private void AddParser(IDeviceParser Parser)
		{
			//	the concept here is to avoid object proliferation
			//	by storing whatever parsers are needed so that there
			//  is at most one instance of each
			if (!_parsers.Contains(Parser))
				_parsers.Add(Parser);
		}
		private IDeviceParser GetParser(Type ParserType)
		{
			foreach (IDeviceParser parser in _parsers)
			{
				if (parser.GetType().Equals(ParserType.Name))
					return parser;
			}
			//	if still here, requested parser does not
			//	exist in parser cache. Init the parser
			//	and add it. 

			//TODO augment as new parsers are added
			if (ParserType.Equals(typeof(DSD201Parser)))
			{
				IDeviceParser parser = new DSD201Parser();
				AddParser(parser);
				return parser;
			}
			return null;
		}

		#endregion

		public void ParseOneTransmission()
		{
			IDeviceParser parser = null;
			IDeviceRecord record = null;
			SerialDeviceTransmission Tx = null;
			{
				// grab a record from the queue and parse it
				// but beware of changes to queue while accessing it
				lock (_parseLock)
				{
					if (TxQueue.GetInstance().Count > 0)
					{
						Tx = (SerialDeviceTransmission)TxQueue.GetInstance().Dequeue();
					}
				}
				//  if we got something, this instance will be the only
				//	instance with this record. parse it!
				if (Tx != null)
				{
					Log.GetInstance().WriteLog(LogEntryType.DebugTrace, "ParseDispatcher acquired record");

					switch (Tx.SourceDevice)
					{
						// TODO augment if/when new serial devices are added
						case SupportedSerialDevices.DSD201:
						case SupportedSerialDevices.DSD201SoftwareSimulation:
							parser = GetParser(typeof(DSD201Parser));
							break;
					}
					if (parser.Validate(Tx.Data))
					{
						record = parser.Parse(Tx.Data);
						Log.GetInstance().WriteLog(LogEntryType.DebugTrace, "Record parsed: " + record.ToString());
						//	all parsed records pass through the CycleReportAccumulator
						switch (((DeviceRecord)record).RecordType)	// debug
						{
							case DeviceRecordTypes.CycleCount:
							case DeviceRecordTypes.CycleEnd:
							case DeviceRecordTypes.CycleStart:
							case DeviceRecordTypes.DisinfectantCycleCount:
							case DeviceRecordTypes.LogRecord:
								break;
							default:
								break;
						}
						CycleReportAccumulator.GetInstance().AddRecord((DeviceRecord)record);
					}
					else
						Log.GetInstance().WriteLog(LogEntryType.DebugTrace, "Record failed validation: " + Utilities.ByteArrayToString(Tx.Data), EventLogEntryType.Warning);
				}
			}
		}
	}
}

