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

namespace Medivators.BusinessObjects
{

	#region supporting constants & enums

	public class DSD201Constants
	{
		public static byte[] StartFlag = { 20, 17 };
		public static byte[] EndFlag = { 13, 10 };

		public const byte RawRecordTypeCycleStart = 86;
		public const byte RawRecordTypeCycleEnd_a = 48;
		public const byte RawRecordTypeCycleEnd_b = 49;
		public const byte RawRecordTypeScopeID = 231;
		public const byte RawRecordTypeOperatorID = 232;
		public const byte RawRecordTypePatientID = 233;
		public const byte RawRecordTypePhysicianID = 234;
		public const byte RawRecordTypeCycleCount = 162;
		public const byte RawRecordTypeDisinfectantCycleCount = 93;
		public const byte RawRecordTypeSSGRatio = 235;

		public const byte RawRecordAlarmIndicator = 47;

		#region field ordinals, character positions, & lengths

		//
		// ordinal positions
		//

		// common fields
		public const int RecordType = 0;
		public const int LogNumber = 1;
		public const int MachineID = 2;
		public const int Station = 3;
		public const int Text = 4;

		// event record fields
		public const int Program = 5;
		public const int Day = 6;
		public const int Month = 7;
		public const int Hour = 8;
		public const int Minute = 9;
		public const int Year = 10;

		// header record fields
		public const int Data = 5;

		//
		//	field character positions
		//

		// common fields 
		public const int StartFlagPos = 0;
		public const int RecordTypePos = 2;
		public const int MachineIDPos = 3;
		public const int LogNumberPos = 5;
		public const int StationPos = 7;
		public const int TextPos = 8;

		// event fields
		public const int ProgramPos = 26;
		public const int DayPos = 27;
		public const int MonthPos = 29;
		public const int HourPos = 32;
		public const int MinutePos = 34;
		public const int YearPos = 36;
		public const int EventChecksumPos = 38;
		public const int EventEndFlagPos = 39;

		//	header fields
		public const int DataPos = 26;
		public const int HeaderChecksumPos = 36;
		public const int HeaderEndFlagPos = 37;

		//
		// field lengths
		//

		// common fields 
		public static int StartFlagLen = StartFlag.Length;
		public const int RecordTypeLen = 1;
		public const int LogNumberLen = 2;
		public const int MachineIDLen = 2;
		public const int StationLen = 1;
		public const int TextLen = 18;
		public const int ChecksumLen = 1;
		public static int EndFlagLen = EndFlag.Length;

		// event fields
		public const int ProgramLen = 1;
		public const int DayLen = 2;
		public const int MonthLen = 3;
		public const int HourLen = 2;
		public const int MinuteLen = 2;
		public const int YearLen = 2;

		//	header fields
		public const int DataLen = 10;
		#endregion 
	}

	public enum DeviceRecordTypes
	{
		CycleStart,
		CycleEnd,
		LogRecord,
		ScopeID,
		OperatorID,
		PatientID,
		PhysicianID,
		CycleCount,
		DisinfectantCycleCount,
		SSGRatio
	}

	#endregion

	#region base class handles all the common fields

	abstract public class DeviceRecord : IDeviceRecord, IBusinessObject
	{
		abstract public string Key { get; }

		public DeviceRecordTypes RecordType;
		public bool IsAlarm = false;
		public long LogNumber = 0;
		public long MachineID = 0;
		public string Station = string.Empty;
		public string Text = string.Empty;

		public virtual void FromByteArray(byte[] Data)
		{
			IsAlarm = false;

			switch ( Data[DSD201Constants.RecordTypePos] )
			{
				case DSD201Constants.RawRecordTypeCycleCount:
					RecordType = DeviceRecordTypes.CycleCount;
					break;
				case DSD201Constants.RawRecordTypeCycleEnd_a:
				case DSD201Constants.RawRecordTypeCycleEnd_b:
					RecordType = DeviceRecordTypes.CycleEnd;
					break;
				case DSD201Constants.RawRecordTypeCycleStart:
					RecordType = DeviceRecordTypes.CycleStart;
					break;
				case DSD201Constants.RawRecordTypeDisinfectantCycleCount:
					RecordType = DeviceRecordTypes.DisinfectantCycleCount;
					break;
				case DSD201Constants.RawRecordTypeOperatorID:
					RecordType = DeviceRecordTypes.OperatorID;
					break;
				case DSD201Constants.RawRecordTypePatientID:
					RecordType = DeviceRecordTypes.PatientID;
					break;
				case DSD201Constants.RawRecordTypePhysicianID:
					RecordType = DeviceRecordTypes.PhysicianID;
					break;
				case DSD201Constants.RawRecordTypeScopeID:
					RecordType = DeviceRecordTypes.ScopeID;
					break;
				case DSD201Constants.RawRecordTypeSSGRatio:
					RecordType = DeviceRecordTypes.SSGRatio;
					break;
				default:
					RecordType = DeviceRecordTypes.LogRecord;
					if (Data[DSD201Constants.RecordTypePos] <= DSD201Constants.RawRecordAlarmIndicator)
						IsAlarm = true;
					break;
			}
			
			LogNumber = (int)(Data[DSD201Constants.LogNumberPos] << 8);
			LogNumber += (int)Data[DSD201Constants.LogNumberPos + 1];

			// machine id stored in an unusal fashion: 
			// first byte is the hundreds and thousands digits cast into a byte (0-99)
			// Concatenate with the tens and ones digits in the second byte (also 0-99)
			int wrkInt = Convert.ToInt16(Data[DSD201Constants.MachineIDPos]);
			MachineID = (long)(wrkInt * 100);
			wrkInt = Convert.ToInt16(Data[DSD201Constants.MachineIDPos+1]);
			MachineID += (long)(wrkInt);

			Station = ((char)Data[DSD201Constants.StationPos]).ToString();

			Text = string.Empty;
			for (int i = DSD201Constants.TextPos; i < (DSD201Constants.TextPos + DSD201Constants.TextLen); i++)
			{
				Text += (char)Data[i];
			}
			Text = Text.Replace((char)0, (char)32);
			Text = Text.Trim();			
		}
		public override string ToString()
		{
			string rtn = string.Empty;
			rtn += string.Format("RecordType = {0}\r\n", RecordType.ToString());
			rtn += string.Format("IsAlarm = {0}\r\n", IsAlarm.ToString());
			rtn += string.Format("LogNumber = {0}\r\n", LogNumber.ToString());
			rtn += string.Format("MachineID = {0}\r\n", MachineID);
			rtn += string.Format("Station = {0}\r\n", Station);
			rtn += string.Format("Text = {0}\r\n", Text);
			return rtn;
		}
		public virtual bool Equals(IDeviceRecord Record)
		{
			return ( this.IsAlarm		 == ((DeviceRecord)Record).IsAlarm) && 
			       ( this.LogNumber		 == ((DeviceRecord)Record).LogNumber) &&
				   ( this.MachineID      == ((DeviceRecord)Record).MachineID) &&
				   ( this.Station.Equals   (((DeviceRecord)Record).Station)) &&
				   ( this.Text.Equals      (((DeviceRecord)Record).Text));
		}
		public virtual bool Equals(IBusinessObject Record)
		{
			if (Record.GetType().GetInterface("IDeviceRecord", true) != null)
				return this.Equals((IDeviceRecord)Record);
			else
				return false;
		}
		public virtual string ReportKey
		{
			get
			{
				return MachineID.ToString("0000") +
					   Station;
			}
		}
	}

	#endregion

	#region collection class generically typed to base class
	public class DeviceRecordList : List<IBusinessObject> { }
	#endregion 

	#region defines 'Normal' event log records

	public class DSD201EventRecord : DeviceRecord, ITimestampedDeviceRecord, IBusinessObject
	{
		private int _program = 0;
		private DateTime _timestamp;
		
		public DSD201EventRecord()
			: base()
		{
		}

		public DateTime Timestamp
		{
			get { return _timestamp; }
			set { _timestamp = value; }
		}
		public int Program
		{
			get { return _program; }
			set { _program = value; }
		}
		public override string Key
		{
			get
			{
				return Timestamp.ToString(Constants.TimestampKeyFormat) +
					   LogNumber.ToString("0000") +
					   RecordType.ToString() +
					   MachineID.ToString("0000") +
					   Station;					   
			}
		}
		public override void FromByteArray(byte[] Data)
		{
			base.FromByteArray(Data);
			Program = Data[DSD201Constants.ProgramPos] - 48;

			string dateString = string.Empty;
			
			for (int i = DSD201Constants.DayPos; i < DSD201Constants.YearPos + DSD201Constants.YearLen; i++)
				dateString += (char)Data[i];
			_timestamp = DSD201DateTime.DSD201DateConverter(dateString);
		}
		public override string ToString()
		{
			string rtn = base.ToString();
			rtn += string.Format("Program = {0}\r\n", Program);
			rtn += string.Format("Timestamp = {0}\r\n", _timestamp.ToString());
			return rtn;
		}
		public override bool Equals(IDeviceRecord Record)
		{
			return (base.Equals(Record)) &&
				   (this.Timestamp.Equals(((DSD201EventRecord)Record).Timestamp)) &&
				   (this.Program == ((DSD201EventRecord)Record).Program);
		}
	}

	#endregion 

	#region defines 'Header' event records

	public class DSD201HeaderRecord : DeviceRecord, IBusinessObject
	{
		private string _data = string.Empty;

		public DSD201HeaderRecord()
		{
		}
		public DSD201HeaderRecord(string Data)
		{
			_data = Data;
		}
		public string Data
		{
			get { return _data; }
			set { _data = value; }
		}
		public override string Key
		{
			get
			{
				//	since there is no timestamp, the
				//	whole record becomes the key
				return LogNumber.ToString("0000") +
					   RecordType.ToString() +
					   MachineID.ToString("0000") +
					   Station + 
					   Data;
			}
		}
		public override void FromByteArray(byte[] Data)
		{
			base.FromByteArray(Data);
			_data = string.Empty;
			for (int i = DSD201Constants.DataPos; i < DSD201Constants.DataPos + DSD201Constants.DataLen; i++)
				_data += (char)Data[i];
			_data = _data.Replace((char)0, (char)32).Trim();
		}
		public override string ToString()
		{
			string rtn = base.ToString();
			rtn += string.Format("Data = {0}\r\n", Data);
			return rtn;
		}
		public override bool Equals(IDeviceRecord Record)
		{
			return (base.Equals(Record)) &&
				   (this.Data.Equals(((DSD201HeaderRecord)Record).Data));
		}
	}
	#endregion

	#region defines a virtual Device Header record with a locally-generated timestamp added to it

	public class TimestampedHeaderRecord : DSD201HeaderRecord, ITimestampedDeviceRecord
	{
		private DateTime _timestamp = DateTime.MinValue;

		public TimestampedHeaderRecord()
		{
		}
		public TimestampedHeaderRecord(DSD201HeaderRecord Record, DateTime Timestamp)
		{
			base.Data = Record.Data;
			base.IsAlarm = Record.IsAlarm;
			base.LogNumber = Record.LogNumber;
			base.MachineID = Record.MachineID;
			base.RecordType = Record.RecordType;
			base.Station = Record.Station;
			base.Text = Record.Text;
			this._timestamp = Timestamp;
		}

		public DateTime Timestamp
		{
			get { return _timestamp; }
			set { _timestamp = value; }
		}
		public override string Key
		{
			get
			{
				return Timestamp.ToString(Constants.TimestampKeyFormat) +
					   LogNumber.ToString("0000") +
					   RecordType.ToString() +
					   MachineID.ToString("0000") +
					   Station;
			}
		}
		public override void FromByteArray(byte[] Data)
		{
			//	this should never actually be invoked. This
			//	record type is generated, not received from
			//	a device.
			base.FromByteArray(Data);
		}
		public override string ToString()
		{
			string rtn = base.ToString();
			rtn += string.Format("Timestamp = {0}\r\n", _timestamp.ToString());
			return rtn;
		}
		public override bool Equals(IDeviceRecord Record)
		{
			return (base.Equals(Record)) &&
				   (this.Timestamp.Equals(((TimestampedHeaderRecord)Record).Timestamp));
		}
	}

	#endregion

	#region join table records
	
	public class CycleReportLogRecord : IBusinessObject
	{
		private string _cycleKey	= string.Empty;
		private string _logKey = string.Empty;

		public CycleReportLogRecord(string CycleReportKey, string LogRecordKey)
		{
			_cycleKey = CycleReportKey;
			_logKey = LogRecordKey;
		}
		public CycleReportLogRecord()
		{
		}
		public string CycleReportKey
		{
			get { return _cycleKey; }
			set { _cycleKey = value; }
		}
		public string LogRecordKey
		{
			get { return _logKey; }
			set { _logKey = value; }
		}
		public string Key
		{
			get { throw new Exception("The method or operation is not implemented."); }
		}
		public bool Equals(IBusinessObject BusinessObject)
		{
			bool rtn = false;
			if (BusinessObject.GetType().Equals(typeof(CycleReportLogRecord)))
				rtn = (_cycleKey == ((CycleReportLogRecord)BusinessObject).CycleReportKey &&
					   _logKey == ((CycleReportLogRecord)BusinessObject).LogRecordKey);
			return rtn;
		}
		public override string ToString()
		{
			return string.Format(
				"CycleReportKey={0} LogRecordKey={1}\r\n",
				_cycleKey,
				_logKey);
		}
	}

	#endregion
}

