using System;
using System.Collections;
using System.IO;
using System.Data;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Text;
using System.Windows.Forms;
using DBEncapsulation;
using ROFields;
using VlnStatus;
using System.Collections.Specialized;
using Org.Mentalis.Files;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace RODBInterface
{
	public static class ShowCount		// use for debugging only
	{
		public static int GetRootGroups = 0;
		public static int GetChildData = 0;
		public static int ReadRo = 0;
		public static int GetFields = 0;
		public static int Children = 0;
		public static int RoFields = 0;
		public static void OutputResults()
		{
			Console.WriteLine("GetRootGroups = {0}, GetChildData = {1}, ReadRo = {2}, GetFields = {3}, Children = {4}, RoFields = {5}", GetRootGroups, GetChildData, ReadRo, GetFields, Children, RoFields);
		}
	}
// 
	/// C2017-003: support for sql server
	/// The following class handles generation of sql strings to get/put requested parts of
	/// XML RO Tree. It uses queries that are stored in the sql database that contains the ro data.
	/// 
	public partial class SqlRODB : RODB
	{
		public DBEncapsulation.SQLEncap DBE_SQL;
		public string SqlConnectionStr = null;
		private SqlConnection ROAPP_SqlConnectionOnce = null;
		public SqlConnection ROAPP_SqlConnection
		{
			get
			{
				if (OnlyConnectOnce && ROAPP_SqlConnectionOnce != null) return ROAPP_SqlConnectionOnce;
				// Attempt to make a connection 
				SqlConnection cn = new SqlConnection(SqlConnectionStr);
				try
				{
					cn.Open();
					if (OnlyConnectOnce && ROAPP_SqlConnectionOnce == null) ROAPP_SqlConnectionOnce = cn;
					return cn;
				}
				catch (SqlException ex)
				{
					MessageBox.Show(ex.Message, "Could Not Make Connection To Database");
					return null;
				}
			}
		}
		public SqlRODB(string RODirectoyPath, string sqlConnectionStr, bool useConnStr)
		{
			SqlConnectionStr = sqlConnectionStr;
			if (!useConnStr)
				BuildConnectionString(RODirectoyPath, sqlConnectionStr);
			DBE_SQL = new SQLEncap();
			DBE = DBE_SQL;
			try
			{
				strDatabaseConnectionCommand = sqlConnectionStr;
				DBE.Connection(SqlConnectionStr);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Setup of OLEDB/ODBC");
			}
		}
		#region SQL version info
		// The following code was taken from PROMS so that the Database server can be available for the 'About' dialog and the error log:
		private static DateTime _RevDate = DateTime.MinValue;
		public static DateTime RevDate
		{
			get { return _RevDate; }
			set { _RevDate = value; }
		}
		private static string _RevDescription = "Unknown";
		public static string RevDescription
		{
			get { return _RevDescription; }
			set { _RevDescription = value; }
		}
		private string _DBServer = null;
		public string DBServer
		{
			get
			{
				if (_DBServer == null)
				{
					string cnstr = null;
					try
					{
						using (SqlConnection cn = ROAPP_SqlConnection)
						{
							cnstr = cn.ConnectionString;
							try
							{
								using (SqlCommand cmd = new SqlCommand("vesp_GetSQLCodeRevision", cn))
								{
									cmd.CommandType = CommandType.StoredProcedure;
									cmd.CommandTimeout = 0;
									SqlDataReader dr = cmd.ExecuteReader();
									while (dr.Read())
									{
										_RevDate = dr.GetDateTime(0);
										_RevDescription = dr.GetString(1);
									}
								}
							}
							catch (Exception ex)
							{
								_RevDate = DateTime.MinValue;
								_RevDescription = "Unknown";
							}
							string server = "";
							string db = "";
							Match m = Regex.Match(cnstr, "Data Source=([^;]+)(;[^;]+)*;*Initial Catalog=([^;]+)(;[^;]+)*");
							if (m.Success && m.Groups.Count > 4)
							{
								server = m.Groups[1].Value;
								db = m.Groups[3].Value;
							}
							_DBServer = string.Format("{0} - {1} [SQL:{2:yyMM.ddHH}]", server, db, RevDate);
						}
					}
					catch (Exception)
					{
						_DBServer = cnstr;
					}
				}
				return _DBServer;
			}
		}
		#endregion
		#region overrides
		public override string RODB_HasBeenConverted()
		{
			return SqlConnectionStr;
		}
		public override string RODB_GetDBNameForAbout()
		{
			int indx = DBServer.IndexOf("-");
			return string.Format("Database: {0}", DBServer.Substring(indx + 2, DBServer.Length - indx - 2));
		}
		public override string RODB_GetDBServerForAbout()
		{
			return string.Format("SQL Server: {0}", DBServer.Substring(0, DBServer.IndexOf(" -")));
		}
		public override string RODB_GetNextGroupTable()
		{
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;			// B2020-003: missing command connection.
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getInfobyRecidRectype";
					command.Parameters.AddWithValue("@ROTable", "ROMASTER");
					command.Parameters.AddWithValue("@RecID", "00000001");
					command.Parameters.AddWithValue("@RecType", (int)RecordType.Master);
					string rettable = null;
					string newstr = null;
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (!reader.Read())
						{
							MessageBox.Show("Read returned null", "Getting New Group Table");
							return null;
						}
						rettable = reader.GetString(0);
						string nxttable = rettable.Substring(2, 6);
						int ltbl = System.Convert.ToInt32(nxttable, 10);
						ltbl++;
						nxttable = ltbl.ToString("d6"); // this format "d6" should pad left with zeros
						newstr = "RO" + nxttable;
					}
					string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
					command.Parameters.Clear();
					command.CommandText = "updateInfoByRecidRectype";
					command.Parameters.AddWithValue("@ROTable", "ROMASTER");
					command.Parameters.AddWithValue("@Info", newstr);
					command.Parameters.AddWithValue("@ModDateTime", dt);
					command.Parameters.AddWithValue("@RecID", "00000001");
					command.Parameters.AddWithValue("@RecType", (int)RecordType.Master);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						return rettable;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Getting New Group Table");
			}
			return null;
		}
		public override string RODB_GetNextRecId(string table)
		{
			// read record type of master for this table. This is where the highest recid is stored.
			// Update the recid by 1, write it out and return the new recid.
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getInfobyRecidRectype";
					command.Parameters.AddWithValue("@ROTable", table);
					command.Parameters.AddWithValue("@RecID", "00000000");
					command.Parameters.AddWithValue("@RecType", (int)RecordType.Master);
					string retrec = null;
					string padstr = null;
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (!reader.Read())
						{
							MessageBox.Show("Read returned null", "Getting New Record Id");
							return null;
						}
						retrec = reader.GetString(0);
						int lrecid = System.Convert.ToInt32(retrec, 16);
						lrecid++;
						retrec = lrecid.ToString("x");
						// need an 8 char string so pad left with zero
						padstr = retrec.PadLeft(8, '0');
					}
					string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
					command.Parameters.Clear();
					command.CommandText = "updateInfoByRecidRectype";
					command.Parameters.AddWithValue("@ROTable", table);
					command.Parameters.AddWithValue("@RecID", "00000000");
					command.Parameters.AddWithValue("@Info", padstr);
					command.Parameters.AddWithValue("@ModDateTime", dt);
					command.Parameters.AddWithValue("@RecType", (int)RecordType.Master);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						return padstr;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Getting New Record Id");
			}
			return null;
		}
		public override bool RODB_GetRootGroups(VlnXmlElement root)
		{
			ShowCount.GetRootGroups++;
			string table;
			string group;
			string title;
			// Get menu fields to display
			SortedList mySL = new SortedList();
			try
			{
				if (OnlyConnectOnce)
				{
					SqlConnection cn = ROAPP_SqlConnection;
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getRecidInfoByRectypeAsc";
					command.Parameters.AddWithValue("@ROTable", "ROMaster");
					command.Parameters.AddWithValue("@RecType", (int)RecordType.SubDatabase);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						while (reader.Read())
						{
							// Parse info string, it has group name & table name. Store in a sorted
							// list, sorting by Group Name.
							string mrecid = reader.GetString(0);
							group = reader.GetString(1);
							mySL.Add(mrecid, group);
						}
					}
				}
				else
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getRecidInfoByRectypeAsc";
						command.Parameters.AddWithValue("@ROTable", "ROMaster");
						command.Parameters.AddWithValue("@RecType", (int)RecordType.SubDatabase);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							while (reader.Read())
							{
								// Parse info string, it has group name & table name. Store in a sorted
								// list, sorting by Group Name.
								string mrecid = reader.GetString(0);
								group = reader.GetString(1);
								mySL.Add(mrecid, group);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Root Groups");
				return false;
			}
			// Now, for each group, i.e. table, make xml elements for the top level groups by
			// reading in the Xml Element for the group from each table.
			for (int i = 0; i < mySL.Count; i++)
			{
				group = mySL.GetByIndex(i).ToString();
				int grnamindx = group.IndexOf("\t");
				if (grnamindx == -1)
				{// no tab, default to RO000001
					table = "RO000001";
					title = group;
				}
				else
				{
					StringBuilder tablesb = new StringBuilder(70);
					tablesb.Append(group);
					tablesb.Remove(0, grnamindx + 1);
					table = tablesb.ToString();
					title = group.Substring(0, grnamindx);
				}
				try
				{
					if (OnlyConnectOnce)
					{
						SqlConnection cn = ROAPP_SqlConnection;
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getRecidInfoByRectypeParidAsc";
						command.Parameters.AddWithValue("@ROTable", table);
						command.Parameters.AddWithValue("@RecType", (int)RecordType.Group);
						command.Parameters.AddWithValue("@ParentID", "00000000");
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								string RecID = reader.GetString(0);
								string Info = reader.GetString(1);
								XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
								XmlNode nd = ROXml.ReadNode(rdr);
								VlnXmlElement elem = (VlnXmlElement)nd;
								elem.MyROID = RecID;
								root.AppendChild(elem);
								elem.SetAttribute("RecID", RecID);
								elem.SetAttribute("ParentID", "00000000");
								elem.SetAttribute("Table", table);
								elem.SetAttribute("MasterRecID", mySL.GetKey(i).ToString());
							}
						}
					}
					else
					{
						using (SqlConnection cn = ROAPP_SqlConnection)
						{
							SqlCommand command = new SqlCommand();
							command.Connection = cn;
							command.CommandType = CommandType.StoredProcedure;
							command.CommandText = "getRecidInfoByRectypeParidAsc";
							command.Parameters.AddWithValue("@ROTable", table);
							command.Parameters.AddWithValue("@RecType", (int)RecordType.Group);
							command.Parameters.AddWithValue("@ParentID", "00000000");
							using (SqlDataReader reader = command.ExecuteReader())
							{
								if (reader.Read())
								{
									string RecID = reader.GetString(0);
									string Info = reader.GetString(1);
									XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
									XmlNode nd = ROXml.ReadNode(rdr);
									VlnXmlElement elem = (VlnXmlElement)nd;
									elem.MyROID = RecID;
									root.AppendChild(elem);
									elem.SetAttribute("RecID", RecID);
									elem.SetAttribute("ParentID", "00000000");
									elem.SetAttribute("Table", table);
									elem.SetAttribute("MasterRecID", mySL.GetKey(i).ToString());
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error Getting Root Groups");
					return false;
				}
			}
			// get child count for each group.
			XmlNode node = root.FirstChild;
			while (node != null)
			{
				if (node is VlnXmlElement)
				{
					VlnXmlElement child = (VlnXmlElement)node;
					string tablex = child.GetAttribute("Table");
					//get child count for each table listed in innertext.
					try
					{
						if (OnlyConnectOnce)
						{
							SqlConnection cn = ROAPP_SqlConnection;
							SqlCommand command = new SqlCommand();
							command.Connection = cn;
							command.CommandType = CommandType.StoredProcedure;
							command.CommandText = "getCountRecidByParid";
							command.Parameters.AddWithValue("@ROTable", tablex);
							command.Parameters.AddWithValue("@ParentID", child.GetAttribute("RecID"));
							using (SqlDataReader reader = command.ExecuteReader())
							{
								if (reader.Read())
								{
									int cnt = reader.GetInt32(0);
									if (cnt > 0)
										child.SetAttribute("HasChild", "True");
									else
										child.SetAttribute("HasChild", "False");
									child.SetAttribute("ChildLoaded", "False");
								}
							}
						}
						else
						{
							using (SqlConnection cn = ROAPP_SqlConnection)
							{
								SqlCommand command = new SqlCommand();
								command.Connection = cn;
								command.CommandType = CommandType.StoredProcedure;
								command.CommandText = "getCountRecidByParid";
								command.Parameters.AddWithValue("@ROTable", tablex);
								command.Parameters.AddWithValue("@ParentID", child.GetAttribute("RecID"));
								using (SqlDataReader reader = command.ExecuteReader())
								{
									if (reader.Read())
									{
										int cnt = reader.GetInt32(0);
										if (cnt > 0)
											child.SetAttribute("HasChild", "True");
										else
											child.SetAttribute("HasChild", "False");
										child.SetAttribute("ChildLoaded", "False");
									}
								}
							}
						}
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.Message, "Error Getting Root Groups");
						return false;
					}
				}
				node = node.NextSibling;
			}
			// for the beginning and end schema element, go to the master & read it from there. Store these to be used
			// by all of the databases.
			schemastart = null;
			try
			{
				if (OnlyConnectOnce)
				{
					SqlConnection cn = ROAPP_SqlConnection;
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getInfoByParidRectype";
					command.Parameters.AddWithValue("@ROTable", "ROMaster");
					command.Parameters.AddWithValue("@ParentID", "00000000");
					command.Parameters.AddWithValue("@RecType", (int)RecordType.SchemaStart);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
						{
							schemastart = reader.GetString(0);
						}
					}
				}
				else
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getInfoByParidRectype";
						command.Parameters.AddWithValue("@ROTable", "ROMaster");
						command.Parameters.AddWithValue("@ParentID", "00000000");
						command.Parameters.AddWithValue("@RecType", (int)RecordType.SchemaStart);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								schemastart = reader.GetString(0);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Root Groups");
				return false;
			}
			schemaend = null;
			try
			{
				if (OnlyConnectOnce)
				{
					SqlConnection cn = ROAPP_SqlConnection;
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getInfoByParidRectype";
					command.Parameters.AddWithValue("@ROTable", "ROMaster");
					command.Parameters.AddWithValue("@ParentID", "00000000");
					command.Parameters.AddWithValue("@RecType", (int)RecordType.SchemaEnd);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
						{
							schemaend = reader.GetString(0);
						}
					}
				}
				else
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getInfoByParidRectype";
						command.Parameters.AddWithValue("@ROTable", "ROMaster");
						command.Parameters.AddWithValue("@ParentID", "00000000");
						command.Parameters.AddWithValue("@RecType", (int)RecordType.SchemaEnd);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								schemaend = reader.GetString(0);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Root Groups");
				return false;
			}
			return true;
		}
		public override bool RODB_DeleteGroup(XmlNode group, string tbname, string toprecid)
		{
			VlnXmlElement egroup = null;
			// delete table entry in ROMaster
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "deleteByRecid";
					command.Parameters.AddWithValue("@ROTable", "ROMaster");
					if (group != null)
					{
						egroup = (VlnXmlElement)group;
						command.Parameters.AddWithValue("@RecID", egroup.GetAttribute("MasterRecID"));
					}
					else
						command.Parameters.AddWithValue("@RecID", toprecid);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						reader.Read();		// if this fails, it will go to the catch.
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error When deleting SubGroup ROs.");
				return false;
			}
			// now remove all entries in the table (from roall)
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "deleteByROTable";
					if (egroup != null)
					{
						command.Parameters.AddWithValue("@ROTable", egroup.GetAttribute("Table"));
					}
					else
						command.Parameters.AddWithValue("@ROTable", tbname);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						reader.Read();		// if this fails, it will go to the catch.
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error When deleting SubGroup ROs.");
				return false;
			}
			return true;
		}
		public override bool RODB_DeleteRO(XmlNode group)
		{
			// delete from database and then delete from tree. delete group and all subgroups & ros beneath it. delete  
			// subgroups by calling this.  note that if just an ro is passed in, it will only delete it.
			XmlNode node = group.FirstChild;
			VlnXmlElement child;
			VlnXmlElement egroup = (VlnXmlElement)group;
			if (group.Name == "vlnGroup")
			{
				while (node != null)
				{
					if (node is VlnXmlElement)
					{
						child = (VlnXmlElement)node;
						// if this is a group, call this again.
						if (child.Name == "vlnGroup")
						{
							bool success = RODB_DeleteRO(node);
							if (success == false)
							{
								MessageBox.Show("error in RODB_DeleteGroup");
								return false;
							}
						}
					}
					node = node.NextSibling;
				}
				// delete all db records that have the ParentID = to this group RecID.
				// (All subgroups should be processed from above recursion.
				try
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "deleteByParid";
						command.Parameters.AddWithValue("@ROTable", egroup.GetAttribute("Table"));
						command.Parameters.AddWithValue("@ParentID", egroup.GetAttribute("RecID"));
						using (SqlDataReader reader = command.ExecuteReader())
						{
							reader.Read();		// if this fails, it will go to the catch.
						}
					}
				}
				catch (Exception e)
				{
					MessageBox.Show(e.Message, "Error When deleting SubGroup ROs.");
					return false;
				}
			}
			// now delete this ro or group, i.e. the one passed in.
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "deleteByRecid";
					command.Parameters.AddWithValue("@ROTable", egroup.GetAttribute("Table"));
					command.Parameters.AddWithValue("@RecID", egroup.GetAttribute("RecID"));
					using (SqlDataReader reader = command.ExecuteReader())
					{
						reader.Read();
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error When deleting this RO.");
				return false;
			}
			return true;
		}
		public override bool RODB_WriteGroup(XmlNode group, VlnXmlElement master)
		{
			bool success = true;
			string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			string xmlstr = GenerateXmlString(group, true);
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "updateInfoByRecid";
					command.Parameters.AddWithValue("@ROTable", master.GetAttribute("Table"));
					command.Parameters.AddWithValue("@Info", xmlstr);
					command.Parameters.AddWithValue("@ModDateTime", dt);
					command.Parameters.AddWithValue("@RecID", master.GetAttribute("RecID"));
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error on Write Group");
				success = false;
			}
			return success;
		}
		public override bool RODB_InsertGroup(VlnXmlElement group)
		{
			bool success = true;
			string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			string xmlstr = GenerateXmlString(group, true);
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "insertAllRectypes";
					command.Parameters.AddWithValue("@ROTable", group.GetAttribute("Table"));
					command.Parameters.AddWithValue("@RecID", group.GetAttribute("RecID"));
					command.Parameters.AddWithValue("@RecType", (int)RecordType.Group);
					command.Parameters.AddWithValue("@ParentID", group.GetAttribute("ParentID"));
					command.Parameters.AddWithValue("@AccPageID", "");		// B2020-003: make null empty string.
					command.Parameters.AddWithValue("@Info", xmlstr);
					command.Parameters.AddWithValue("@ModDateTime", dt);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error on Insert group");
				success = false;
			}
			return success;
		}
		public override string RODB_AddNewTable(string TblName, string newgrpname)
		{
			//for sql server version, a new table is not added. A new value for the ROTable field is used (TblName that is passed in )
			bool success = false;
			// now insert the first record with the new table name
			string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "insertAllRectypes";
					command.Parameters.AddWithValue("@ROTable", TblName);
					command.Parameters.AddWithValue("@RecID", "00000000");
					command.Parameters.AddWithValue("@RecType", 0);
					command.Parameters.AddWithValue("@ParentID", "00000000");
					command.Parameters.AddWithValue("@AccPageID", "");		// B2020-003: make null empty string.
					command.Parameters.AddWithValue("@Info", "00000001");
					command.Parameters.AddWithValue("@ModDateTime", dt);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error Creating New " + TblName);
				success = false;
			}
			// return if error on insert of master record of new table, otherwise add
			// this table reference to the master.
			if (success == false) return null;
			string recid = RODB_GetNextRecId("ROMaster");
			string info = CvtUserFldToFld(newgrpname) + "\t" + TblName;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "insertAllRectypes";
					command.Parameters.AddWithValue("@ROTable", "ROMaster");
					command.Parameters.AddWithValue("@RecID", recid);
					command.Parameters.AddWithValue("@RecType", 1);
					command.Parameters.AddWithValue("@ParentID", "00000001");
					command.Parameters.AddWithValue("@AccPageID", "");		// B2020-003: make null empty string.
					command.Parameters.AddWithValue("@Info", info);
					command.Parameters.AddWithValue("@ModDateTime", dt);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error Writing new table to master.");
				success = false;
			}
			if (success == false)
				return null;
			else
				return recid;
		}
		public override bool RODB_CopyFieldDefs(string fromtb, string totb, int type)
		{
			// Copy over field definitions from the ROMaster to the input table name.
			ArrayList retlist = new ArrayList();
			string Info;
			string RecID;
			string name;
			uint ftype;
			ROField rof;
			bool success;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getRecidInfoByRectype";
					command.Parameters.AddWithValue("@ROTable", "ROMaster");
					command.Parameters.AddWithValue("@RecType", 2);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						while (reader.Read())
						{
							RecID = reader.GetString(0);
							Info = reader.GetString(1);
							// it's defined in the local table if the first character is "<" which starts
							// the schema definition string.
							name = ParseEleName(Info);
							if (name != null)
							{
								// what type of schema element?
								rof = new ROField(name, RecID, null, 0);
								ftype = rof.ParseFieldType(Info);
								rof.SetFieldType(ftype);
								retlist.Add((object)rof);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error on Copy Field Definitions");
				return false;
			}
			// using this list, add new records for field definition references to the new
			// table.
			for (int i = 0; i < retlist.Count; i++)
			{
				rof = (ROField)retlist[i];
				if (rof.GetFieldname != null)
				{
					string mrecid = rof.GetRecID;
					string lrecid = RODB_GetNextRecId(totb);
					StringBuilder strbld = new StringBuilder();
					strbld.Append(mrecid);
					strbld.Append(" ");
					string strtype = rof.GetFieldType.ToString();
					strbld.Append(strtype.PadLeft(3, '0'));
					strbld.Append(" ");
					if (strtype == "128") // append an 'a' for combo type
						strbld.Append(rof.MakeFieldName(rof.GetFieldname) + "a");
					else
						strbld.Append(rof.MakeFieldName(rof.GetFieldname));
					// add new field.
					success = RODB_NewSchemaPiece(lrecid, "00000002", totb, strbld.ToString(), 2);
					if (success == false) return false;
				}
			}
			return true;
		}
		public override bool RODB_GetGroupAndSubgroups(VlnXmlElement node, StringBuilder sb)
		{
			// This is similar to GetChildData() but this function reads in all the subgroups for the given node
			// Note that this function is designed to be call for the root node (i.e. it gets all of group and RO data 
			// for a given database table)
			VlnStatusBar StatBar = new VlnStatusBar("Reading from the Database");
			string tablename = node.GetAttribute("Table");
			HybridDictionary dicGroups = new HybridDictionary();
			dicGroups.Add(node.GetAttribute("RecID").ToString(), node);
			StatBar.BarMax = RODB_GetNumberOfGroupRecords(tablename); //get number of groups
			StatBar.BarStepValue = 5;
			StatBar.StatMsg = node.InnerText;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getGroupAndSubgroups";
					command.Parameters.AddWithValue("@ROTable", tablename);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (!reader.Read())
						{
							StatBar.PerformStep(1);
							reader.Close();
							return false;
						}
						while (reader.Read())
						{
							string RecID = reader.GetString(0);
							int RecType = reader.GetInt32(1);
							string ParID = reader.GetString(2);
							string AccPageID = reader.GetString(3);
							string Info = reader.GetString(4);
							if (!ParID.Equals(node.GetAttribute("RecID")))
							{
								if (!dicGroups.Contains(ParID))
								{
									if (sb.Length == 0)
									{
										sb.AppendLine("The following records were skipped.");
										sb.AppendLine("The parent for these RO's was missing.");
										sb.AppendLine();
									}
									string strMBText = "RecID: " + RecID + "\n\n  Table: " + tablename + "\n";
									sb.AppendLine(strMBText);
									continue; // skip - no parent for this node
								}
								node = (VlnXmlElement)dicGroups[ParID];
								node.SetAttribute("HasChild", "True");
								node.SetAttribute("ChildLoaded", "True");
							}
							else
							{
								node.SetAttribute("ChildLoaded", "True");
							}
							if (RecType == (int)RecordType.Group)
							{
								VlnXmlElement elem;
								StatBar.PerformStep();
								try
								{
									XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
									XmlNode nd = ROXml.ReadNode(rdr);
									elem = (VlnXmlElement)nd;
								}
								catch (Exception e)
								{
									MessageBox.Show(e.Message, "Error reading Xml Group From data");
									reader.Close();
									return false;
								}
								elem.MyROID = RecID;
								elem.SetAttribute("RecID", RecID);
								elem.SetAttribute("ParentID", node.GetAttribute("RecID"));
								elem.SetAttribute("Table", node.GetAttribute("Table"));
								if (!dicGroups.Contains(RecID))
								{
									node.AppendChild(elem);
									dicGroups.Add(RecID, elem);
								}
								// The Parameter Display Data is stored along with the group. This data
								// was migrated over so that the data was not lost, but there is no
								// User Interface to get to it. So remove the xml elements from the
								// group part of tree. All valid group data only has one sublevel of
								// data defining the group name.
								XmlNode grp = (XmlNode)elem;
								XmlNode kid = grp.FirstChild;
								XmlNode tmpkid;
								while (kid != null)
								{
									tmpkid = kid.NextSibling;
									if (kid is VlnXmlElement)
										if (kid.ChildNodes.Count > 1) grp.RemoveChild(kid);
									kid = tmpkid;
								}
							}
							// Store data in the VlnXmlElement as an RO
							else if (RecType == (int)RecordType.RRO)
							{
								//Create the reader. 
								XmlNode ro;
								try
								{
									XmlTextReader roreader = new XmlTextReader(Info, XmlNodeType.Element, null);
									ro = ROXml.ReadNode(roreader);
								}
								catch (Exception e)
								{
									MessageBox.Show(e.Message, "Error reading Xml RRO From data");
									reader.Close();
									return false;
								}
								VlnXmlElement elem = (VlnXmlElement)ro;
								node.AppendChild(ro);
								elem.MyROID = RecID;
								elem.SetAttribute("RecID", RecID);
								elem.SetAttribute("ParentID", node.GetAttribute("RecID"));
								elem.SetAttribute("Table", node.GetAttribute("Table"));
								elem.SetAttribute("AccPageID", AccPageID);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error reading Xml RRO From data");
				return false;
			}
			dicGroups = null;
			StatBar.Dispose();
			return true;
		}
		public override bool RODB_GetChildData(VlnXmlElement node, bool CheckChildCount)
		{
			ShowCount.GetChildData++;
			// get menu fields to display here. 
			string tablename = node.GetAttribute("Table");
			try
			{
				if (OnlyConnectOnce)
				{
					SqlConnection cn = ROAPP_SqlConnection;
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getChildData";
					command.Parameters.AddWithValue("@ROTable", tablename);
					command.Parameters.AddWithValue("@ParentID", node.GetAttribute("RecID"));
					using (SqlDataReader reader = command.ExecuteReader())
					{
						while (reader.Read())
						{
							ShowCount.Children++;
							string RecID = reader.GetString(0);
							MyRecID = RecID;
							//if (RecID.StartsWith("000300006193")) Console.WriteLine("here");
							if (RecID.StartsWith("0003")) Console.WriteLine("here");
							if (RecID.EndsWith("6193")) Console.WriteLine("here");
							int RecType = reader.GetInt32(1);
							string AccPageID = reader.GetString(2);
							string Info = reader.GetString(3);
							node.SetAttribute("HasChild", "True");
							if (RecType == (int)RecordType.Group)
							{
								VlnXmlElement elem;
								try
								{
									XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
									XmlNode nd = ROXml.ReadNode(rdr);
									elem = (VlnXmlElement)nd;
								}
								catch (Exception e)
								{
									MessageBox.Show(e.Message, "Error Reading Xml Group From Data");
									return false;
								}
								node.AppendChild(elem);
								elem.MyROID = RecID;
								elem.SetAttribute("RecID", RecID);
								elem.SetAttribute("ParentID", node.GetAttribute("RecID"));
								elem.SetAttribute("Table", node.GetAttribute("Table"));
								// The Parameter Display Data is stored along with the group. This data
								// was migrated over so that the data was not lost, but there is no
								// User Interface to get to it. So remove the xml elements from the
								// group part of tree. All valid group data only has one sublevel of
								// data defining the group name.
								XmlNode grp = (XmlNode)elem;
								XmlNode kid = grp.FirstChild;
								XmlNode tmpkid;
								while (kid != null)
								{
									tmpkid = kid.NextSibling;
									if (kid is VlnXmlElement)
										if (kid.ChildNodes.Count > 1) grp.RemoveChild(kid);
									kid = tmpkid;
								}
							}
							// Store data in the VlnXmlElement as an RO
							else if (RecType == (int)RecordType.RRO)
							{
								//Create the reader. 
								XmlNode ro;
								try
								{
									XmlTextReader roreader = new XmlTextReader(Info, XmlNodeType.Element, null);
									ro = ROXml.ReadNode(roreader);
								}
								catch (Exception e)
								{
									MessageBox.Show(e.Message, "Error Reading Xml RRO From Data");
									return false;
								}
								VlnXmlElement elem = (VlnXmlElement)ro;
								elem.MyROID = RecID;
								node.AppendChild(ro);
								elem.SetAttribute("RecID", RecID);
								elem.SetAttribute("ParentID", node.GetAttribute("RecID"));
								elem.SetAttribute("Table", node.GetAttribute("Table"));
								elem.SetAttribute("AccPageID", AccPageID);
							}
						}
					}
				}
				else
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getChildData";
						command.Parameters.AddWithValue("@ROTable", tablename);
						command.Parameters.AddWithValue("@ParentID", node.GetAttribute("RecID"));
						using (SqlDataReader reader = command.ExecuteReader())
						{
							while (reader.Read())
							{
								ShowCount.Children++;
								string RecID = reader.GetString(0);
								MyRecID = RecID;
								//if (RecID.StartsWith("000300006193")) Console.WriteLine("here");
								if (RecID.StartsWith("0003")) Console.WriteLine("here");
								if (RecID.EndsWith("6193")) Console.WriteLine("here");
								int RecType = reader.GetInt32(1);
								string AccPageID = reader.GetString(2);
								string Info = reader.GetString(3);
								node.SetAttribute("HasChild", "True");
								if (RecType == (int)RecordType.Group)
								{
									VlnXmlElement elem;
									try
									{
										XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
										XmlNode nd = ROXml.ReadNode(rdr);
										elem = (VlnXmlElement)nd;
									}
									catch (Exception e)
									{
										MessageBox.Show(e.Message, "Error Reading Xml Group From Data");
										return false;
									}
									node.AppendChild(elem);
									elem.MyROID = RecID;
									elem.SetAttribute("RecID", RecID);
									elem.SetAttribute("ParentID", node.GetAttribute("RecID"));
									elem.SetAttribute("Table", node.GetAttribute("Table"));
									// The Parameter Display Data is stored along with the group. This data
									// was migrated over so that the data was not lost, but there is no
									// User Interface to get to it. So remove the xml elements from the
									// group part of tree. All valid group data only has one sublevel of
									// data defining the group name.
									XmlNode grp = (XmlNode)elem;
									XmlNode kid = grp.FirstChild;
									XmlNode tmpkid;
									while (kid != null)
									{
										tmpkid = kid.NextSibling;
										if (kid is VlnXmlElement)
											if (kid.ChildNodes.Count > 1) grp.RemoveChild(kid);
										kid = tmpkid;
									}
								}
								// Store data in the VlnXmlElement as an RO
								else if (RecType == (int)RecordType.RRO)
								{
									//Create the reader. 
									XmlNode ro;
									try
									{
										XmlTextReader roreader = new XmlTextReader(Info, XmlNodeType.Element, null);
										ro = ROXml.ReadNode(roreader);
									}
									catch (Exception e)
									{
										MessageBox.Show(e.Message, "Error Reading Xml RRO From Data");
										return false;
									}
									VlnXmlElement elem = (VlnXmlElement)ro;
									elem.MyROID = RecID;
									node.AppendChild(ro);
									elem.SetAttribute("RecID", RecID);
									elem.SetAttribute("ParentID", node.GetAttribute("RecID"));
									elem.SetAttribute("Table", node.GetAttribute("Table"));
									elem.SetAttribute("AccPageID", AccPageID);
								}
							}
						}
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error Getting Child Data");
				return false;
			}
			// get child count for each subgroup.
			if (CheckChildCount)
			{
				//Build a Dictionary of Children with Children from  the query results
				HybridDictionary dicChild = new HybridDictionary();
				//A different approach - Look for grandchildren and set the haschildren attribute
				//Build a query to get a list of children with children
				try
				{
					if (OnlyConnectOnce)
					{
						SqlConnection cn = ROAPP_SqlConnection;
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getCountChildrenOfChildData";
						command.Parameters.AddWithValue("@ROTable", tablename);
						command.Parameters.AddWithValue("@ParentID", node.GetAttribute("RecID"));
						using (SqlDataReader reader = command.ExecuteReader())
						{
							while (reader.Read())
							{
								string tmp = reader.GetString(0);
								int tmpi = reader.GetInt32(1);
								dicChild[tmp] = tmpi;
							}
						}
					}
					else
					{
						using (SqlConnection cn = ROAPP_SqlConnection)
						{
							SqlCommand command = new SqlCommand();
							command.Connection = cn;
							command.CommandType = CommandType.StoredProcedure;
							command.CommandText = "getCountChildrenOfChildData";
							command.Parameters.AddWithValue("@ROTable", tablename);
							command.Parameters.AddWithValue("@ParentID", node.GetAttribute("RecID"));
							using (SqlDataReader reader = command.ExecuteReader())
							{
								while (reader.Read())
								{
									string tmp = reader.GetString(0);
									int tmpi = reader.GetInt32(1);
									//dicChild[reader.GetString(0)] = reader.GetInt32(1);//This adds the entry and sets the value
									dicChild[tmp] = tmpi;
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error Reading Child Counts");
					return false;
				}
				XmlNode child = node.FirstChild;
				while (child != null) //Look at each child node
				{
					if (child is VlnXmlElement)
					{
						VlnXmlElement echild = (VlnXmlElement)child;
						if (echild.Name == "vlnGroup")
						{
							//If the dictionary contains an entry, the child has children
							echild.SetAttribute("HasChild", (dicChild.Contains(echild.GetAttribute("RecID"))) ? "True" : "False");
							echild.SetAttribute("ChildLoaded", "False");
						}
					}
					child = child.NextSibling;
				}
				dicChild = null;
			}
			node.SetAttribute("ChildLoaded", "True");
			return true;
		}
		public override bool IsDuplicateAccPageID(VlnXmlElement ro, string newacc)
		{
			string inacc = null;
			bool isdup = false;
			int indx = newacc.IndexOf("'");
			if (indx >= 0)
				inacc = newacc.Insert(indx, "'");
			else
				inacc = newacc;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					if (ro.HasAttribute("RecID"))  // new ro's don't have recid defined yet before this test.
					{
						command.CommandText = "getCountRecidByAccidNotRecid";
						command.Parameters.AddWithValue("RecID", ro.GetAttribute("RecID"));
					}
					else
						command.CommandText = "getCountRecidByAccid";
					command.Parameters.AddWithValue("@AccPageID", inacc);
					command.Parameters.AddWithValue("@ROTable", ro.GetAttribute("Table"));
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
						{
							int cnt = reader.GetInt32(0);
							if (cnt > 0)
								isdup = true;
							else
								isdup = false;
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error on IsDuplicateAccPageID");
				return true;
			}
			return isdup;
		}
		public override VlnXmlElement RODB_ReadRO(string tbl, string recid)
		{
			ShowCount.ReadRo++;
			VlnXmlElement retele = null;
			if (OnlyConnectOnce)
			{
				try
				{
					SqlConnection cn = ROAPP_SqlConnection;
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					if (recid == null)
					{
						command.CommandText = "getParidAccidInfoByRectypeParid";
						command.Parameters.AddWithValue("@ROTable", tbl);
						command.Parameters.AddWithValue("@ParentID", "00000000");
						command.Parameters.AddWithValue("@RecType", 3);
					}
					else
					{
						command.CommandText = "getParidAccidInfoByRecid";
						command.Parameters.AddWithValue("@ROTable", tbl);
						command.Parameters.AddWithValue("@RecId", recid);
					}
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
						{
							string ParentID = reader.GetString(0);
							string AccPageID = reader.GetString(1);
							string Info = reader.GetString(2);
							// Store data in the VlnXmlElement
							XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
							XmlNode nd = ROXml.ReadNode(rdr);
							retele = (VlnXmlElement)nd;
							retele.SetAttribute("RecID", recid);
							retele.SetAttribute("ParentID", ParentID);
							retele.SetAttribute("Table", tbl);
							if (retele.Name != "vlnGroup") retele.SetAttribute("AccPageID", AccPageID);
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error on ReadRo");
					return null;
				}
			}
			else
			{
				try
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						if (recid == null)
						{
							command.CommandText = "getParidAccidInfoByRectypeParid";
							command.Parameters.AddWithValue("@ROTable", tbl);
							command.Parameters.AddWithValue("@ParentID", "00000000");
							command.Parameters.AddWithValue("@RecType", 3);
						}
						else
						{
							command.CommandText = "getParidAccidInfoByRecid";
							command.Parameters.AddWithValue("@ROTable", tbl);
							command.Parameters.AddWithValue("@RecId", recid);
						}
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								string ParentID = reader.GetString(0);
								string AccPageID = reader.GetString(1);
								string Info = reader.GetString(2);
								// Store data in the VlnXmlElement
								XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null);
								XmlNode nd = ROXml.ReadNode(rdr);
								retele = (VlnXmlElement)nd;
								retele.SetAttribute("RecID", recid);
								retele.SetAttribute("ParentID", ParentID);
								retele.SetAttribute("Table", tbl);
								if (retele.Name != "vlnGroup") retele.SetAttribute("AccPageID", AccPageID);
							}
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error on ReadRo");
					return null;
				}
			}
			// if this is a group, see if children. If the RO is not found, return null
			if (retele != null && retele.Name == "vlnGroup")
			{
				if (OnlyConnectOnce)
				{
					try
					{
						SqlConnection cn = ROAPP_SqlConnection;
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getCountRecidByParid";
						command.Parameters.AddWithValue("@ROTable", tbl);
						if (recid == null)
							command.Parameters.AddWithValue("@ParentID", "00000000");
						else
							command.Parameters.AddWithValue("@ParentID", recid);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								int cnt = reader.GetInt32(0);
								if (cnt > 0)
									retele.SetAttribute("HasChild", "True");
								else
									retele.SetAttribute("HasChild", "False");
							}
							else
								retele.SetAttribute("HasChild", "False");
							retele.SetAttribute("ChildLoaded", "False");
						}
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.Message, "Error on ReadRo");
						return null;
					}
				}
				else
				{
					try
					{
						using (SqlConnection cn = ROAPP_SqlConnection)
						{
							SqlCommand command = new SqlCommand();
							command.Connection = cn;
							command.CommandType = CommandType.StoredProcedure;
							command.CommandText = "getCountRecidByParid";
							command.Parameters.AddWithValue("@ROTable", tbl);
							if (recid == null)
								command.Parameters.AddWithValue("@ParentID", "00000000");
							else
								command.Parameters.AddWithValue("@ParentID", recid);
							using (SqlDataReader reader = command.ExecuteReader())
							{
								if (reader.Read())
								{
									int cnt = reader.GetInt32(0);
									if (cnt > 0)
										retele.SetAttribute("HasChild", "True");
									else
										retele.SetAttribute("HasChild", "False");
								}
								else
									retele.SetAttribute("HasChild", "False");
								retele.SetAttribute("ChildLoaded", "False");
							}
						}
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.Message, "Error on ReadRo");
						return null;
					}
				}
			}
			return retele;
		}
		public override bool RODB_WriteRO(VlnXmlElement ro)
		{
			bool success;
			if (ro.Name == "vlnGroup")
			{
				success = RODB_WriteGroup(ro, ro);
				return success;
			}
			else
			{
				string wraccid = null;
				string accid = ro.GetAttribute("AccPageID");
				int quote = accid.IndexOf("'");
				if (quote >= 0)
					wraccid = accid.Insert(quote, "'");
				else
					wraccid = accid;
				string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
				string xmlstr = GenerateXmlString(ro, false);
				try
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "updateInfoAccidByRecid";
						command.Parameters.AddWithValue("@ROTable", ro.GetAttribute("Table"));
						command.Parameters.AddWithValue("@Info", xmlstr);
						command.Parameters.AddWithValue("@ModDateTime", dt);
						command.Parameters.AddWithValue("@AccPageID", wraccid);		// B2020-003: set accpageid to correct value
						command.Parameters.AddWithValue("@RecID", ro.GetAttribute("RecID"));
						using (SqlDataReader reader = command.ExecuteReader())
						{
							success = true;
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error on Write RO");
					success = false;
				}
			}
			return success;
		}
		public override bool RODB_InsertRO(VlnXmlElement ro)
		{
			bool success = false;
			string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			VlnXmlElement parent = (VlnXmlElement)ro.ParentNode;
			string lrecid = RODB_GetNextRecId(parent.GetAttribute("Table"));
			ro.SetAttribute("Table", parent.GetAttribute("Table"));
			ro.SetAttribute("RecID", lrecid);
			ro.SetAttribute("ParentID", parent.GetAttribute("RecID"));
			string haskids = parent.GetAttribute("HasChild");
			if (haskids == "False" || haskids == "")
			{
				parent.SetAttribute("HasChild", "True");
				parent.SetAttribute("ChildLoaded", "True");
			}
			string xmlstr = GenerateXmlString(ro, false);
			string wraccid = null;
			if (ro.HasAttribute("AccPageID"))
			{
				string accid = ro.GetAttribute("AccPageID");
				int quote = accid.IndexOf("'");
				if (quote >= 0)
					wraccid = accid.Insert(quote, "'");
				else
					wraccid = accid;
			}
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "insertAllRectypes";
					if (ro.Name == "vlnGroup")
					{
						// add attribute to flag that this does not have xml children, i.e. any
						// other subgroups or ros under it.
						ro.SetAttribute("HasChild", "False");
						if (ro.HasAttribute("AccPageID"))
						{
							command.Parameters.AddWithValue("@RecType", (int)RecordType.Group);
							command.Parameters.AddWithValue("@AccPageID", wraccid);
						}
						else
						{
							command.Parameters.AddWithValue("@RecType", (int)RecordType.Group);
							command.Parameters.AddWithValue("@AccPageID", "");		// B2020-003: set accpageid to correct value
						}
					}
					else
					{
						command.Parameters.AddWithValue("@RecType", (int)RecordType.RRO);
						command.Parameters.AddWithValue("@AccPageID", wraccid);
					}
					command.Parameters.AddWithValue("@ROTable", parent.GetAttribute("Table"));
					command.Parameters.AddWithValue("@RecID", ro.GetAttribute("RecID"));
					command.Parameters.AddWithValue("@ParentID", ro.GetAttribute("ParentID"));
					command.Parameters.AddWithValue("@Info", xmlstr);
					command.Parameters.AddWithValue("@ModDateTime", dt);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error on Insert RO");
				success = false;
			}
			return success;
		}
		public override ushort RODB_GetFieldType(VlnXmlElement elem, string TableName, string Fld)
		{
			string Info;
			string RecID;
			string name;
			string tmpname;
			ushort ftype = 0;
			ROField rof;
			if (TableName != lastTable)
			{
				lastTable = TableName;
				dicFldTypes = new HybridDictionary();
				try
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getRecidInfoByRectype";
						command.Parameters.AddWithValue("@ROTable", TableName);
						command.Parameters.AddWithValue("@RecType", 2);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							// NOTE !!!!
							// This function does not handle Combo type fields (128).
							while (reader.Read())
							{
								RecID = reader.GetString(0);
								Info = reader.GetString(1);
								// it's defined in the local table if the first character is "<" which starts
								// the schema definition string.
								if ("<" == Info.Substring(0, 1))
								{
									name = ParseEleName(Info);
									if (name != null)
									{
										tmpname = CvtUserFldToFld(name);
										rof = new ROField(name, RecID, null, 0);
										ftype = GetFSTreturnType(System.Convert.ToUInt16(rof.ParseFieldType(Info)), tmpname, elem);
										dicFldTypes[tmpname] = ftype;
									}
								}
								else
								{
									// references master, but what we need is here: the field name and
									// the the field type.  Just parse this from the info field.
									string parsename;
									parsename = Info.Substring(13, Info.Length - 13);
									ftype = GetFSTreturnType(System.Convert.ToUInt16(Info.Substring(9, 3)), parsename, elem);
									dicFldTypes[parsename] = ftype;
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error Getting Field Type");
					return 0;
				}
			}
			ftype = ((ushort)dicFldTypes[Fld]);
			return ftype;
		}
		// For the given element's table, get all of the RO fields defined in this table.
		public override ArrayList RODB_GetFields(VlnXmlElement elem, uint rtype)
		{
			string table = elem.GetAttribute("Table");
			if (!FieldDefinitions.ContainsKey(table))
			{
				FieldDefinitions.Add(table, RODB_GetFieldsFromDB(elem));
			}
			return FieldDefinitions[table];
		}
		private Dictionary _FieldDefinitions = null;
		public Dictionary FieldDefinitions
		{
			get
			{
				if (_FieldDefinitions == null) _FieldDefinitions = new Dictionary();
				return _FieldDefinitions;
			}
			set { _FieldDefinitions = value; }
		}
		public ArrayList RODB_GetFieldsFromDB(VlnXmlElement elem)
		{
			ArrayList retlist = new ArrayList();
			string Info;
			string RecID;
			string name;
			uint ftype;
			ROField rof;
			ShowCount.GetFields++;
			try
			{
				if (OnlyConnectOnce)
				{
					SqlConnection cn = ROAPP_SqlConnection;
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getRecidInfoByRectype";
					command.Parameters.AddWithValue("@ROTable", elem.GetAttribute("Table"));
					command.Parameters.AddWithValue("@RecType", 2);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						while (reader.Read())
						{
							ShowCount.RoFields++;
							RecID = reader.GetString(0);
							MyRecID = RecID;
							Info = reader.GetString(1);
							// it's defined in the local table if the first character is "<" which starts
							// the schema definition string.
							if ("<" == Info.Substring(0, 1))
							{
								name = ParseEleName(Info);
								if (name != null)
								{
									// what type of schema element?
									rof = new ROField(name, RecID, null, 0);
									ftype = rof.ParseFieldType(Info);
									rof.SetFieldType(ftype);
									retlist.Add((object)rof);
								}
							}
							else
							{
								// the recid points to a field definition (schema) in the master - use it to get 
								// the schema element from the master and copy it.
								string parsename;
								string masterrecid;
								string strftype;
								masterrecid = Info.Substring(0, 8);
								strftype = Info.Substring(9, 3);
								ftype = System.Convert.ToUInt32(Info.Substring(9, 3));
								parsename = CvtFldToUserFld(Info.Substring(13, Info.Length - 13));
								// if we have a choice element, remove the last character (it was added to the
								// field names in order to allow for unique fields names for the choice items.
								if (ftype == (uint)FieldTypes.Combination)
								{
									string tparsename = parsename.Remove((parsename.Length) - 1, 1);  // remove the choice string and the space
									parsename = tparsename;
								}
								rof = new ROField(parsename, RecID, masterrecid, ftype);
								retlist.Add((object)rof);
							}
						}
					}
				}
				else
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						ShowCount.RoFields++;
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getRecidInfoByRectype";
						command.Parameters.AddWithValue("@ROTable", elem.GetAttribute("Table"));
						command.Parameters.AddWithValue("@RecType", 2);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							while (reader.Read())
							{
								RecID = reader.GetString(0);
								MyRecID = RecID;
								Info = reader.GetString(1);
								// it's defined in the local table if the first character is "<" which starts
								// the schema definition string.
								if ("<" == Info.Substring(0, 1))
								{
									name = ParseEleName(Info);
									if (name != null)
									{
										// what type of schema element?
										rof = new ROField(name, RecID, null, 0);
										ftype = rof.ParseFieldType(Info);
										rof.SetFieldType(ftype);
										retlist.Add((object)rof);
									}
								}
								else
								{
									// the recid points to a field definition (schema) in the master - use it to get 
									// the schema element from the master and copy it.
									string parsename;
									string masterrecid;
									string strftype;
									masterrecid = Info.Substring(0, 8);
									strftype = Info.Substring(9, 3);
									ftype = System.Convert.ToUInt32(Info.Substring(9, 3));
									parsename = CvtFldToUserFld(Info.Substring(13, Info.Length - 13));
									// if we have a choice element, remove the last character (it was added to the
									// field names in order to allow for unique fields names for the choice items.
									if (ftype == (uint)FieldTypes.Combination)
									{
										string tparsename = parsename.Remove((parsename.Length) - 1, 1);  // remove the choice string and the space
										parsename = tparsename;
									}
									rof = new ROField(parsename, RecID, masterrecid, ftype);
									retlist.Add((object)rof);
								}
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Fields");
				return null;
			}
			return retlist;
		}
		public override string RODB_GetSchemaPiece(string Recid, string table)
		{
			string Info;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getInfoByRecid";
					command.Parameters.AddWithValue("@ROTable", table);
					command.Parameters.AddWithValue("@RecID", Recid);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (DBE.Read())
							Info = reader.GetString(0);
						else
							Info = null;
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Schema Piece");
				return null;
			}
			return Info == null ? null : Info.Replace("'", "\'");
		}
		public override bool RODB_NewSchemaPiece(string recid, string parentid, string table, string schpiece, uint rtype)
		{
			bool success = true;
			string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "insertAllRectypes";
					command.Parameters.AddWithValue("@ROTable", table);
					command.Parameters.AddWithValue("@RecID", recid);
					command.Parameters.AddWithValue("@RecType", 2);
					command.Parameters.AddWithValue("@ParentID", parentid);
					command.Parameters.AddWithValue("@AccPageID", "");		// B2020-003: set accpageid to correct value
					command.Parameters.AddWithValue("@Info", schpiece.Replace("\'", "'"));
					command.Parameters.AddWithValue("@ModDateTime", dt);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error on New Schema Piece");
				success = false;
			}
			return success;
		}
		public override bool RODB_WriteSchemaPiece(string Recid, string table, string schpiece)
		{
			bool success = true;
			string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "updateInfoByRecid";
					command.Parameters.AddWithValue("@ROTable", table);
					command.Parameters.AddWithValue("@Info", schpiece.Replace("\'", "'"));
					command.Parameters.AddWithValue("@ModDateTime", dt);
					command.Parameters.AddWithValue("@RecID", Recid);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						success = true;
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error on Write Schema Piece");
				success = false;
			}
			return success;
		}
		public override bool RODB_ProcessRROFieldChange(VlnXmlElement child, string oldname, string newname, uint editlevel, VlnStatusMessage StatMsgWindow, bool combofield)
		{
			bool success = false;
			string info;
			string tinfo1, tinfo2, tinfo3, tinfo4;
			string dt;
			string onameOpen, nnameOpen, onameClose, nnameClose, onameComma, nnameComma;
			string onameOpenX, nnameOpenX;
			onameOpen = "<" + oldname + ">";
			nnameOpen = "<" + newname + ">";
			onameOpenX = "<" + oldname + ">";
			nnameOpenX = "<" + newname + ">";
			onameComma = "<" + oldname + ",";    // if format in attribute
			nnameComma = "<" + newname + ",";
			onameClose = "" + oldname + ">";
			nnameClose = "" + newname + ">";
			dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now);
			// first do the string replace for the group node. This will change any
			// attributes which may have changed.
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getInfoByRecid";
					command.Parameters.AddWithValue("@ROTable", child.GetAttribute("Table"));
					command.Parameters.AddWithValue("@RecID", child.GetAttribute("RecID"));
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
							info = reader.GetString(0);
						else
							info = null;
					}
				}
				tinfo1 = info.Replace(onameOpen, nnameOpen);
				tinfo2 = tinfo1.Replace(onameClose, nnameClose);
				tinfo3 = tinfo2.Replace(onameComma, nnameComma);
				tinfo4 = tinfo3.Replace(onameOpenX, nnameOpenX);
				if (tinfo4 != info)
				{
					StatMsgWindow.StatusMessage = child.GetAttribute("MenuTitle");
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "updateInfoByRecid";
						command.Parameters.AddWithValue("@ROTable", child.GetAttribute("Table"));
						command.Parameters.AddWithValue("@Info", tinfo4);
						command.Parameters.AddWithValue("@ModDateTime", dt);
						command.Parameters.AddWithValue("@RecID", child.GetAttribute("RecID"));
						using (SqlDataReader reader = command.ExecuteReader())
						{
							success = true;
						}
					}
					if (child.Name == "vlnGroup" && child.ParentNode.Name != "RO_Root")
					{
						XmlNodeList nodeList;
						nodeList = child.SelectNodes(oldname);
						foreach (XmlNode nod in nodeList)
						{
							XmlNode newnode = ROXml.CreateNode(XmlNodeType.Element, newname, "");
							newnode.InnerXml = nod.InnerXml;
							child.ReplaceChild(newnode, nod);
						}
					}
					// if at top, replace strings in attributes for xml node. Only needs done 
					// for top node because remainder are done in xml string replace for entire
					// node and written to database, but XML tree is deleted from memory and
					// reread in as needed.
					if (child.ParentNode.Name == "RO_Root")
					{
						string tmp, tmp1;
						tmp = child.GetAttribute("RetVal");
						if (tmp != null)
						{
							tmp1 = tmp.Replace(onameOpen, nnameOpen);
							child.SetAttribute("RetVal", tmp1);
						}
						tmp = child.GetAttribute("MenuItem");
						if (tmp != null)
						{
							string conameOpen = "<" + oldname + ",";
							string cnnameOpen = "<" + newname + ",";
							tmp1 = tmp.Replace(onameOpen, nnameOpen);
							string tmp2 = tmp1.Replace(conameOpen, cnnameOpen);
							child.SetAttribute("MenuItem", tmp2);
						}
						tmp = child.GetAttribute("AccPageID");
						if (tmp != null)
						{
							tmp1 = tmp.Replace(onameOpen, nnameOpen);
							child.SetAttribute("AccPageID", tmp1);
						}
						tmp = child.GetAttribute("GroupMenuItem");
						if (tmp != null)
						{
							tmp1 = tmp.Replace(onameOpen, nnameOpen);
							child.SetAttribute("GroupMenuItem", tmp1);
						}
					}
				}
				// if this was an RO field definition change, need to go through the XML for
				// the ROs too.
				if (editlevel == (int)RecordType.Schema)
				{
					XmlNode chldnode = (XmlNode)child.FirstChild;
					VlnXmlElement echild;
					while (chldnode != null)
					{
						if (chldnode is VlnXmlElement)
						{
							echild = (VlnXmlElement)chldnode;
							if (echild.Name != "vlnGroup")
							{
								// If this is a group definition subtree it will only have one
								// child, which is the text definition for the subgroup. Don't
								// include these in the tree.
								int levelCnt = chldnode.ChildNodes.Count;
								string TheMenuTitle = echild.GetAttribute("MenuTitle");
								if ((levelCnt >= 1) && !TheMenuTitle.Equals(""))
								{
									// read record, do string replace in info & write record. Also, replace
									// the xml node with a node with the new name.
									using (SqlConnection cn = ROAPP_SqlConnection)
									{
										SqlCommand command = new SqlCommand();
										command.Connection = cn;
										command.CommandType = CommandType.StoredProcedure;
										command.CommandText = "getInfoByRecid";
										command.Parameters.AddWithValue("@ROTable", echild.GetAttribute("Table"));
										command.Parameters.AddWithValue("@RecID", echild.GetAttribute("RecID"));
										using (SqlDataReader reader = command.ExecuteReader())
										{
											if (reader.Read())
												info = reader.GetString(0);
											else
												info = null;
										}
									}
									if (combofield == false)
									{
										tinfo1 = info.Replace(onameOpen, nnameOpen);
										tinfo1 = DoReplaceParentChildField(tinfo1, onameOpen, nnameOpen); //C2021-0226 also update Parent/Child fields
										tinfo2 = tinfo1.Replace(onameClose, nnameClose);
										tinfo2 = DoReplaceParentChildField(tinfo2, onameClose, nnameClose); //C2021-0226 also update Parent/Child fields
										XmlNodeList nodeList;
										nodeList = echild.SelectNodes(oldname);
										foreach (XmlNode nod in nodeList)
										{
											XmlNode newnode = ROXml.CreateNode(XmlNodeType.Element, newname, "");
											newnode.InnerXml = nod.InnerXml;
											echild.ReplaceChild(newnode, nod);
										}
									}
									else  // need a,b,c,d yikes
									{
										string nma, nmb, nmc, nmd;
										nma = DoCmbFieldReplace(info, onameOpen, nnameOpen, "a");
										nmb = DoCmbFieldReplace(nma, onameOpen, nnameOpen, "b");
										nmc = DoCmbFieldReplace(nmb, onameOpen, nnameOpen, "c");
										nmd = DoCmbFieldReplace(nmc, onameOpen, nnameOpen, "d");
										nma = DoCmbFieldReplace(nmd, onameClose, nnameClose, "a");
										nmb = DoCmbFieldReplace(nma, onameClose, nnameClose, "b");
										nmc = DoCmbFieldReplace(nmb, onameClose, nnameClose, "c");
										tinfo2 = DoCmbFieldReplace(nmc, onameClose, nnameClose, "d");
										// replace the combo nodes with the new name, do a-d in case there are more than
										// one value field stored for this combo type
										for (char tmplet = 'a'; tmplet < 'e'; tmplet++)
										{
											XmlNodeList nodeList;
											nodeList = echild.SelectNodes(oldname + tmplet.ToString());
											foreach (XmlNode nod in nodeList)
											{
												XmlNode newnode = ROXml.CreateNode(XmlNodeType.Element, newname + tmplet, "");
												newnode.InnerXml = nod.InnerXml;
												echild.ReplaceChild(newnode, nod);
											}
										}
									}
									StatMsgWindow.StatusMessage = echild.GetAttribute("MenuTitle");
									using (SqlConnection cn = ROAPP_SqlConnection)
									{
										SqlCommand command = new SqlCommand();
										command.Connection = cn;
										command.CommandType = CommandType.StoredProcedure;
										command.CommandText = "updateInfoByRecid";
										command.Parameters.AddWithValue("@ROTable", echild.GetAttribute("Table"));
										command.Parameters.AddWithValue("@Info", tinfo2);
										command.Parameters.AddWithValue("@ModDateTime", dt);
										command.Parameters.AddWithValue("@RecID", echild.GetAttribute("RecID"));
										using (SqlDataReader reader = command.ExecuteReader())
										{
											success = true;
										}
									}
								}
							}
						}
						chldnode = chldnode.NextSibling;
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Processing RRO Field Change");
				success = false;
			}
			return success;
		}
		public override bool RODB_UpdateFieldRecord(ROField myrof, VlnXmlElement myelem, string strschema,
			string oldname, string newname, uint editlevel, bool combofield)
		{
			bool success = false;
			// If this is a generic definition that was modified, i.e. from ROMaster, ask
			// the user whether to update the generic field definition or make it local.
			if (myrof.GetMasterRecID != null)
			{
				// if field is defined in the ROMaster, and we change the field name - 
				// first check if it is using one of the names used to define new
				// setpoint or graphics databases. If so, make it a local change only!
				bool changegeneric = true;
				if (oldname != newname)
				{
					// if this is one of the standard names - don't let them save it.
					bool isused = RODB_CheckForStandardName(oldname);
					if (isused == true)
					{
						MessageBox.Show("The field name is used to define new Setpoints or Graphics Database.\n The update will be for this local (database) group only.", "Field name modification.");
						changegeneric = false;
					}
				}
				// if standard name, from above check, don't ask user if it is generic
				// or not. 
				if (changegeneric != false)
				{
					System.Windows.Forms.DialogResult result = MessageBox.Show("Update Generic definition?", "Referenced Object Definition",
						MessageBoxButtons.YesNo, MessageBoxIcon.Question);
					if (result == DialogResult.No) changegeneric = false;
				}
				if (changegeneric == true)
				{
					success = RODB_WriteSchemaPiece(myrof.GetMasterRecID, "ROMaster", strschema);
					if (success != true) return false;
					// if change in fieldname, need to go through all of the databases (tables) to
					// adjust for it.
					if (oldname != newname)
					{
						VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of RO Field Change");
						// get root xml element
						// loop through each subgroup under root.
						VlnXmlElement topgroup;
						topgroup = (VlnXmlElement)ROXml.FirstChild.FirstChild;
						string sqlstr, recid, tbl;
						while (topgroup != null)
						{
							tbl = topgroup.GetAttribute("Table");
							// get the recid of this field definition from the current table.
							try
							{
								using (SqlConnection cn = ROAPP_SqlConnection)
								{
									SqlCommand command = new SqlCommand();
									command.Connection = cn;
									command.CommandType = CommandType.StoredProcedure;
									command.CommandText = "getRecidByRectypeInfo";
									command.Parameters.AddWithValue("@ROTable", tbl);
									command.Parameters.AddWithValue("@RecType", 2);
									command.Parameters.AddWithValue("@Info", myrof.GetMasterRecID + "%");
									using (SqlDataReader reader = command.ExecuteReader())
									{
										if (reader.Read())
										{
											recid = reader.GetString(0);
										}
										else
											recid = null;
									}
								}
							}
							catch (Exception ex)
							{
								MessageBox.Show(ex.Message, "Error Getting Group Schema");
								return false;
							}
							if (recid != null)
							{
								// update the local field definition record
								StringBuilder strbld = new StringBuilder();
								strbld.Append(myrof.GetMasterRecID);
								strbld.Append(" ");
								string strtype = myrof.GetFieldType.ToString();
								strbld.Append(strtype.PadLeft(3, '0'));
								strbld.Append(" ");
								strbld.Append(myrof.MakeFieldName(myrof.GetFieldname));
								success = RODB_WriteSchemaPiece(recid, topgroup.GetAttribute("Table"), strbld.ToString());
								topgroup.SetAttribute("TreeNotData", "True");
								// Update subelements too.
								if (success == true)
									success = RODB_UpdateFieldNames(topgroup, recid, oldname, newname, editlevel, false, StatMsgWindow, combofield);
								topgroup = (VlnXmlElement)topgroup.NextSibling;
							}
						}
						StatMsgWindow.Dispose();
					}
				}
				else  // change local
				{
					// modify the current to have the definition local.
					success = RODB_WriteSchemaPiece(myrof.GetRecID, myelem.GetAttribute("Table"), strschema);
					// get top group node in tree. and from there process fields if
					// there was a change in fieldname text
					if (success == true && oldname != newname)
					{
						VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of RO Field Change");
						// get top node - the one immediately below the RO_Root.
						VlnXmlElement parent, startEle;
						parent = myelem;
						startEle = myelem;
						while (parent.Name != "RO_Root")
						{
							startEle = parent;
							parent = (VlnXmlElement)parent.ParentNode;
						}
						startEle.SetAttribute("TreeNotData", "True");
						success = RODB_UpdateFieldNames(startEle, myrof.GetRecID, oldname, newname, editlevel, false, StatMsgWindow, combofield);
						StatMsgWindow.Dispose();
					}
				}
			}
			else
			{
				success = RODB_WriteSchemaPiece(myrof.GetRecID, myelem.GetAttribute("Table"), strschema);
				// changed the fieldname, change xml text.
				if (success == true && oldname != newname)
				{
					VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of RO Field Change");
					// get top node - the one immediately below the RO_Root.
					VlnXmlElement parent, startEle;
					parent = myelem;
					startEle = myelem;
					while (parent.Name != "RO_Root")
					{
						startEle = parent;
						parent = (VlnXmlElement)parent.ParentNode;
					}
					startEle.SetAttribute("TreeNotData", "True");
					success = RODB_UpdateFieldNames(startEle, myrof.GetRecID, oldname, newname, editlevel, false, StatMsgWindow, combofield);
					StatMsgWindow.Dispose();
				}
			}
			return success;
		}
		public override XmlSchema RODB_GetGroupSchema(VlnXmlElement elem)
		{
			XmlSchema myschema;
			VlnXmlElement parent;
			string entireschema;
			// if at top of tree, use this group schema information (fields in use). but if not,
			// the parent node defines the schema for the subgroups.
			if (elem.ParentNode.Name == "RO_Root")
				parent = elem;
			else
				parent = elem;
			while (parent != null)
			{
				if (parent.HasAttribute("GroupFieldsInUse") == true) break;
				// if we've looped to the top, just set the parent to null. (B2004-015)
				if (parent.ParentNode is VlnXmlElement)
					parent = (VlnXmlElement)parent.ParentNode;
				else
					parent = null;
			}
			if (parent == null) return null;
			// The group schema never gets saved as an attribute. it's always read in, because
			// it's not used as often as the ro schema. So for the Group Schema, we're looking
			// for 'GroupFieldsInUse'.
			// otherwise, read in schema definitions for those 'FieldsInUse'.
			entireschema = null;
			string GroupFieldsInUse = parent.GetAttribute("GroupFieldsInUse");
			int strpos = 0;
			string Info = null;
			// For each item in use, get its xmlschema text. If unique to this group, the definition
			// exists local to this table. Otherwise, go back to the master.
			string recid;
			recid = GroupFieldsInUse.Substring(0, 8);
			while (recid != null)
			{
				try
				{
					using (SqlConnection cn = ROAPP_SqlConnection)
					{
						SqlCommand command = new SqlCommand();
						command.Connection = cn;
						command.CommandType = CommandType.StoredProcedure;
						command.CommandText = "getInfoByRecid";
						command.Parameters.AddWithValue("@ROTable", elem.GetAttribute("Table"));
						command.Parameters.AddWithValue("@RecID", recid);
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								Info = reader.GetString(0);
							}
						}
						if (Info != null)
						{
							// if the info string has "<", i.e. an opening XML schema bracket, use
							// this text as the field definition, otherwise, go to the master to get it
							if ("<" == Info.Substring(0, 1))
								entireschema = entireschema + Info;		// use this field definition, else get from the master.
							else
							{
								string recidpart = Info.Substring(0, 8);
								string localfieldname = Info.Substring(13, Info.Length - 13);
								SqlCommand command1 = new SqlCommand();
								command1.Connection = cn;
								command1.CommandType = CommandType.StoredProcedure;
								command1.CommandText = "getInfoByRecid";
								command1.Parameters.AddWithValue("@ROTable", "RoMaster");
								command1.Parameters.AddWithValue("@RecID", recidpart);
								using (SqlDataReader reader1 = command1.ExecuteReader())
								{
									if (reader1.Read())
									{
										Info = reader1.GetString(0);
										entireschema = entireschema + Info;
									}
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message, "Error Getting Group Schema");
					return null;
				}
				strpos = strpos + 9;
				if (strpos > GroupFieldsInUse.Length)
					recid = null;
				else
					recid = GroupFieldsInUse.Substring(strpos, 8);
			}
			entireschema = schemastart + entireschema + schemaend;
			XmlTextReader schemareader = new XmlTextReader(entireschema, XmlNodeType.Element, null);
			try
			{
				myschema = XmlSchema.Read(schemareader, null);
				return myschema;
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Schema Error");
				return null;
			}
		}
		public override XmlSchema RODB_GetSchema(VlnXmlElement elem)
		{
			XmlSchema myschema;
			VlnXmlElement parent;
			string entireschema;
			XmlNode nparent;
			nparent = elem;
			if (nparent is VlnXmlDocument)
			{
				// at top already. use this one.
				parent = elem;
			}
			else
			{
				parent = (VlnXmlElement)nparent;
				while (parent != null)
				{
					if (parent.HasAttribute("Schema") == true) break;
					if (parent.HasAttribute("FieldsInUse") == true) break;
					parent = (VlnXmlElement)parent.ParentNode;
				}
			}
			// if the schema has already been read in, just use it from the schema attribute,
			// otherwise, read in schema definitions for those 'FieldsInUse'.
			if (parent.HasAttribute("Schema"))
			{
				entireschema = parent.GetAttribute("Schema");
			}
			else
			{
				entireschema = null;
				string FieldsInUse = parent.GetAttribute("FieldsInUse");
				int strpos = 0;
				string Info = null;
				// For each item in use, get its xmlschema text. If unique to this group, the definition
				// exists local to this table. Otherwise, go back to the master.
				string recid;
				recid = FieldsInUse.Substring(0, 8);
				while (recid != null)
				{
					try
					{
						using (SqlConnection cn = ROAPP_SqlConnection)
						{
							SqlCommand command = new SqlCommand();
							command.Connection = cn;
							command.CommandType = CommandType.StoredProcedure;
							command.CommandText = "getInfoByRecid";
							command.Parameters.AddWithValue("@ROTable", elem.GetAttribute("Table"));
							command.Parameters.AddWithValue("@RecID", recid);
							using (SqlDataReader reader = command.ExecuteReader())
							{
								if (reader.Read())
								{
									Info = reader.GetString(0);
								}
							}
							if (Info != null)
							{
								// if the info string has "<", i.e. an opening XML schema bracket, use
								// this text as the field definition, otherwise, go to the master to get
								// it.
								if ("<" == Info.Substring(0, 1))
								{
									// use this field definition, else get from the master.
									entireschema = entireschema + Info;
								}
								else
								{
									string recidpart = Info.Substring(0, 8);
									string localfieldname = Info.Substring(13, Info.Length - 13);
									SqlCommand command1 = new SqlCommand();
									command1.Connection = cn;
									command1.CommandType = CommandType.StoredProcedure;
									command1.CommandText = "getInfoByRecid";
									command1.Parameters.AddWithValue("@ROTable", "RoMaster");
									command1.Parameters.AddWithValue("@RecID", recidpart);
									using (SqlDataReader reader1 = command1.ExecuteReader())
									{
										if (reader1.Read())
										{
											// check if field name is same, if not, use local name.
											string goodname;
											Info = reader1.GetString(0);
											int indx1 = Info.IndexOf("\"");
											int indx2 = Info.IndexOf("\"", indx1 + 1);
											string masterfieldname = Info.Substring(indx1 + 1, indx2 - indx1 - 1);
											if (localfieldname != masterfieldname)
												goodname = Info.Replace(masterfieldname, localfieldname);
											else
												goodname = Info;
											entireschema = entireschema + goodname;
										}
									}
								}
								strpos = strpos + 9;
								if (strpos > FieldsInUse.Length)
									recid = null;
								else
									recid = FieldsInUse.Substring(strpos, 8);
							}
						}
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.Message, "Error Getting Schema");
						return null;
					}
				}
				// C2021-026 read in schema for the "ApplicabilityEnabled" attribute
				entireschema = schemastart + entireschema + schemaend;
			}
			XmlTextReader schemareader = new XmlTextReader(entireschema, XmlNodeType.Element, null);
			try
			{
				myschema = XmlSchema.Read(schemareader, null);
				parent.SetAttribute("Schema", entireschema);
				return myschema;
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Schema Read Error");
				return null;
			}
		}
		public override int RODB_GetNumberOfROValueRecords(string tablename)
		{
			int ROCnt = 0;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getCountRectypeByRectype";
					command.Parameters.AddWithValue("@ROTable", tablename);
					command.Parameters.AddWithValue("@RecType", (int)RecordType.RRO);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
						{
							ROCnt = reader.GetInt32(0);
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Number of RO Value Records");
				return 0;
			}
			return ROCnt;
		}
		public override int RODB_GetNumberOfGroupRecords(string tablename)
		{
			int GrpCnt = 0;
			try
			{
				using (SqlConnection cn = ROAPP_SqlConnection)
				{
					SqlCommand command = new SqlCommand();
					command.Connection = cn;
					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = "getCountRectypeByRectype";
					command.Parameters.AddWithValue("@ROTable", tablename);
					command.Parameters.AddWithValue("@RecType", (int)RecordType.Group);
					using (SqlDataReader reader = command.ExecuteReader())
					{
						if (reader.Read())
						{
							GrpCnt = reader.GetInt32(0);
						}
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Getting Number of Group Value Records");
				return 0;
			}
			return GrpCnt;
		}
		#endregion
		public static bool TestConnect(string constring)
		{
			bool success = false;
			using (SqlConnection connection = new SqlConnection(constring))
			{
				try
				{
					connection.Open();
					if (connection.State == ConnectionState.Open)
					{
						// now see if there is an roall table
						SqlCommand command = new SqlCommand();
						command.Connection = connection;
						command.CommandType = CommandType.Text;
						command.CommandText = "SELECT count(*) FROM roall";
						using (SqlDataReader reader = command.ExecuteReader())
						{
							if (reader.Read())
							{
								int count = reader.GetInt32(0);
								if (count > 0) success = true;
							}
						}
					}
				}
				catch (Exception ex)
				{
				}
			}
			return success;
		}
		public static string GetSqlDbConnectString(string origConString)
		{
			string value = origConString;
			InputBox inpbox = new InputBox(origConString);
			DialogResult dr = inpbox.ShowDialog();
			if (dr == DialogResult.OK)
			{
				// test & if successful, save and return:
				if (TestConnect(inpbox.ResultConnectString)) return inpbox.ResultConnectString;
			}
			return null;
		}
		public override bool RODB_WriteSqlConnectToAccess(string newConectStr)
		{
			return false;
		}
	}
}