using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using Medivators.BusinessObjects;
using Medivators.Common;

namespace Medivators.BusinessObjects
{
	public class CycleReportList : List<IBusinessObject>
	{
	}

	public class CycleReport : IBusinessObject
	{
		#region field declarations

		public long MachineID = 0;
		public string Station = string.Empty;
		public DateTime StartTimestamp = DateTime.MinValue;
		public DateTime EndTimestamp = DateTime.MinValue;
		public int CycleCount = 0;
		public int DisinfectantCycleCount = 0;
		public long OperatorID = 0;
		public long PatientID = 0;
		public long PhysicianID = 0;
		public long ScopeID = 0;
		public string SSGRatio = "000:000:00.0";

		#endregion

		private List<IDeviceRecord> _records;
		private int _endingLogNumber = 0;
		private bool _initHeaderValues = false;

		public CycleReport()
		{
			_records = new List<IDeviceRecord>();
		}
		public CycleReport(long MachineID, string Station)
		{
			_records = new List<IDeviceRecord>();
			this.MachineID = MachineID;
			this.Station = Station;
		}

		public List<IDeviceRecord> LogRecords
		{
			get { return _records; }
		}

		public string Key
		{
			get
			{
				return string.Format("{0}{1}{2}{3}",
									  MachineID.ToString("0000"),
									  Station,
									  StartTimestamp.ToString(Constants.TimestampKeyFormat),
									  EndTimestamp.ToString(Constants.TimestampKeyFormat));
			}
		}
		public string ReportKey
		{
			get { return MachineID.ToString("0000") + Station; }
		}
		public void AddRecord(DeviceRecord Record)
		{
			try
			{
				//	this concept of passing records through the report and adding
				//	all of them to the report only works when the process doing 
				//	the passing can confirm that it is passing only records known
				//	to belong in 'this' cycle report

				//	initialize header values
				if (!_initHeaderValues)
				{
					this.MachineID = Record.MachineID;
					this.Station = Record.Station;
					_initHeaderValues = true;
				}


				//	truly strange business logic: log numbers within a cycle report's
				//	event records will all be consecutively numbered in intervals of 11. 

				if (!ValidateLogNumber(Record))
					throw new ArgumentOutOfRangeException(
						string.Format("Log number {0} does not fit into report containing Ending log number {1}", Record.LogNumber, this._endingLogNumber));

				// don't add a record that is already in the records collection

				if (this._records != null && this._records.Count > 0)
					foreach (IDeviceRecord item in this._records)
						if (item.Key.Equals(Record.Key))
							throw new IndexOutOfRangeException(
								string.Format("Record {0} is already present in records collection for report {1}",
								Record.ToString(),
								this.ToString()));

				switch (Record.RecordType)
				{
					case DeviceRecordTypes.CycleCount:
						this.CycleCount = Convert.ToInt32(((DSD201HeaderRecord)Record).Data.Trim());
						break;
					case DeviceRecordTypes.CycleEnd:
						this.EndTimestamp = ((DSD201EventRecord)Record).Timestamp;
						break;
					case DeviceRecordTypes.CycleStart:
						this.StartTimestamp = ((DSD201EventRecord)Record).Timestamp;
						break;
					case DeviceRecordTypes.DisinfectantCycleCount:
						this.DisinfectantCycleCount = Convert.ToInt32(((DSD201HeaderRecord)Record).Data.Trim());
						break;
					case DeviceRecordTypes.OperatorID:
						this.OperatorID = Convert.ToInt64(((DSD201HeaderRecord)Record).Data.Trim());
						break;
					case DeviceRecordTypes.PatientID:
						this.PatientID = Convert.ToInt64(((DSD201HeaderRecord)Record).Data.Trim());
						break;
					case DeviceRecordTypes.PhysicianID:
						this.PhysicianID = Convert.ToInt64(((DSD201HeaderRecord)Record).Data.Trim());
						break;
					case DeviceRecordTypes.ScopeID:
						this.ScopeID = Convert.ToInt64(((DSD201HeaderRecord)Record).Data.Trim());
						break;
					case DeviceRecordTypes.SSGRatio:
						// SSGRatio transmits as nine digits but needs 
						// to be formatted as "000:000:00.0"
						string wrkString = ((DSD201HeaderRecord)Record).Data.Trim().PadRight(9, (char)32);
						wrkString = wrkString.Insert(3, ":");
						wrkString = wrkString.Insert(7, ":");
						wrkString = wrkString.Insert(10, ".");
						this.SSGRatio = wrkString;
						break;
					default:
						break;
				}
				//	LogEvent records get added to the internal collection
				//	0f related LogEvents
				_records.Add(Record);

			}
			catch { throw; }
		}
		public override string ToString()
		{
			string rtn = string.Empty;
			rtn += string.Format("MachineID = {0}\r\n", MachineID);
			rtn += string.Format("Station = {0}\r\n" ,  Station);
			rtn += string.Format("StartTimestamp = {0}\r\n" , StartTimestamp);
			rtn += string.Format("EndTimeStamp = {0}\r\n" , EndTimestamp);
			rtn += string.Format("CycleCount = {0}\r\n" , CycleCount.ToString());
			rtn += string.Format("DisinfectantCycleCount = {0}\r\n" , DisinfectantCycleCount.ToString());
			rtn += string.Format("OperatorID = {0}\r\n" , OperatorID.ToString());
			rtn += string.Format("PatientID = {0}\r\n" , PatientID.ToString());
			rtn += string.Format("PhysicianID = {0}\r\n" , PhysicianID.ToString());
			rtn += string.Format("ScopeID = {0}\r\n" , ScopeID.ToString());
			rtn += string.Format("SSGRatio = {0}\r\n" , SSGRatio);
			foreach (IDeviceRecord record in _records)
				rtn += string.Format("\r\n{0}",record.ToString());
			return rtn;
		}
		public bool Validate()
		{
			// a cycle record cannot exist without a starttime, endtime, machine, and station
			if (MachineID == 0 ||
				Station == string.Empty ||
				StartTimestamp.Equals(DateTime.MinValue) ||
				EndTimestamp.Equals(DateTime.MinValue))
				return false;
			// end time cannot come before start time
			else if ( EndTimestamp.CompareTo(StartTimestamp) < 0 )
				return false;
			else                   
				return true;
		}
		public bool Equals(CycleReport Report)
		{

			bool rtn = (Report.Key == this.Key &&
						Report.CycleCount == this.CycleCount &&
						Report.DisinfectantCycleCount == this.DisinfectantCycleCount &&
						Report.OperatorID == this.OperatorID &&
						Report.PatientID == this.PatientID &&
						Report.PhysicianID == this.PhysicianID &&
						Report.ScopeID == this.ScopeID &&
						Report.SSGRatio == this.SSGRatio);

			if ( rtn )
				rtn = (_records.Count == Report.LogRecords.Count);
			
			//	attached log records may present in differing order
			//	so we have to do a dual walk of both sets
			if ( rtn )
				foreach (IDeviceRecord record in this._records)
				{
					bool found = false;
					foreach (IDeviceRecord rec2 in Report.LogRecords)
						if (record.Equals(rec2))
						{
							found = true;
							break;
						}
					if (!found)
					{
						rtn = false;
						break;
					}
				}
			if ( rtn )
				foreach (IDeviceRecord record in Report.LogRecords)
				{
					bool found = false;
					foreach (IDeviceRecord rec2 in this._records)
						if (record.Equals(rec2))
						{
							found = true;
							break;
						}
					if (!found)
					{
						rtn = false;
						break;
					}
				}

			return rtn;
		}
		public bool Equals(IBusinessObject BusinessObject)
		{
			bool rtnVal = false;
			if (BusinessObject.GetType().Equals(typeof(CycleReport)))
				rtnVal = this.Equals((CycleReport)BusinessObject);
			return rtnVal;
		}

		private bool ValidateLogNumber(DeviceRecord rec)
		{
			//	if this is an End record, it is always valid
			//	otherwise, do math to ensure new record is
			//	eleven-offset from end number
			bool rtnVal = false;
			if (rec.RecordType == DeviceRecordTypes.CycleEnd)
			{
				rtnVal = true;
				this._endingLogNumber = (int)rec.LogNumber;
			}
			else
				// as a general statement, records should have a log number
				// less than the ending record log number
				if (this._endingLogNumber - rec.LogNumber > 0)
				{
					if ((this._endingLogNumber - rec.LogNumber) % 11 == 0)
						rtnVal = true;
				}
				// exception: log numbers rollover from 48850 back to 32768 
				// and from 64943 back to 48861
				else if ((this._endingLogNumber - 32768) <= 220 && (48850 - rec.LogNumber) <= 220)
					rtnVal = true;
				else if ((this._endingLogNumber - 48861) <= 220 && (64943 - rec.LogNumber) <= 220)
					rtnVal = true;

			return rtnVal;
		}
	}
}
