using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using VEPROMS.CSLA.Library;
using Volian.Base.Library;
using Volian.Pipe.Library;
using System.Xml;
using System.Diagnostics;
using JR.Utils.GUI.Forms;
namespace Volian.Controls.Library
{
	public delegate void NewMessageDelegate(string NewMessage);
	public partial class AnnotationDetails : UserControl
	{
		private static readonly log4net.ILog _MyLog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
		#region Properties
        private bool _LoadingAnnotation = false;
        private bool _LoadingGrid = false;
				private XmlDocument _ProcList;
				private ItemInfo _ProcItem = null;
				public ItemInfo ProcItem
				{
					get { return _ProcItem; }
					set 
					{
						// B2019-054 needed to add a check for value being NULL else we would always get a list of PROMS database to choose from (prematurely)
						if (!DesignMode && value != null) // B2019-043 need to check if we are just saving changes to the user interface
						{
							//if (_ProcItem == value) return; // Jeff (Westinghouse) requested list to be provided every time fixes both C2016-007 and B2016-097
							_ProcItem = value;
							if (ExeType > 0) CreateProcList();
						}
					}
				}
				private void CreateProcList()
				{
					_ProcList = null;
					if (_ProcItem == null) return;
					_ProcList = new XmlDocument();
					_ProcList.LoadXml("");
					AddItem(0,ProcItem);
					//Console.WriteLine(_ProcList.OuterXml);
				}
				private void AddItem(int parentID, ItemInfo myItemInfo)
				{
					if (myItemInfo.IsProcedure || myItemInfo.IsSection || myItemInfo.IsHigh ||
						myItemInfo.IsRNOPart || myItemInfo.IsSequential || myItemInfo.IsNote ||
						myItemInfo.IsCaution)
					{
						XmlElement xe = _ProcList.CreateElement(TypeName(myItemInfo));
						AddAttribute(xe, "ItemID", myItemInfo.ItemID);
						AddAttribute(xe, "ParentID", parentID);
						if (myItemInfo.IsProcedure || myItemInfo.IsSection)
							AddAttribute(xe, "Number", myItemInfo.DisplayNumber);
						else
							AddAttribute(xe, "Number", myItemInfo.MyTab.CleanText);
						AddAttribute(xe, "Text", myItemInfo.DisplayText);
						_ProcList.DocumentElement.AppendChild(xe);
						if (myItemInfo.Cautions != null) foreach (StepInfo caui in myItemInfo.Cautions) AddItem(myItemInfo.ItemID, caui);
						if (myItemInfo.Notes != null) foreach (StepInfo noti in myItemInfo.Notes) AddItem(myItemInfo.ItemID, noti);
						if (myItemInfo.RNOs != null) foreach (StepInfo rnoi in myItemInfo.RNOs) AddItem(myItemInfo.ItemID, rnoi);
						if (myItemInfo.Sections != null) foreach (SectionInfo seci in myItemInfo.Sections) AddItem(myItemInfo.ItemID, seci);
						if (myItemInfo.Steps != null)
						{
							if(myItemInfo.IsSection || (myItemInfo.IsHigh && SubStepHasRNOs(myItemInfo.Steps)))
								foreach (StepInfo stpi in myItemInfo.Steps) AddItem(myItemInfo.ItemID, stpi);
						}
					}
				}
				private string TypeName(ItemInfo myItemInfo)
				{
					if (myItemInfo.IsProcedure) return "Procedure";
					if (myItemInfo.IsSection) return "Section";
					if (myItemInfo.IsHigh) return "Step";
					if (myItemInfo.IsRNOPart) return "RNO";
					if (myItemInfo.IsCaution) return "Caution";
					if (myItemInfo.IsNote) return "Note";
					return "Substep";
				}
				private bool SubStepHasRNOs(ItemInfoList substeps)
				{
					foreach (ItemInfo ii in substeps)
						if (ii.RNOs != null) return true;
					return false;
				}
				private ItemInfo _CurrentItem = null;
				public ItemInfo CurrentItem
				{
					get { return _CurrentItem; }
					set 
					{
						if (!DesignMode && value != null) // B2019-043 need to check if we are just saving changes to the user interface
						{
							_CurrentItem = value;
							SetupCurrentItemValues();
							SetupConfigEdit();
							// B2017-126 Only turn-on NamedPipe if Command Line parameter /NamedPipe is used.  
							// This eliminates waiting for the Pipe if the command line parameter is not used.
							if (ExeType > 0 && Volian.Base.Library.VlnSettings.GetCommandFlag("NamedPipe")) SendPromsAnnotationData();
						}
					}
				}
				private int _FromType;
				private string _ROPath;
				private void SetupCurrentItemValues()
				{
					_FromType = 0;
					if (CurrentItem.FirstSibling.ItemPartCount > 0)
						_FromType = CurrentItem.FirstSibling.ItemParts[0].FromType;
					_ROPath = "";
					if (CurrentItem.MyDocVersion != null)
						if (CurrentItem.MyDocVersion.DocVersionAssociationCount > 0)
							_ROPath = CurrentItem.MyDocVersion.DocVersionAssociations[0].MyROFst.MyRODb.FolderPath;
					ProcItem = CurrentItem.MyProcedure;
				}
				public AnnotationInfo FirstExeAnnotation(ItemInfo ii) 
				{
					if (ii == null) return null;
					if (ii.ItemAnnotationCount == 0) return null;
					foreach (AnnotationInfo ai in ii.ItemAnnotations)
						if (ai.TypeID == ExeType) return ai;
					return null;
				}
				private void SetupConfigEdit()
				{
					//if (ExeType == 0) ; // initialize ExeType
				}
				private string _ExePath;
				private string _PipeOut;
				private string _PipeIn;
				// Made _ExeType a lazy load to fix Jeff's (Westinghouse) request that ProcStep data be generated every time - fixes both C2016-007 and B2016-097
				private int _ExeType = 0;
				public int ExeType
				{
					get 
					{
						if (!DesignMode && _ExeType == 0) // B2019-043 need to check if we are just saving changes to the user interface
						{
							_ExeType = -1;
							foreach (AnnotationTypeInfo ati in AnnotationTypeInfoList.Get())
							{
								if ((ati.Config ?? "") != "")
								{
									_ExePath = ati.AnnotationTypeConfig.Integration_Executable;
									_PipeIn = ati.AnnotationTypeConfig.Integration_PipeIn;
									_PipeOut = ati.AnnotationTypeConfig.Integration_PipeOut;
									if (_ExePath != "" && _PipeIn != "" && _PipeOut != "")
									{
										_ExeType = ati.TypeID;
										break;
									}
								}
							}
						}
						return _ExeType; 
					}
				}
        private DisplaySearch _AnnotationSearch;
		private AnnotationInfoList _Annotations;
		public AnnotationInfoList Annotations
		{
			get { return _Annotations; }
			set 
			{
				if (!DesignMode) // B2019-043 need to check if we are just saving changes to the user interface
				{
					_Annotations = value;
					itemAnnotationsBindingSource.DataSource = _Annotations;
				}
			}
		}
		private AnnotationInfo _CurrentAnnotation = null;
		public AnnotationInfo CurrentAnnotation
		{
			get { return _CurrentAnnotation; }
			set
			{
				if (DesignMode) return; // B2019-043 need to check if we are just saving changes to the user interface
				if (_CurrentAnnotation == null && value == null) return; // No Change
				if (_CurrentAnnotation != null && value != null)
					if (_CurrentAnnotation.AnnotationID == value.AnnotationID) return; // No Change
				//vlnStackTrace.ShowStack("CurrentAnnotation = '{0}' Old = '{1}'", value, _CurrentAnnotation);
				if (_CurrentAnnotation != null || _AddingAnnotation)
				{
					if (AnnotationDirty)
						SaveAnnotation();
				}
				_CurrentAnnotation = value;
				InitializeAnnotation();
            }
		}
		private bool _AnnotationDirty = false;
		public bool AnnotationDirty
		{
			get { return _AnnotationDirty; }
			set
			{
				btnRemoveAnnotation.Enabled = btnAddAnnotation.Enabled = !value;
				btnSaveAnnotation.Enabled = btnCancelAnnoation.Enabled = value;
				_AddingAnnotation = value && (CurrentAnnotation == null);
				_AnnotationDirty = value;
			}
		}
		public string AnnotationText
		{
			get { return rtxbComment.Text; }
			set
			{
				if (!DesignMode) // B2019-043 need to check if we are just saving changes to the user interface
				{
					rtxbComment.Text = value;
					if (rtxbComment.Text != string.Empty)
						rtxbComment.SelectionStart = rtxbComment.TextLength; // position cursor to end of text
				}
			}
		}
		public string AnnotationRTFText
		{
			get { return rtxbComment.Rtf; }
			set
			{
				if (!DesignMode) // B2019-043 need to check if we are just saving changes to the user interface
				{
					rtxbComment.Rtf = value;
					if (rtxbComment.Rtf != string.Empty)
						rtxbComment.SelectionStart = rtxbComment.TextLength; // position cursor to end of text
				}
			}
		}
		private UserInfo _MyUserInfo;
		public UserInfo MyUserInfo
		{
			get { return _MyUserInfo; }
			set { _MyUserInfo = value; }
		}
		#endregion
		#region Constructors
		public AnnotationDetails()
		{
			InitializeComponent();
//#if(DEBUG)
			//Resize+=new EventHandler(AnnotationDetails_Resize); // Debug the resize event
//#endif
			Resize += AnnotationDetails_Resize;
		}
		void AnnotationDetails_Resize(object sender, EventArgs e)
		{
			if(Height > 0)
				rtxbComment.Height = Height - rtxbComment.Top;
		}
		#endregion
		#region Events
		private bool _AddingAnnotation = false;
		private void btnAddAnnotation_Click(object sender, EventArgs e)
		{
            dgAnnotations.ClearSelection();
			CurrentAnnotation = null;
			_AddingAnnotation = true;
            rtxbComment.Focus();
		}
		private void btnRemoveAnnotation_Click(object sender, EventArgs e)
		{
			//using (Annotation annotation = CurrentAnnotation.Get())
			//{
			//				annotation.Delete();
			_AnnotationSearch.LoadingList = true;
			Annotation.DeleteAnnotation(CurrentAnnotation);
			//				annotation.Save();
			_AnnotationSearch.LoadingList = false;
			CurrentAnnotation = null;
			UpdateAnnotationGrid();
			_AnnotationSearch.UpdateAnnotationSearchResults(); // B2019-004: update search results list when an annotation is removed.
			//}
		}
		private void btnSaveAnnotation_Click(object sender, EventArgs e)
		{
			if (cbGridAnnoType.SelectedIndex == -1)
			{
				FlexibleMessageBox.Show("You Must Select an Annotation Type", "Annotation Type Not Selected", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				cbGridAnnoType.Focus();
				return;
			}
			if (rtxbComment.Text == string.Empty)
			{
				FlexibleMessageBox.Show("You Must Enter Annotation Text", "Annotation Text Is Blank", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				rtxbComment.Focus();
				return;
			}
			SaveAnnotation();
		}
		private void btnCancelAnnoation_Click(object sender, EventArgs e)
		{
			InitializeAnnotation();
		}
		private void cbGridAnnoType_SelectedValueChanged(object sender, EventArgs e)
		{
			if (!_LoadingAnnotation)
				AnnotationDirty = true;
		}
        private void dgAnnotations_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (!_LoadingGrid) // Only set the Current Annotation when not loading the grid
            {
                if ((_Annotations != null) && (dgAnnotations.Rows.Count > 0))
                    CurrentAnnotation = _Annotations[dgAnnotations.CurrentRow.Index];
                else
                    CurrentAnnotation = null;
            }
        }
		private void rtxbComment_TextChanged(object sender, EventArgs e)
		{
			if (!_LoadingAnnotation)
				AnnotationDirty = true;
		}
		#endregion
		#region LoadControlData
		public void SetupAnnotations(DisplaySearch annosrch)
		{
			_LoadingAnnotation = true;
			_AnnotationSearch = annosrch; // reference the Annotation Search to update its lists
			cbGridAnnoType.DisplayMember = "Name";
			cbGridAnnoType.ValueMember = "TypeId";
			cbGridAnnoType.DataSource = AnnotationTypeInfoList.Get().Clone();
			// If there are no annotatons, then selected index is -1 (not defined), otherwise select the first.
			// This was done so that it could be saved if there was text entered but user moves to another steprtb without selecting save button
			// so that annotation gets saved.
			if (cbGridAnnoType.Items.Count == 0)
				cbGridAnnoType.SelectedIndex = -1;
			else
				cbGridAnnoType.SelectedIndex = 0;
			_LoadingAnnotation = false;
		}
		private void InitializeAnnotation()
		{
			//vlnCSLAStackTrace.ShowStack("InitializeAnnotation - CurrentAnnotation = {0}", CurrentAnnotation);
			_LoadingAnnotation = true;
			if (CurrentAnnotation == null)
			{
				if (cbGridAnnoType.Items.Count == 0)		// see comment for SetupAnnotations
					cbGridAnnoType.SelectedIndex = -1;
				else
					cbGridAnnoType.SelectedIndex = 0;
				AnnotationText = "";
			}
			else
			{
				cbGridAnnoType.SelectedValue = CurrentAnnotation.TypeID;
				if (CurrentAnnotation.RtfText != "")
					AnnotationRTFText = CurrentAnnotation.RtfText;
				else
					AnnotationText = CurrentAnnotation.SearchText;
			}
			_LoadingAnnotation = false;
			AnnotationDirty = false;
			if (!rtxbComment.Visible) 
				rtxbComment.Visible = true;	
			if (!_LoadingGrid)
				rtxbComment.Focus(); // Set the focus to the comment text
			// Bug Fix:  B2016-274 if admin user also has reviewer set, allow admin user to modify existing annotations
			if (CurrentAnnotation != null && (!(MyUserInfo.IsAdministrator() || MyUserInfo.IsSetAdministrator(CurrentAnnotation.MyItem.MyDocVersion)) && (MyUserInfo.IsReviewer(CurrentAnnotation.MyItem.MyDocVersion ) && CurrentAnnotation.UserID != MyUserInfo.UserID)))
			{
				btnRemoveAnnotation.Enabled = false;
				cbGridAnnoType.Enabled = false;
				rtxbComment.Enabled = false;
			}
			else
			{
				btnRemoveAnnotation.Enabled = true;
				cbGridAnnoType.Enabled = true;
				rtxbComment.Enabled = true;
			}
		}
		private void CheckClientProcess()
		{
			if (ClientProcess.HasExited)
			{
				ClientProcess = null;
				StartClientProcess();
			}
		}
		void ClientProcess_Exited(object sender, EventArgs e)
		{
				ClientProcess = null;
		}
		private Process _ClientProcess;
		public Process ClientProcess
		{
			get {
				if (_ClientProcess == null)
				{
					StartClientProcess();
				}
				return _ClientProcess; 
			}
			set { _ClientProcess = value; }
		}
		private void StartClientProcess()
		{
			if (_ExePath != null) // B2019-043 if we are just saving changes to the user interface don't do anything
			{
				_ClientProcess = Process.Start(_ExePath);
				_ClientProcess.WaitForInputIdle();
				_ClientProcess.Exited += ClientProcess_Exited;
			}
		}
		private void SendPromsAnnotationData()
		{
			//Console.WriteLine("Send {0}", CurrentItem);
			XmlDocument xdMessage = new XmlDocument();
			xdMessage.LoadXml("");
			// Add Steps Data
			if(_ProcList != null)
			{
				xdMessage.DocumentElement.AppendChild(xdMessage.ImportNode(_ProcList.DocumentElement, true));
				_ProcList = null;
			}
			// Add Config Data
			AnnotationInfo ai = FirstExeAnnotation(CurrentItem);
			if (ai != null && ai.Config.Length > 0)
			{
				XmlDocument xdConfig = new XmlDocument();
				xdConfig.LoadXml(ai.Config);
				ProcedureInfo currentProc = CurrentItem.MyProcedure;
				XmlNode nd = xdMessage.DocumentElement.SelectSingleNode("//PromsAnnotationConfig");
				nd.AppendChild(xdMessage.ImportNode(xdConfig.DocumentElement, true));
			}
			// Add Local Info
			AddAttribute(xdMessage.DocumentElement, "ItemID", CurrentItem.ItemID);
			AddAttribute(xdMessage.DocumentElement, "ROPath", _ROPath);
			AddAttribute(xdMessage.DocumentElement, "FromType", _FromType);
			AddAttribute(xdMessage.DocumentElement, "Type", CurrentItem.MyContent.Type);
			PipeToClient.Send(xdMessage.OuterXml);
			PipeFromClient.Listen();
		}
		private void AddAttribute(XmlElement xe, string name, object value)
		{
			XmlAttribute xa = xe.OwnerDocument.CreateAttribute(name);
			xa.Value= value.ToString();
			xe.Attributes.Append(xa);
		}
		private PipeClient _PipeToClient;
		public PipeClient PipeToClient
		{
			get
			{
				if (_PipeToClient == null)
					_PipeToClient = new PipeClient(_PipeOut);
				return _PipeToClient;
			}
		}
		private PipeServer _PipeFromClient;
		public PipeServer PipeFromClient
		{
			get
			{
				if (_PipeFromClient == null)
				{
					_PipeFromClient = new PipeServer(_PipeIn);
					_PipeFromClient.PipeMessage += PipesMessageHandler;
				}
				return _PipeFromClient;
			}
		}
		void PipesMessageHandler(string message)
		{
			try
			{
				if (this.InvokeRequired)
				{
					this.Invoke(new NewMessageDelegate(PipesMessageHandler), message);
				}
				else
				{
					ProcessMessage(message);
				}
			}
			catch (Exception ex)
			{
				PipesMessageHandler(string.Format("{0} - {1}", ex.GetType().FullName, ex.Message));
			}
		}
		private void ProcessMessage(string message)
		{
			//Console.WriteLine(message);
			XmlDocument xd = new XmlDocument();
			xd.LoadXml(message);
			string Mode = GetAttribute(xd, "ClientToProms", "Mode");
			switch (Mode)
			{
				case "SaveAnnotationConfig":
					// Get the AnnotationID
					int itemID = GetAttributeInt(xd,"ClientToProms","ItemID");
					// Get the Config
					string config = GetNode(xd,"PromsAnnotationConfig").InnerXml;
					// Save updated Config Value
					AnnotationInfo ai = FirstExeAnnotation(ItemInfo.Get(itemID));
					if(ai != null)  // Update existing Annotation
					using(Annotation aa = Annotation.Get(ai.AnnotationID))
					{
						if (config == "")
						{
							Annotation.DeleteAnnotation(ai);
							UpdateAnnotationGrid();
						}
						else
						{
							aa.Config = config;
							aa.DTS = DateTime.Now;
							aa.UserID = Volian.Base.Library.VlnSettings.UserID;
							aa.Save();
							AnnotationInfo.Refresh(aa);
							UpdateAnnotationGrid();
						}
					}
					else // Add a new Annotation
					{
						if (config != "")
						{
							using (Item myItem = Item.Get(itemID))
							{
								using (AnnotationType myType = AnnotationType.Get(ExeType))
								{
									using (Annotation annotation = Annotation.MakeAnnotation(myItem, myType, null, "New", config))
									{
										annotation.Save();
										UpdateAnnotationGrid();
									}
								}
							}
						}
					}
					break;
				default:
					Console.WriteLine("Unknown Message Type {0}", Mode);
					break;
			}
		}
		private XmlNode GetNode(XmlDocument xd, string nodeName)
		{
			return xd.DocumentElement.SelectSingleNode("//" + nodeName);
		}
		private string GetAttribute(XmlDocument xd, string nodeName, string attrName)
		{
			return GetNode(xd,nodeName).Attributes[attrName].Value;
		}
		private int GetAttributeInt(XmlDocument xd, string nodeName, string attrName)
		{
			return int.Parse(GetAttribute(xd, nodeName, attrName));
		}
		#endregion
		#region VariousSupportMethods
		/// 
		/// Set up the Annotation Grid for the given item
		/// This is called from frmVEPROMS
		/// 
		/// 
		public void UpdateAnnotationGrid(ItemInfo currentitem)
		{
			CurrentItem = currentitem;
			UpdateAnnotationGrid();
		}
		private void UpdateAnnotationGrid()
		{
			_LoadingGrid = true;
			
			if (CurrentItem == null) 
					_Annotations= null;
			else
			{
				CurrentItem.RefreshItemAnnotations();
				_Annotations = CurrentItem.ItemAnnotations;
			}
			itemAnnotationsBindingSource.DataSource = _Annotations;
            dgAnnotations.Refresh();
			if ((CurrentAnnotation == null || (CurrentItem.ItemID != CurrentAnnotation.ItemID)))
			{
				if (_Annotations != null && _Annotations.Count > 0)
					CurrentAnnotation = _Annotations[0];
				else
					CurrentAnnotation = null;
			}
			FindCurrentAnnotation(); // position to the grid row of the current annotation
			_LoadingGrid = false;
			if (_Annotations == null || _Annotations.Count == 0) btnRemoveAnnotation.Enabled = false;
		}
		/// 
		/// Find the Current Annotation in the Annotation Grid select the corresponding row
		/// Note: this is also called from AnnotationSearch.cs when a search results is selected
		/// 
		public void FindCurrentAnnotation()
		{
			int row = 0;
            if (CurrentAnnotation != null)
            {
                if (_Annotations != null)
                {
                    foreach (AnnotationInfo ai in _Annotations)
                    {
                        if (ai.AnnotationID == CurrentAnnotation.AnnotationID)
                        {
                            row = _Annotations.IndexOf(ai);// +1;
                            break;
                        }
                    }
										try
										{
											dgAnnotations.Rows[row].Selected = true;
											if (!_LoadingGrid && (dgAnnotations.FirstDisplayedScrollingRowIndex != -1))
												dgAnnotations.FirstDisplayedScrollingRowIndex = row;
										}
										catch (Exception ex)
										{
											_MyLog.InfoFormat("Trying to open an annotation which has been removed");
										}
                }
            }
		}
		public void SaveAnnotation()
		{
			if (cbGridAnnoType.SelectedIndex == -1) return;
			if (rtxbComment.Text == string.Empty) return;
			using (AnnotationType annotationType = AnnotationType.Get((int)cbGridAnnoType.SelectedValue))
			{
				if (_AddingAnnotation)
				{
					_AddingAnnotation = false;
					using (Item myItem = CurrentItem.Get())
					{
						using (Annotation annotation = Annotation.MakeAnnotation(myItem, annotationType, rtxbComment.Rtf, rtxbComment.Text, ""))
						{
							CurrentAnnotation = AnnotationInfo.Get(annotation.AnnotationID);
							//annotation.DTS = DateTime.Now;
							//annotation.Save();
						}
					}
				}
				else
				{
					using (Annotation annotation = CurrentAnnotation.Get())
					{
						annotation.RtfText = rtxbComment.Rtf;
						annotation.SearchText = rtxbComment.Text;
						annotation.MyAnnotationType = annotationType;
						annotation.DTS = DateTime.Now;
						annotation.UserID = Volian.Base.Library.VlnSettings.UserID;
						annotation.Save();
					}
				}
			}
			AnnotationDirty = false;
			UpdateAnnotationGrid();
			AnnotationTypeInfoList.Reset();		// B2018-135: refresh annotation type list to update when annotations are used.
		}
		#endregion
	}
}