using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

using Infragistics.Win.UltraWinGrid;

using Medivators.Common;
using Medivators.Data;
using Medivators.Data.Queries;

namespace Medivators.Reporting
{
	public partial class ucSelector : UserControl
	{
		#region SelectionChanged event and eventargs class

		public delegate void FilterCriteriaChangedEventHandler(object sender, FilterCriteriaChangedEventArgs e);
		public event FilterCriteriaChangedEventHandler FilterCriteriaChanged;
		public class FilterCriteriaChangedEventArgs : EventArgs
		{
			private Dictionary<string, string> _filterCriteria;

			public FilterCriteriaChangedEventArgs(Dictionary<string, string> Criteria)
			{
				_filterCriteria = Criteria;
			}
			public Dictionary<string, string> FilterCriteria
			{
				get { return _filterCriteria; }
				set { _filterCriteria = value; }
			}
		}
		#endregion

		#region declarations

		private delegate void UpdateDisplay();
		private delegate void ClearCursor();
		private bool _loading = false;
		private bool _dateSet = false;
		private Hashtable _selectorLists = new Hashtable();

		private const string DoNotFilter = "[All]";
		private const string LogEventText = "EventText";
		private string[] DateRangeNames = new string[] {
										  "Today",
  										  "This Week", 
										  "This Month",
										  "This Year"};

		#endregion

		#region construct/load

		public ucSelector()
		{
			InitializeComponent();
		}
		private void ucSelector_Load(object sender, EventArgs e)
		{
			// init date selector combo
			_loading = true;
			this.cboDates.DataSource = this.DateRangeNames;
			btnReset_Click(null, null);
			_loading = false;
		}

		#endregion

		#region cannot allow use of date combo AND datetimepickers

		private void cboDates_ValueChanged(object sender, EventArgs e)
		{
			//	when user changes predefined date, clear values in 
			//	manual entry dates
			if (!_dateSet)
			{
				_dateSet = true;
				dtpFrom.Value = null;
				dtpTo.Value = null;
				_dateSet = false;
			}
		}
		private void DateRange_ValueChanged(object sender, EventArgs e)
		{
			//	when user changes manual entry date, clear values in 
			//	predefined entry dates
			if (!_dateSet)
			{
				_dateSet = true;
				_loading = true;
				cboDates.Value = null;
				_loading = false;
				_dateSet = false;
			}
		}

		#endregion

		#region getting selector data into the report selectors

		private void AcquireSelectorData()
		{
			//	there are three chained methods that populate the 
			//	selector via back-threaded process. This is part 1 of 3
			try
			{
				if (_selectorLists != null && _selectorLists.Count > 0)
					_selectorLists.Clear();

				using (SelectDistinctCycleReportAttribute query = new SelectDistinctCycleReportAttribute())
				{
					foreach (Medivators.Data.Enums.CycleReportAttributes attr in System.Enum.GetValues(typeof(Medivators.Data.Enums.CycleReportAttributes)))
						//	don't prepopulate date entry fields
						if (attr != Medivators.Data.Enums.CycleReportAttributes.Date)
							UnpackSelectorData(query.Select(attr, false), attr);

				}
				//	have to manually unpack the eventTypes data as it is not 
				//	a cycle report attribute. 
				using (SelectDistinctLogEventText query = new SelectDistinctLogEventText())
				{
					ArrayList eventList = query.Select(false);
					foreach (DictionaryEntry[] row in eventList)
					{
						if (!_selectorLists.Contains(LogEventText))
							_selectorLists.Add(LogEventText, new List<string>());
						if (row[0].Value != null)
							((List<string>)_selectorLists[LogEventText]).Add((string)row[0].Value);
					}
				}

				this.Invoke(new UpdateDisplay(this.DisplaySelectorData));
			}
			catch (Exception ex)
			{
				using (dlgError dlg = new dlgError())
				{
					dlg.Message = "Error encountered acquiring data for the cycle report selectors.";
					dlg.Details = ex.ToString();
					dlg.ShowDialog();
				}
				ErrorHandler.GetInstance().LogException("Selector.AcquireSelectorData:", ex);
				this.Invoke(new ClearCursor(this.StopWaiting));
			}
		}
		private void UnpackSelectorData(ArrayList Data, Medivators.Data.Enums.CycleReportAttributes Attribute)
		{
			//	there are three chained methods that populate the 
			//	selector. This is part 2 of 3. It was separated
			//	from AcquireSelectorData mainly because of 
			//	length and complexity 

			lock (_selectorLists)
			{
				foreach (DictionaryEntry[] row in Data)
				{
					//	all but one type of return row will contain only one value
					//	but we have to know the expected datatype to properly access
					//	the relevant List<> object 
					switch (Attribute)
					{
						case Medivators.Data.Enums.CycleReportAttributes.Operator:
						case Medivators.Data.Enums.CycleReportAttributes.Patient:
						case Medivators.Data.Enums.CycleReportAttributes.Physician:
						case Medivators.Data.Enums.CycleReportAttributes.Scope:
							if (!_selectorLists.Contains(Attribute))
								_selectorLists[Attribute] = new List<string>();
							((List<string>)_selectorLists[Attribute]).Add(row[0].Value.ToString());
							break;
						case Medivators.Data.Enums.CycleReportAttributes.MachineStation:
							//	this is the one with two values
							if (!_selectorLists.Contains(Attribute))
								_selectorLists[Attribute] = new List<string>();
							string wrkStr = row[0].Value.ToString() + (string)row[1].Value;
							((List<string>)_selectorLists[Attribute]).Add(wrkStr);
							break;
					}
				}
			}
		}
		private void DisplaySelectorData()
		{
			//	there are three chained methods that populate the 
			//	selector. This is part 3 of 3. It had to be separated
			//	from the others because it requires a delegate
			//	invoke to avoid crossthreading. Plus it is long 
			//	and boring.

			try
			{
				if (_selectorLists != null)
				{
					lock (_selectorLists)
					{
						PopulateSelector(this.cboMachine, Medivators.Data.Enums.CycleReportAttributes.MachineStation);
						PopulateSelector(this.cboOperator, Medivators.Data.Enums.CycleReportAttributes.Operator);
						PopulateSelector(this.cboScope, Medivators.Data.Enums.CycleReportAttributes.Scope);
						PopulateSelector(this.cboPatient, Medivators.Data.Enums.CycleReportAttributes.Patient);
						PopulateSelector(this.cboPhysician, Medivators.Data.Enums.CycleReportAttributes.Physician);
						PopulateSelector(this.cboEvent, LogEventText);
					}
				}
			}
			catch (Exception ex)
			{
				using (dlgError dlg = new dlgError())
				{
					dlg.Message = "Error encountered loading data for the cycle report selectors.";
					dlg.Details = ex.ToString();
					dlg.ShowDialog();
				}
				ErrorHandler.GetInstance().LogException("Cycle Reports form DisplaySelectorData:", ex);
			}
			finally
			{
				this.Invoke(new ClearCursor(this.StopWaiting));
			}
		}
		private void PopulateSelector(UltraCombo combo, object SelectorListKey)
		{
			List<string> values = new List<string>();
			values.Add(DoNotFilter);
			if (_selectorLists.ContainsKey(SelectorListKey))
				foreach (string value in (List<string>)_selectorLists[SelectorListKey])
					values.Add(value);

			string currentSelection = string.Empty;
			if (combo.SelectedRow != null)
				currentSelection = combo.SelectedRow.Cells[0].Text;

			this._loading = true;
			combo.DataSource = values;

			if (currentSelection != null)
				foreach (UltraGridRow row in combo.Rows)
					if (row.Cells[0].Text != null && row.Cells[0].Text.Length > 0)
						if (row.Cells[0].Text.Equals(currentSelection))
						{
							combo.SelectedRow = row;
							break;
						}
			this._loading = false;
		}

		#endregion

		#region private helpers and delegate targets

		private void StopWaiting()
		{
			this.Cursor = Cursors.Default;
		}

		#endregion

		#region Filter management

		private void ApplyFilters()
		{
			if (!_loading)
			{
				Dictionary<string, string> filter = GetSelectedFilters();
				if (filter != null && filter.Count > 0)
					this.FilterCriteriaChanged(this, new FilterCriteriaChangedEventArgs(filter));
				filter.Clear();
			}
		}
		public Dictionary<string, string> GetSelectedFilters()
		{
			try
			{
				Dictionary<string, string> filterValues = new Dictionary<string, string>();

				#region date selectors are convenient for users but extra code for programmer

				if (this.cboDates.Value != null)
				{
					DateTime wrkDate = DateTime.Now.Date;

					switch ((string)this.cboDates.Value)
					{
						case "Today":
							break;
						case "This Week":
							while (!wrkDate.DayOfWeek.Equals(DayOfWeek.Monday))
								wrkDate = wrkDate.AddDays(-1.0);
							break;
						case "This Month":
							while (wrkDate.Day > 1)
								wrkDate = wrkDate.AddDays(-1.0);
							break;
						case "This Year":
							wrkDate = Convert.ToDateTime("01-01-" + wrkDate.Year.ToString());
							break;
					}
					filterValues.Add("StartTimeStamp", wrkDate.ToString(Constants.DateTimeFormat));
				}
				else if (this.dtpFrom.Value != null && this.dtpTo.Value == null)
				{
					filterValues.Add("StartTimeStamp", ((DateTime)dtpFrom.Value).Date.ToString(Constants.DateTimeFormat));
				}
				else if (this.dtpFrom.Value == null && this.dtpTo.Value != null)
				{
					filterValues.Add("EndTimeStamp", ((DateTime)dtpTo.Value).Date.ToString(Constants.DateTimeFormat));
				}
				else if (this.dtpFrom.Value != null && this.dtpTo.Value != null)
				{
					filterValues.Add("StartTimeStamp", ((DateTime)dtpFrom.Value).Date.ToString(Constants.DateTimeFormat));
					filterValues.Add("EndTimeStamp", ((DateTime)dtpTo.Value).Date.ToString(Constants.DateTimeFormat));
				}

				#endregion

				if (this.cboMachine.Value != null)
					if (!this.cboMachine.Value.ToString().Equals(DoNotFilter))
						filterValues.Add(Data.Enums.CycleReportAttributes.MachineStation.ToString(), this.cboMachine.Value.ToString());

				if (this.cboOperator.Value != null)
					if (!this.cboOperator.Value.ToString().Equals(DoNotFilter))
						filterValues.Add(Data.Enums.CycleReportAttributes.Operator.ToString(), Convert.ToInt32(this.cboOperator.Value).ToString());

				if (this.cboPatient.Value != null)
					if (!this.cboPatient.Value.ToString().Equals(DoNotFilter))
						filterValues.Add(Data.Enums.CycleReportAttributes.Patient.ToString(), Convert.ToInt32(this.cboPatient.Value).ToString());

				if (this.cboPhysician.Value != null)
					if (!this.cboPhysician.Value.ToString().Equals(DoNotFilter))
						filterValues.Add(Data.Enums.CycleReportAttributes.Physician.ToString(), Convert.ToInt32(this.cboPhysician.Value).ToString());

				if (this.cboScope.Value != null)
					if (!this.cboScope.Value.ToString().Equals(DoNotFilter))
						filterValues.Add(Data.Enums.CycleReportAttributes.Scope.ToString(), Convert.ToInt32(this.cboScope.Value).ToString());

				if (this.cboEvent.Value != null)
					if (!this.cboEvent.Value.ToString().Equals(DoNotFilter))
						filterValues.Add(LogEventText, this.cboEvent.Value.ToString());

				return filterValues;

			}
			catch (Exception ex)
			{
				ErrorHandler.GetInstance().LogException("ucSelector.CreateFilterValues", ex);
				return null;
			}
		}

		#endregion

		#region control events

		private void btnReset_Click(object sender, EventArgs e)
		{
			bool applyOnComplete = !_loading;
			_loading = true;
			this.cboDates.Value = "Today";
			this.dtpTo.Value = null;
			this.dtpFrom.Value = null;
			this.cboMachine.Value = DoNotFilter;
			this.cboOperator.Value = DoNotFilter;
			this.cboPatient.Value = DoNotFilter;
			this.cboPhysician.Value = DoNotFilter;
			this.cboScope.Value = DoNotFilter;
			this.cboEvent.Value = DoNotFilter;
			_loading = false;
			if (applyOnComplete)
				this.ApplyFilters();
		}
		private void DateRangePickers_Validating(object sender, CancelEventArgs e)
		{
			//	insure end comes after start when both are populated
			if (this.dtpFrom.Value != null && this.dtpTo.Value != null)
				if (((DateTime)this.dtpFrom.Value).CompareTo(this.dtpTo.Value) > 0)
				{
					dtpFrom.Focus();
					MsgBox.Show(
						"To Date cannot come before From Date",
						"Refreshing data",
						MessageBoxButtons.OK,
						MessageBoxIcon.Exclamation);
					e.Cancel = true;
				}
		}
		private void ComboRowSelected(object sender, Infragistics.Win.UltraWinGrid.RowSelectedEventArgs e)
		{
			this.ApplyFilters();
		}
		private void DateRange_Validated(object sender, EventArgs e)
		{
			ApplyFilters();
		}	

		#endregion

		public void SelectorsLoad()
		{
			if (!_loading)
			{
				_loading = true;
				this.Enabled = false;
				this.Cursor = Cursors.WaitCursor;

				AcquireSelectorData();

				this.Enabled = true;
				this.Cursor = Cursors.Default;
				_loading = false;
			}
		}
	}
}
