/*********************************************************************************************
 * Copyright 2002 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: VlnXml.cs $     $Revision: 23 $
 * $Author: Jsj $   $Date: 6/20/05 1:29p $
 *
 * $History: VlnXml.cs $
 * 
 * *****************  Version 23  *****************
 * User: Jsj          Date: 6/20/05    Time: 1:29p
 * Updated in $/LibSource/RODBInterface
 * added status message while reading ROs for Complete RO Report.
 * 
 * *****************  Version 22  *****************
 * User: Jsj          Date: 6/08/05    Time: 4:32p
 * Updated in $/LibSource/RODBInterface
 * Complete RO Report Bug fix  B2001-001
 * 
 * *****************  Version 21  *****************
 * User: Jsj          Date: 9/27/04    Time: 11:02a
 * Updated in $/LibSource/RODBInterface
 * upped release number
 * 
 * *****************  Version 20  *****************
 * User: Kathy        Date: 7/15/04    Time: 11:11a
 * Updated in $/LibSource/RODBInterface
 * Fix B2004-017
 * 
 * *****************  Version 19  *****************
 * User: Kathy        Date: 5/06/04    Time: 9:52a
 * Updated in $/LibSource/RODBInterface
 * B2004-012 fix: skip process of param display data for RO Cmp Rpt
 * 
 * *****************  Version 18  *****************
 * User: Jsj          Date: 4/08/04    Time: 9:43a
 * Updated in $/LibSource/RODBInterface
 * Added and commented out calls to VlnProfiler
 * 
 * *****************  Version 17  *****************
 * User: Jsj          Date: 1/21/04    Time: 12:10p
 * Updated in $/LibSource/RODBInterface
 * added function to return current selected field type of multi type
 * field
 * 
 * *****************  Version 16  *****************
 * User: Kathy        Date: 5/21/03    Time: 12:41p
 * Updated in $/LibSource/RODBInterface
 * B2003-035: process ro's with just one field
 * 
 * *****************  Version 15  *****************
 * User: Jsj          Date: 4/14/03    Time: 2:59p
 * Updated in $/LibSource/RODBInterface
 * changes resulting from speeding up RO FST creation
 * 
 * *****************  Version 14  *****************
 * User: Kathy        Date: 3/11/03    Time: 11:24a
 * Updated in $/LibSource/RODBInterface
 * allow extra text on accpageid template
 * 
 * *****************  Version 13  *****************
 * User: Kathy        Date: 12/17/02   Time: 12:53p
 * Updated in $/LibSource/RODBInterface
 * get image data by node for rofst creation
 * 
 * *****************  Version 12  *****************
 * User: Kathy        Date: 12/10/02   Time: 2:25p
 * Updated in $/LibSource/RODBInterface
 * fieldname special chars & various bugs
 * 
 * *****************  Version 11  *****************
 * User: Kathy        Date: 12/02/02   Time: 8:29a
 * Updated in $/LibSource/RODBInterface
 * fieldname replace chars
 * 
 * *****************  Version 10  *****************
 * User: Jsj          Date: 11/27/02   Time: 12:47p
 * Updated in $/LibSource/RODBInterface
 * modifications for RO.FST file creation
 * 
 * *****************  Version 9  *****************
 * User: Kathy        Date: 11/19/02   Time: 11:32a
 * Updated in $/LibSource/RODBInterface
 * fields within {} are not required
 * 
 * *****************  Version 8  *****************
 * User: Kathy        Date: 11/11/02   Time: 7:14a
 * Updated in $/LibSource/RODBInterface
 * Added complete report methods.
 *********************************************************************************************/
using System;
using System.Xml;
using System.Text;
using System.Collections;
using System.IO;
using System.Windows.Forms;
using ROFields;
using VlnStatus;
//using VlnProfiler; //don't forget to add VlnProfiler to the reference list

namespace RODBInterface
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
 
	public class VlnXmlDocument:XmlDocument 
	{
		FileStream Rptfs;		// for complete report of this document
		BinaryWriter RptBW;
		
		public VlnXmlDocument():base() 
		{
		}

		public override XmlElement CreateElement( string prefix, string localname, string nsURI) 
		{
			VlnXmlElement elem = new VlnXmlElement(prefix, localname, nsURI, this);
			return elem;
		}

		public bool OpenTemp()
		{
			try
			{
				//Rptfs = new FileStream("print.tmp", FileMode.Create);
				Rptfs = new FileStream("print.tmp", FileMode.Append);
				// Create the writer for data.
				RptBW = new BinaryWriter(Rptfs);
			}
			catch (Exception ex) 
			{
				Console.WriteLine("Open Print.Tmp: {0}", ex.Message);
				return false;
			}
			return true;
		}

		public void CloseTemp()
		{
			RptBW.Close();
			Rptfs.Close();
		}

		public void writeInt(short val)
		{
			RptBW.Write(val);
		}

		public void writeText(string str)
		{
			short n = (short) str.Length;
			writeInt(n);
			// write out char by char. (Using rite
			// of entire stirng caused special characters to be
			// written as two bytes - which temp file reading code
			int i;
			for(i =0; i < str.Length; i++)
			{
				byte WrByte;
				WrByte = (byte)str[i];
				RptBW.Write(WrByte);
			}
		}

		public void writeTextValue(string str)
		{
			writeInt(100);
			writeText(str);
		}

		public void writePlot(string str)
		{
			writeInt(101);
			writeText(str);
		}

		public void writeImage(string str)
		{
			writeInt(102);
			writeText(str);
		}

		public void pushHeader(string str)
		{
			writeInt(2);
			writeText(str);
		}

		public void popHeader()
		{
			writeInt(3);
		}

		public void doHeader(ArrayList headers)
		{
			int spaces = -1;
			short cnt = (short) headers.Count;
			writeInt(255);
			writeInt(1);
			writeInt(cnt);
			for (int i=0; i < cnt ;i++)
			{
				writeInt(0);
				if (spaces>0)
				{
					string spc = new String(' ',2*(spaces-1));
					writeText(spc+headers[i].ToString());
				}
				else
				writeText(headers[i].ToString());
				spaces++;
			}
		}

		public void writeRROBegin()
		{
			writeInt(4);
		}

		public void writeRROEnd()
		{
			writeInt(5);
		}
	
		public void writeMultiBegin()
		{
			writeInt(103);
		}

		public void writeMultiEnd()
		{
			writeInt(104);
		}
	}

	public class VlnXmlElement:XmlElement 
	{
		public VlnXmlElement( string prefix, string localname, string nsURI, XmlDocument doc ):base( prefix,localname,nsURI, doc )
		{
		}

		private string CvtUserFldToFld(string fldname)
		{
			if (fldname.Length < 2)
				return fldname;
			// a digit cannot start an xml fieldname, prepend a "__" to it.
			string tmp0;
			if (char.IsDigit(fldname,0))
				tmp0 = "__" + fldname;
			else
				tmp0 = fldname;
			// an xml fieldname cannot have a space, change it to a "__"
			string tmpstr = tmp0.Replace(" ","__");
			int len = tmpstr.Length;
			int cnt = 0;

			// this is also our sequence that tells us the follow 3 digits is the ascii number (base 10)
			// of the character we replaced.
			string OKpunch = "-._"; 

			string outstr = "";
			int decval;

			while (cnt < len)
			{
				char tmpchr = tmpstr[cnt];
				if(!char.IsLetterOrDigit(tmpchr)&& (OKpunch.IndexOf(tmpchr) == -1) )
				{
					decval = tmpchr;
					outstr += OKpunch + decval.ToString("D3");
				}
				else
				{
					outstr += tmpchr.ToString();
				}
				cnt++;
			}
			return outstr;
		}

		private string CvtFldToUserFld(string fldname)
		{
			string tmpstr0;
			if (fldname.Length < 2) return fldname;
			// an xml element name cannot begin with a digit. we had prepended a "__"
			if (fldname.Substring(0,2) == "__" && char.IsDigit(fldname,2))
				tmpstr0 = fldname.Substring(2,fldname.Length-2);
			else
				tmpstr0 = fldname;
			// an xml element name cannot have a space, we converted to a "__"
			string tmpstr = tmpstr0.Replace("__"," ");
			int len = tmpstr.Length;
			int cur = 0;

			// this is also our sequence that tells us the follow 3 digits is the ascii number (base 10)
			// of the character we replaced.
			string OKpunch = "-._"; 

			string outstr = "";
			int decval, indx;
			if (tmpstr.Length <6)
				indx = -1;
			else
				indx=tmpstr.IndexOf(OKpunch,cur);
			string asc_spchar;
			while (indx>=0)
			{
				outstr += tmpstr.Substring(cur,indx-cur);
				asc_spchar = tmpstr.Substring(indx+3,3);
				decval = System.Convert.ToInt16(asc_spchar,10);
				outstr += System.Convert.ToChar(decval).ToString();
				cur = indx+6;
				if (cur+6 > len)
					indx = -1;
				else
					indx = tmpstr.IndexOf(OKpunch,cur);
			}
			if (cur<len) outstr += tmpstr.Substring(cur,len-cur);	

			return outstr;
		}

		// for this (VlnXmlElement), get it's accessory page id based on it's data, using
		// the passed in template (AccPageID template from the xmlelement's attributes).
		// Note that the template represents only 1 field. This was a req/design decision 
		// made during the development based on engineering review.
		public string GetAccPageIDString(string newtmpl)
		{
			string fld;
			// If there aren't properties definied for the group/subgroup - give a message
			// rather than crash (B2004-017)
			if (newtmpl==null)
			{
				MessageBox.Show("Missing Accessory Page Information, check Group definition on Properties.");
				return null;
			}
			VlnXmlElement elm = this;
			int left = newtmpl.IndexOf("<");
			int right= newtmpl.IndexOf(">");
			if (left >= 0 && right > 0) 
				fld = newtmpl.Substring(left+1,right-left-1);
			else
				return null;
			string usefld = CvtUserFldToFld(fld);
			
			XmlNode elmnode = (XmlNode) elm;
			XmlNode nd = elmnode.SelectSingleNode(usefld);
			// if not found it may be a combination, try this.
			if (nd == null)
			{
				string tmp = usefld + "a";
				nd = elmnode.SelectSingleNode(tmp);
			}
			if (nd == null)
			{
				string tmp = usefld + "b";
				nd = elmnode.SelectSingleNode(tmp);
			}
			if (nd == null)
			{
				string tmp = usefld + "c";
				nd = elmnode.SelectSingleNode(tmp);
			}
			if (nd == null)
			{
				string tmp = usefld + "d";
				nd = elmnode.SelectSingleNode(tmp);
			}
			if(nd != null)
			{
				StringBuilder strbld = new StringBuilder();
				if (left > 0) strbld.Append(newtmpl.Substring(0,left));
				strbld.Append(nd.InnerText.ToString());
				if (right+1<newtmpl.Length)
					strbld.Append(newtmpl.Substring(right+1,newtmpl.Length-right-1));
				return strbld.ToString();
			}
			return null;
		}

		// for this (VlnXmlElement), get the accessorypageid template by walking up
		// the tree, starting at this element.
		public string GetAccPageIDTemplate()
		{
			VlnXmlElement parent;
			VlnXmlElement tmp=null;
			
			// get to top node of the ROs & groups to get the accpageID for this tree.
			parent = (VlnXmlElement) this;
			while(parent.Name != "RO_Root")
			{
				tmp = parent;
				parent = (VlnXmlElement) parent.ParentNode;
			}
			if (tmp.HasAttribute("AccPageID") == false) return null;
			else return tmp.GetAttribute("AccPageID");
		}

		// check to see if the input string makes a duplicate menu title.
		public bool IsDuplicateMenuTitle(string menutitle)
		{
			// see if this is a duplicate menu, just check ROs at this level except
			// myself.
			XmlNode parent = this.ParentNode;
			XmlNode chld = parent.FirstChild;
			while (chld != null)
			{
				if (chld is VlnXmlElement)
				{
					VlnXmlElement echld = (VlnXmlElement) chld;
					if (this != echld)
					{
						string chldmenutitle = echld.GetAttribute("MenuTitle");
						if (chldmenutitle != null && chldmenutitle == menutitle)return true;
					}
				}
				chld = chld.NextSibling;
			}
			return false;
		}

		// for this (VlnXmlElement), get the menu title string. Passed in, menuitm, is the
		// template which is used to generate the menu title for this XmlElement. Also
		// passed in is a flag to state whether this is a 'Group' menu title.
		public string GetMenuString(string menuitm, bool isgrp)
		{
			VlnXmlElement elm = this;
			StringBuilder strbld = new StringBuilder();

			// use grpmenuitm, parse for field(s) and generate a string to return
			int left = menuitm.IndexOf("<");
			int right=0;
			int stindx = 0;
			if (left > 0) strbld.Append(menuitm.Substring(0,left));
	
			while (left != -1)
			{
				// get right bracket and then check for valid field & if commas with
				// this, check for integer values.
				right = menuitm.IndexOf(">",stindx);
				string tmpd = menuitm.Substring(left+1,right-left-1);
				// do comma parts now.
				string inusename;
				int comma1 = tmpd.IndexOf(",");
				int frmt1=0, frmt2=0;
				if (comma1 != -1)
				{
					// get any numbers for formatting & check for ints.
					int comma2 = tmpd.IndexOf(",",comma1+1);
					if (comma2 != -1) // two ints.
					{
						if (comma2-comma1 > 1) // first format field - not empty 
						{
							string int1 = tmpd.Substring(comma1+1,comma2-comma1-1);
							frmt1 = System.Convert.ToInt32(int1);
						}
						if (comma2<tmpd.Length)  // second part - not empty
						{
							string int2 = tmpd.Substring(comma2+1,tmpd.Length-comma2-1);
							frmt2 = System.Convert.ToInt32(int2);
						}
					}
					else  // just one format 
					{
						string int1 = tmpd.Substring(comma1,comma2-comma1);
						frmt1 = System.Convert.ToInt32(int1);
					}
					inusename = CvtUserFldToFld(tmpd.Substring(0,comma1));
				}
				else  // no commas, it's just a field name.
					inusename = CvtUserFldToFld(tmpd);

				// get contents of field & append it onto the string builder.
				XmlNode elmnode = (XmlNode) elm;
				XmlNode nd = elmnode.SelectSingleNode(inusename);
				// if not found it may be a combination, try this.
				ushort FieldType = 0;
				string text;
				if (nd == null)
					text = GetComboFieldValueAndType(elmnode,inusename,ref FieldType,true);
				else
					text = nd.InnerText;
				if (!text.Equals(""))
				{
					if (isgrp)
					{
						if (frmt2 != 0)
							strbld.Append(CvtFldToUserFld(text).PadRight(frmt2,' '));
						else
							strbld.Append(CvtFldToUserFld(text));
					}
					else
					{
						if (frmt1 == 0)
						{
							if (frmt2>0)
								strbld.Append(text.PadRight(frmt2,' '));
							else
								strbld.Append(text);
						}
						else if (frmt1 != frmt2)
						{
							int cnt = 0;
//							while (cnt < tmp.Length && tmp[cnt] <= '9' && tmp[cnt] >= '0') cnt++; // new RO logic
//							int padcnt = (cnt>frmt1)?0:frmt1-cnt;
							int padcnt = OldROFSTMenuLogic(text,frmt1,frmt2); // testing!!!!!
							StringBuilder strtmp = new StringBuilder();
							cnt = 0;
							while(cnt<padcnt) 
							{
								strtmp.Append(" ");
								cnt++;
							}
							if ((cnt + text.Length) > frmt2) // longer than the field length?
								strtmp.Append(text.Substring(0,frmt2-cnt));
							else
								strtmp.Append(text);
							string tmpstr = strtmp.ToString();
							strbld.Append(tmpstr.PadRight(frmt2,' '));
						}
						else
							strbld.Append(text.PadLeft(frmt2,' '));
					}
				}
				stindx = right+1;
				left = menuitm.IndexOf("<",left+1);
				if (left != -1 && left > right+1)  // append any plain text.
					strbld.Append(menuitm.Substring(right+1,left-right-1));
			}
			if (right+1<menuitm.Length)
				strbld.Append(menuitm.Substring(right+1,menuitm.Length-right-1));
			return strbld.ToString();
		}


		int OldROFSTMenuLogic(string text,int doff, int wid)
		{
			int siznum = 0;
			int idx =0;
			int sps = 0;
			int newoff = doff;
			int tmpwid;
			char[] str = text.ToCharArray();
			// skip leading blanks
			while (idx < text.Length && str[idx] == ' ') idx++;
			int slen = text.Length - idx;
			while (idx < text.Length && ((str[idx]<='9' && str[idx]>='0') || str[idx] =='-' || str[idx]=='+'))
			{
				siznum++;
				idx++;
			}
			if (siznum == 0)
			{
				newoff= siznum+(wid-slen)/2;
			} 
			else if (((doff-siznum)+slen)> wid)
			{
				newoff = wid-siznum-slen;
			}
			tmpwid = wid;
			while (tmpwid > 0 && siznum < newoff)
			{
				sps++;
				siznum++;
				tmpwid--;
			}
			return sps;
		}


		// for this (VlnXmlElement), get it's menu value template. This template is
		// used to generate the menu titles. Pass in a string 'GroupMenuItem' or
		// 'MenuItem' so that the group level or RRO level menu template is found.
		// This walkds up the tree to find the attribute ('GroupMenuItem' or 'MenuItem')
		// and returns it.
		public string GetMenuValueTemplate(string menuitm)
		{
			VlnXmlElement parent;
			string strmenuitm;
			VlnXmlElement tmp;
			if (menuitm == "GroupMenuItem")
			{		
				// if at top of tree, use this top GroupMenuItem. but if not,
				// the parent node defines the GroupMenuItem for the subgroups.
				if (this.ParentNode.Name == "RO_Root")
				{
					tmp = (VlnXmlElement) this.ParentNode;
					strmenuitm=tmp.GetAttribute(menuitm);
					return(strmenuitm);
				}
				else
					parent = (VlnXmlElement) this.ParentNode;
				while (parent != null) 
				{
					if (parent.HasAttribute(menuitm) == true) break;
					if (parent.Name != "RO_Root")
						parent = (VlnXmlElement) parent.ParentNode;
					else
						parent = null;
				}
				if (parent == null) return null;
				strmenuitm = parent.GetAttribute(menuitm);
				return strmenuitm;
			}

			// Now just do 'MenuItem', which is for ROs
			parent = (VlnXmlElement) this;
			strmenuitm = parent.GetAttribute(menuitm);
			while (parent != null && strmenuitm == "")
			{
				// walk up tree to get parent.
				strmenuitm = parent.GetAttribute(menuitm);
				parent = (VlnXmlElement) parent.ParentNode;
			}
			return strmenuitm;
		}

		// for this (VlnXmlElement), get the Return Value template.
		public string GetReturnValueTemplate()
		{
			VlnXmlElement parent;
			VlnXmlElement tmp=null;
			
			// get to top node of the ROs & groups to get the return for this tree.
			parent = (VlnXmlElement) this;
			tmp = parent;
			while((tmp.HasAttribute("RetVal") == false) && parent.Name != "RO_Root")
			{
				tmp = parent;
				parent = (VlnXmlElement) parent.ParentNode;
			}
			if (tmp.HasAttribute("RetVal") == false) return null;
			else return tmp.GetAttribute("RetVal");
		}

		/*
		 * return true if the field is a pre defined PROC.INI field name
		 */
		private bool IsProcIniDef(string fld)
		{
			bool rtnval = false;
			string tmpstr = fld.ToUpper();

			rtnval = (tmpstr.Equals("U") || 
						tmpstr.Equals("U-") ||
						tmpstr.Equals("P") || 
						tmpstr.Equals("P-") ||
						tmpstr.Equals("S") ||
						tmpstr.Equals("S-"));

			return rtnval;
		}

		/*
		 * This function checks the given field name with the list of fields
		 * that are "in use" for the current group.
		 * If the field name is of one of the pre-defined PROC.INI RO fields
		 * treat that field name as plain text (i.e. NOT in use)
		 */
		private bool IsAInUseField(ArrayList InUseList, string fld)
		{
			bool rtnval = false;
			int i;

			if (IsProcIniDef(fld)) // is this a pre-defined PROC.INI field?
				return rtnval; //return false!

			string tmpstr = fld.Substring(0,1);
			if (tmpstr.Equals("_"))
				tmpstr = fld.Substring(1,fld.Length-1);
			else
				tmpstr = fld;
			tmpstr = CvtFldToUserFld(fld);
			for(i=0; rtnval == false && i<InUseList.Count; i++)
			{
				ROField rof = (ROField) InUseList[i];
				string fldname = rof.GetFieldname;
				if (fldname.Equals(tmpstr))
				{
					rtnval = true; // found
				}
			}

			return rtnval;
		}

		public string GetReturnValue(RODB theDb, string TableName,string RtnValTemplate, ArrayList InUseList, ref ushort ValueType)
		{
			string RtnValStr = "";
			VlnXmlElement elm = this;
			StringBuilder strbld = new StringBuilder();
			
			// Parse the template for the fields and generate a return string
			int left = RtnValTemplate.IndexOf("<"); // look for begining of field
			int right = -1;
			int stindx = 0;
			ushort FldType = 0;

			if (left > 0) strbld.Append(RtnValTemplate.Substring(0,left));

			while (left != -1)
			{
				// get right bracket and then check for valid field & if commas with
				// this, check for integer values.
				right = RtnValTemplate.IndexOf(">",stindx);
				string inusename;
				string substr = RtnValTemplate.Substring(left+1,right-left-1);
				inusename = CvtUserFldToFld(substr);

				// get contents of field & append it onto the string builder.
				XmlNode elmnode = (XmlNode) elm;
				XmlNode nd = elmnode.SelectSingleNode(inusename);
				// Check to see if the found field is a "Field In Use"

				if (IsAInUseField(InUseList,inusename))
				{
					string text="";
					if (nd == null)
					{
						text = GetComboFieldValueAndType(elmnode,inusename,ref FldType,false);
						if (text.Equals(""))
							text = " ";
					}
					else
					{
//						Profiler.Start("Grv7");
						FldType = theDb.RODB_GetFieldType((VlnXmlElement)nd,TableName,inusename);
//						Profiler.End("Grv7");
//						Profiler.Start("Grv8");
						// for an image, get the filename, height, width, otherwise just use
						// data in the node.
						if (FldType == 8) // for the '8' see RODBInterface - GetFSTreturntype
						{
							// get filename, length, width in that order, separated by newlines
							XmlNode tmp = nd.SelectSingleNode("Image_Filename");
							if (tmp != null && tmp.InnerText != null)
								text = tmp.InnerText + "\r\n";
							else
								text = " \r\n";
							tmp = nd.SelectSingleNode("Image_Height");
							if (tmp != null && tmp.InnerText != null)
								text = text + tmp.InnerText + "\r\n";
							else
								text = text + " \r\n";
							tmp = nd.SelectSingleNode("Image_Width");
							if (tmp != null && tmp.InnerText != null)
								text = text + tmp.InnerText + "\r\n";
							else
								text = text + " \r\n";
						}
						else
						{
							text = nd.InnerText;
						}
//						Profiler.End("Grv8");
					}

					ValueType |= FldType;
					strbld.Append(text);
				}
				else
				{
					strbld.Append(RtnValTemplate.Substring(left,right-left+1));			
				}

				stindx = right+1;
				left = RtnValTemplate.IndexOf("<",left+1);
				if (left != -1 && left > right+1)  // append any plain text.
					strbld.Append(RtnValTemplate.Substring(right+1,left-right-1));			
			}

			right++; // move past the right bracket ('>')
			if (right < RtnValTemplate.Length)
				strbld.Append(RtnValTemplate.Substring(right,RtnValTemplate.Length-right));

// Bug fix: B2004-044
// If the ValueType is zero, then it will not appear on the list of ROs in the
// procedure editor.
			if (ValueType == 0)
				ValueType = 1;

			RtnValStr = strbld.ToString();
			return RtnValStr;
		}

		public string GetComboFieldValueAndType(XmlNode CurNode, string Fld, ref ushort FldTyp, bool ForMenu)
		{
			string RtnVal = "";
			XmlNode ChildNode = CurNode.FirstChild;
			FldTyp = 0;

			while (ChildNode != null && FldTyp == 0)
			{
				if (ChildNode is VlnXmlElement)
				{
					string ChildName = ChildNode.Name.ToString();
					if (ChildName.StartsWith(Fld))
					{
						char CmboChar = Convert.ToChar(ChildName.Substring(ChildName.Length-1,1));
						switch (CmboChar)
						{
							case 'a': // Fixed Text
							case 'b': // Multi Line Text
								FldTyp = 1;
								RtnVal = ChildNode.InnerText;
								break;
							case 'c': // Table
								FldTyp = 2;
								if (ForMenu)
									RtnVal = "(Table)";
								else
									RtnVal = ChildNode.InnerText;
								break;
							case 'd': // X/Y Plot
								FldTyp = 4;
								if (ForMenu)
									RtnVal = "(Graph)";
								else
									RtnVal = ChildNode.InnerText;
								break;
						}
					}
				}
					ChildNode = ChildNode.NextSibling;
			}
			return RtnVal;
		}

		// For this (VlnXmlElement), get the list of used fields. Need to find the
		// fields from the accessory page id template, the return value template 
		// and the menu title template or group title template (depends on if a
		// group edit or a RRO edit.
		public ArrayList GetRequiredFields()
		{
			ArrayList fldlist = new ArrayList();
			string accpagetmpl = GetAccPageIDTemplate();
			string retvaltmpl = null;
			string menutmpl = null;
			if (this.Name == "vlnGroup")
			{
				menutmpl = GetMenuValueTemplate("GroupMenuItem");
			}
			else
			{
				menutmpl = GetMenuValueTemplate("MenuItem");
				retvaltmpl = GetReturnValueTemplate();
			}
			// for each string, add its field names to the list.
			// accessory page id's only have a field and only used for ROs.
			if (this.Name != "vlnGroup" && accpagetmpl != null)
			{
				string fld = accpagetmpl.Substring(1,accpagetmpl.Length-2);
				fldlist.Add(fld+"\tAccessory Page Value");
			}
			// menutmpl's may have multiple fields. loop through them.
			int left, right, stindx, comma, rdelim;
			if (menutmpl != null)
			{
				left = menutmpl.IndexOf("<");
				right = 0;
				stindx = 0;
				comma = 0;
				while (left != -1)
				{
					// get right bracket and then check for valid field & if commas with
					// this, check for integer values.
					right = menutmpl.IndexOf(">",stindx);
					comma = menutmpl.IndexOf(",",stindx);
					rdelim = (comma>-1 && comma>left && comma<right)?comma:right;
					string substring = menutmpl.Substring(left+1,rdelim-left-1);
					string tmpds = CvtUserFldToFld(substring);
					substring = tmpds + "\tMenu Value";
					if(fldlist.Contains((Object)substring) == false)fldlist.Add(substring);
					left = menutmpl.IndexOf("<",right+1);
					stindx = right+1;
				}
			}
			// now add return value's if not a group level.
			if (this.Name != "vlnGroup" && retvaltmpl != null)
			{
				left = retvaltmpl.IndexOf('<');
				right = 0;
				stindx = 0;
				int leftbrace, rightbrace, stindxbrace=0;
				bool isreq;
				while (left != -1)
				{
					isreq = true;
					// get right bracket and then check for valid field & if commas with
					// this, check for integer values. Also, if it's within {}, it isn't
					// required.
					right = retvaltmpl.IndexOf('>',stindx);
					// first see if it is within it's own {}
					if (stindxbrace<retvaltmpl.Length)
						leftbrace = retvaltmpl.IndexOf('{',stindxbrace);
					else
						leftbrace=-1;
					if (leftbrace > -1) 
					{
						rightbrace = retvaltmpl.IndexOf('}',stindxbrace);
						if ((left>leftbrace&&left<rightbrace)&&(right>leftbrace&&right<rightbrace)) // don't do
							isreq=false;
						stindxbrace=rightbrace+1;
					}
					if (isreq==true)
					{
						string substring = retvaltmpl.Substring(left+1,right-left-1);
						string tmpds = CvtUserFldToFld(substring);
						substring = tmpds + "\tReturn Value";
						if(fldlist.Contains((Object)substring) == false)fldlist.Add(substring);
					}
					left = retvaltmpl.IndexOf("<",right+1);
					stindx = right+1;
				}
			}
			return fldlist;
		}

		/// ******** RO Complete Report methods.
		/// 
		/// These methods are called by CmpRpt. They 'show', i.e. write to output
		/// file, print.tmp, the fields/data defining the group and Ro.
		/// 

		// ShowGroupDetail sends to print.tmp the data for a Group level.
		private bool ShowGroupDetail(RODB myrodb)
		{
			ROField rof;
			XmlNode nd;
			VlnXmlDocument xmldoc = (VlnXmlDocument) this.OwnerDocument;
			string nm;

			// First get the fields used for this Group.
			string tmp=null;  // needed for inuse call
			ArrayList AvailList = myrodb.RODB_GetFields(this, 0);
			ArrayList InUseList = myrodb.RODB_GetFieldsInUse(this, AvailList, "GroupFieldsInUse", ref tmp, true);
			
			// For each field, print the header (field name) & then print data if it exists.
			if (InUseList != null) 
			{
				xmldoc.writeRROBegin();
				xmldoc.writeInt(7);
				xmldoc.writeText(" ");

				for (int i=0; i< InUseList.Count; i++)
				{
					rof = (ROField) InUseList[i];
					if (rof.GetFieldname != null)
					{
						string nm0 = rof.GetFieldname;
						nm = rof.MakeFieldName(nm0);
						xmldoc.pushHeader(nm0);
						// see if it has data in the ro.
						nd = this.SelectSingleNode(nm);
						// if not found with just the string, search the tree.
						if (nd==null)nd = this.SelectSingleNode("*/"+nm);
						if (nd == null) // not found, write out a '0'.
							xmldoc.writeInt(0);
						else
						{
							VlnXmlElement end;
							end = (VlnXmlElement) nd;
							xmldoc.writeTextValue(end.InnerText);
						}
						xmldoc.popHeader();
					}
				}
				xmldoc.writeRROEnd();
			}
			return true;
		}
		
		public XmlNode GetCurrentOfMultiFields(VlnXmlElement CurElement, string FldName)
		{
			XmlNode nd = null;
			XmlNode grpnd = (XmlNode) CurElement.FirstChild;
			string CurFieldName = "";
			while (grpnd != null)
			{
				if (grpnd.Name.Substring(0,grpnd.Name.Length-1) == FldName.Substring(0,FldName.Length))
				{
					CurFieldName = grpnd.Name;
					break;
				}
				grpnd = grpnd.NextSibling;
			}

			if (!CurFieldName.Equals(""))
				nd = CurElement.SelectSingleNode(CurFieldName);

			return nd;
		}

		// ShowRRO writes data for the input RO.
		private bool ShowRRO(RODB myrodb, ArrayList headers)
		{
			VlnXmlDocument xmldoc = (VlnXmlDocument) this.OwnerDocument;
			ROField rof;
			XmlNode nd;
			bool doingplot;
			string nm;

			// Get used fields for this RO.
			string tmp=null;  // needed for inuse call
			ArrayList AvailList = myrodb.RODB_GetFields(this, 0);
			ArrayList InUseList = myrodb.RODB_GetFieldsInUse(this, AvailList, "FieldsInUse", ref tmp, true);
			
			// put out the stored headers, i.e. the group titles up the tree.
			xmldoc.doHeader(headers);

			// Put out fields and their associated data.
			if (InUseList != null) 
			{
				xmldoc.writeRROBegin();
				xmldoc.writeInt(7);
				xmldoc.writeText(this.GetAttribute("AccPageID").Trim());
				// add items to the InUseList box.
				for (int i=0; i< InUseList.Count; i++)
				{
					doingplot=false;
					rof = (ROField) InUseList[i];
					if (rof.GetFieldname != null)
					{
						string nm0 = rof.GetFieldname;
						nm = rof.MakeFieldName(nm0);
						xmldoc.pushHeader(nm0);
						// see if it has data in the ro.
						nd = this.SelectSingleNode(nm);
						// if not found with just the string, search the tree.
						if (nd==null)nd = this.SelectSingleNode("*/"+nm);
						if ((uint)rof.GetFieldType == (uint)FieldTypes.Combination && nd==null)
						{	
							nd = GetCurrentOfMultiFields(this,nm);
//							nd = this.SelectSingleNode(nm+"a");
//							if (nd==null) nd = this.SelectSingleNode(nm+"b");
//							if (nd==null) nd = this.SelectSingleNode(nm+"c");
//							if (nd==null)
//							{
//								doingplot=true;
//								nd = this.SelectSingleNode(nm+"d");
//							}	
						}
						if (nd == null) // not found, write out a '0'.
							xmldoc.writeInt(0);
						else
						{
							if (nd.Equals(nm+"d"))
								doingplot = true;
							VlnXmlElement end;
							end = (VlnXmlElement) nd;
							if (rof.GetFieldType == (uint) FieldTypes.Image)
							{
								// get filename.
								XmlNode ind = this.SelectSingleNode("*/Image_Filename");
								if (ind==null) 
									xmldoc.writeInt(0);
								else
								{
									// find space & filename is after it.
									int indx = ind.InnerText.IndexOf(" ");
									if (indx < ind.InnerText.Length && indx > -1)
										xmldoc.writeImage(ind.InnerText.Substring(indx+1,ind.InnerText.Length-indx-1));
									else
										xmldoc.writeImage(ind.InnerText);
									// now do height & width??
									ind = this.SelectSingleNode("*/Image_Height");
									if (ind != null)
									{
										short iht = (short) System.Convert.ToInt16(ind.InnerText);
										xmldoc.writeInt(iht);
									}
									else
										xmldoc.writeInt(0);
									ind = this.SelectSingleNode("*/Image_Width");
									if (ind != null)
									{
										short iht = (short) System.Convert.ToInt16(ind.InnerText);
										xmldoc.writeInt(iht);
									}
									else
										xmldoc.writeInt(0);
								}
							}
							else
							{
								if (doingplot)
									xmldoc.writePlot(end.InnerText);
								else
									xmldoc.writeTextValue(end.InnerText);
						
							}
						}
						xmldoc.popHeader();
					}

				}
				xmldoc.writeRROEnd();
			}
			return true;
		}

		// ShowGroup processes the input group node for the complete report by
		// using ShowGroupDetail to output data for the group and ShowRRO to
		// output data for the RO
		private bool ShowGroup(RODB myrodb, ArrayList headers, VlnStatusMessage showStat)
		{
			XmlNode chldnode;
			
			if (this.ParentNode.Name != "RO_Root")
			{	
				/* Bug fix:  B2001-001
				 * Compete RO Report for All ROs was printing an extra
				 * page showing just a prameter field.  This information
				 * was not always related to the RO Group being printed at
				 * this time.  Turns out that we don't need to call the
				 * ShowGroupDetail() at this point, only need to save
				 * header informat.  -jsj 6/8/2005
				 *
				ShowGroupDetail(myrodb);
				*/
				headers.Add(this.GetAttribute("MenuTitle"));
			}
			else
			{
				int bracket = this.InnerXml.IndexOf("<",0);
				if (bracket>-1) 
					headers.Add(this.InnerXml.Substring(0,bracket));
				else
					headers.Add(this.InnerXml);
			}
			string haskids, kidsloaded;
			haskids = this.GetAttribute("HasChild");
			kidsloaded = this.GetAttribute("ChildLoaded");
			if (haskids == "True" && kidsloaded == "False" )
			{
				myrodb.RODB_GetChildData(this,true);
				this.SetAttribute("ChildLoaded", "True");
			}

			// go through all of the children for this node and put them out.
			chldnode = (XmlNode) this.FirstChild;
			
			while (chldnode != null)
			{
				if (chldnode is VlnXmlElement)
				{
					VlnXmlElement elem = (VlnXmlElement) chldnode;
//					showStat.StatusMessage = elem.InnerText;
	
					// if it's a group, recurse through it.
					if (chldnode.Name == "vlnGroup")
					{
						elem.ShowGroup(myrodb, headers, showStat);
					}
						// do RO's too.
					else
					{
						int levelcnt = chldnode.ChildNodes.Count;
						bool isParDisData = chldnode.Name == "Parameter__Display__Data";
						string TheMenuTitle = elem.GetAttribute("MenuTitle");
						showStat.StatusMessage = TheMenuTitle;
						if (!isParDisData && ((levelcnt > 1) || (levelcnt==1 && !TheMenuTitle.Equals(""))))
							elem.ShowRRO(myrodb, headers);
					}
				}
				chldnode = chldnode.NextSibling;
			}
			// remove this group from the headers.
			headers.RemoveAt(headers.Count-1);
			return true;
		}

		// put this element, and if a group all it's subelements out to the 'print.tmp'
		// file for complete reports.
		public bool Show(RODB myrodb, ArrayList headers)
		{
			bool success;
			VlnStatusMessage showStat = new VlnStatusMessage("Reading ROs");
			VlnXmlDocument xmldoc = (VlnXmlDocument) this.OwnerDocument;
			success = xmldoc.OpenTemp();
			if (success==false) return false;

			// if this is the top node, i.e. with name "RO_Root", it is a complete report 
			// for all Ros, work from the root.
			if (this.Name == "RO_Root")
			{
				// show each 'table' group under the top.
				VlnXmlElement group = (VlnXmlElement) this.FirstChild;
				VlnXmlElement tmpgroup;
				while (group != null)
				{	
					// Now get subgroups for each group.
					myrodb.RODB_GetChildData(group,true);
					group.ShowGroup(myrodb, headers, showStat);
					tmpgroup = group;
					group.RemoveAll();
					group = (VlnXmlElement) tmpgroup.NextSibling;
				}
							
			}
			// this is a particular RO or group of ROs.
			else
			{
				if (this.Name == "vlnGroup")
					ShowGroup(myrodb, headers, showStat);
				else
					ShowRRO(myrodb, headers);
			}
			showStat.Dispose();
			xmldoc.CloseTemp();
			return true;
		}
	}
}
