/*********************************************************************************************
 * Copyright 2002 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: ConvertParadoxROs.cs $     $Revision: 16 $
 * $Author: Jsj $   $Date: 8/02/04 11:02a $
 *
 * $History: ConvertParadoxROs.cs $
 * 
 * *****************  Version 16  *****************
 * User: Jsj          Date: 8/02/04    Time: 11:02a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * Bug fix B2004-028  added check of local defined fields when checking if
 * the field we are converting is a combo field.
 * 
 * *****************  Version 15  *****************
 * User: Kathy        Date: 10/15/03   Time: 10:29a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * Found/fix during debugging of B2003-060 (will go through QA process
 * with that bug fix)
 * 
 * *****************  Version 14  *****************
 * User: Kathy        Date: 3/13/03    Time: 1:15p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * master different than local field name, use local
 * 
 * *****************  Version 13  *****************
 * User: Jsj          Date: 1/22/03    Time: 12:49p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * remove old paradox user and lock files before trying the convert.  Was
 * not able to open the paradox master file.
 * 
 * *****************  Version 12  *****************
 * User: Jsj          Date: 1/06/03    Time: 12:56p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * uses a special ROMASTER.MDB to convert data, will now remove temporary
 * TXT files if the DEBUG switch is not passed in.
 * 
 * *****************  Version 11  *****************
 * User: Jsj          Date: 12/19/02   Time: 9:17a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * now modifies the VEPROMS INI file if it has a [Graphics] section
 * 
 * *****************  Version 10  *****************
 * User: Jsj          Date: 12/18/02   Time: 9:01a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * fix for IP2 and FNP data
 * 
 * *****************  Version 9  *****************
 * User: Jsj          Date: 12/06/02   Time: 3:20p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * added Debug and new function to replace invalid characters in the field
 * names
 * 
 * *****************  Version 8  *****************
 * User: Jsj          Date: 12/04/02   Time: 11:01a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * added logic to handle Parameter Display Data data
 * 
 * *****************  Version 7  *****************
 * User: Jsj          Date: 11/27/02   Time: 1:39p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * Handle - and / in field names
 * 
 * *****************  Version 6  *****************
 * User: Jsj          Date: 10/15/02   Time: 11:39a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * added "a" to combo field names in non-master tables.
 * 
 * *****************  Version 5  *****************
 * User: Jsj          Date: 10/04/02   Time: 2:02p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * added better user interface
 * 
 * *****************  Version 4  *****************
 * User: Jsj          Date: 9/27/02    Time: 2:57p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * Fixed Graphic image information storage
 * 
 * *****************  Version 3  *****************
 * User: Jsj          Date: 9/26/02    Time: 4:17p
 * Updated in $/EXE/RefObj/ParadoxConversion
 * There was a leading space in all of the field values - is now fixed.
 * 
 * *****************  Version 2  *****************
 * User: Jsj          Date: 9/25/02    Time: 10:19a
 * Updated in $/EXE/RefObj/ParadoxConversion
 * added header
 *********************************************************************************************/
using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Windows.Forms; // Application.StartupPath
using System.Diagnostics;
using DBEncapsulation;
using ROFields;
namespace ParadoxConversion
{
	/// 
	/// This class contains the functions needed to convert the old Paradox RO Database
	/// to a newer type database.
	/// 
	public class ConvertParadoxROs
	{
		System.Windows.Forms.Label StatusWin;
		bool ProcessingMasterTable = false;
		bool DoDebugFile = false;
		BinaryReader BReader; // read text file as binary (for chars like hard spaces)
		BinaryWriter BWrite; // for a debug file
		String AppPath;
		String ROConvertPath;
		String ROPath;
		String LineOfText;
		String ROTableName, ROTblElmt;
		String RecID, RecType, ParentRecID, AccPageID, DateTime, Info, GroupTitle;
		int intRecType = 0;
		int CurrentFieldType = 0;
		int NumCRLFs = 0;
		XmlTextWriter xmlTxtWriter = null;
		RODBRecordInterface MyDbRec;
		ROField NewDbField;
		struct FieldInfo
		{
			public String fldRecID;		// Field Record ID
			public String fldName;			// Field name
			public String fldType;
		};
		ArrayList MasterFieldInfoList = new ArrayList();
		ArrayList LocalFieldInfoList = new ArrayList();
		struct GroupTitles
		{
			public String GrpRecID;
			public String GrpTitle;
		}
		ArrayList GroupTitlesList = new ArrayList();
		public void OpenTextFile(String FName)
		{
			BReader = new BinaryReader(File.Open(FName,System.IO.FileMode.Open,System.IO.FileAccess.ReadWrite));
		}
		public void CloseTextFile()
		{
			BReader.Close();
		}
		public String TxF_ReadLine()
		{
			bool success = true;
			String TxFstr = "";
			byte rdByte = 0;
			if (BReader != null)
			{
				NumCRLFs = 0;
				while (success)
				{
					try
					{
						rdByte = BReader.ReadByte();
					}
					catch (Exception e)
					{
						success = false;
						TxFstr =  null;
					}
					if (rdByte == 0x0D) // carrage return (end of line)
					{
						NumCRLFs++;
						rdByte = BReader.ReadByte(); // read the new line character
						if (rdByte == 0x0D) // sometimes there's an extra carrage return
						{
							rdByte = BReader.ReadByte();
							NumCRLFs++;
						}
						success = false; // to stop the loop
					}
					if (success)
					{
						TxFstr += Convert.ToString(Convert.ToChar(rdByte));
					}
				}
			}
			return TxFstr;
		}
/*** for debugging..... */
		public void DEBUG_OpenFile(String FName)
		{
			if (DoDebugFile)
				BWrite = new BinaryWriter(File.Open(FName,System.IO.FileMode.Create,System.IO.FileAccess.ReadWrite));
		}
		public void DEBUG_CloseFile()
		{
			if (DoDebugFile)
				BWrite.Close();
		}
		public void DEBUG_Write(string str)
		{
			if (DoDebugFile)
			{
				DEBUG_WriteString(str);
				DEBUG_WriteString("\n");
			}
		}
		public void DEBUG_WriteString(string str)
		{
			if (DoDebugFile)
			{
				int i;
				for(i =0; i < str.Length; i++)
				{
					byte WrByte;
					WrByte = (byte)str[i];
					BWrite.Write(WrByte);
				}
			}
		}
		public void DEBUG_CheckFieldName(string fldname)
		{
			if (DoDebugFile)
			{
				char[] tmpstr = fldname.ToCharArray();
				int len = fldname.Length;
				int cnt = 0;
				bool FirstOne = true;
				string OKpunch = " -._"; // we're also going to ignore spaces
				string outstr = "";
				int decval;
				while (cnt < len)
				{
					char tmpchr = tmpstr[cnt];
					if(!char.IsLetterOrDigit(tmpchr))
					{
						if (OKpunch.IndexOf(tmpchr) == -1) // not found
						{
							if (FirstOne)
							{
								FirstOne = false;
								DEBUG_Write(fldname);
							}
							decval = tmpchr;
							outstr = "   " + tmpchr.ToString() +"  [" + decval.ToString() + "]";;
							DEBUG_Write(outstr);
						}
					}
					cnt++;
				}
				if (!FirstOne)
					DEBUG_Write("----------------\n");
			}
		}
/**  End - For Debugging **/
		public ConvertParadoxROs(System.Windows.Forms.Label statWin, bool DoingDebugFile)
		{
			// Assume we are currently in the RO directory to be converted
			// Was it already converted? (check for existance of a backup
			//     created during the conversion process)
			// Yes - return
			// else  select the new database type
			StatusWin = statWin;
			DoDebugFile = DoingDebugFile;
			ClearFieldStrings();
		}
			/*
			 * Initialize the fields to empty strings.
			 */
		public void ClearFieldStrings()
		{
			RecID = "";
			RecType = "";
			ParentRecID = "";
			AccPageID = "";
			DateTime = "";
			Info = "";
		}
		public void CreateNewRoDatabase()
		{
			// Prompt user to select the new databaes type (ex Access, SQL Server, etc)
			// For now, we will be creating just an Access database.
			
			/*
			 * Copy our empty ROMaster.mdb file into the CONVERT directory (is the current
			 * directory at this point)  This RomasterCvrt.mdb is located in the VE-PROMS.NET\BIN16
			 * directory and is different than the ROMaster.mdb in VE-PROM.NET\BIN.  The one in the BIN16
			 * directory does not have any tables, while the one in the BIN directory has an
			 * ROMASTER table with the default records needed to create RO tables.
			 */
			String toPath = ROConvertPath + "\\ROMaster.mdb";
			String fromPath = AppPath + "16\\RomasterCvrt.mdb";
			File.Copy(fromPath,toPath,true); // overwrite the file if already there
			// Create an empty database.
			MyDbRec = new RODBRecordInterface(1,ROConvertPath);
			
			// Open the database connection
			MyDbRec.RODB_OpenConnection();
		}
		public void CloseNewRoDatabase()
		{
			MyDbRec.RODB_CloseConnection();
		}
		/*
		 * Create a ORIG_RO directory and move everything(except for the CONVERT directory)
		 * in RO into it.  Then move everying in CONVERT into RO.
		 */
		public void MoveOldROData()
		{
			Directory.SetCurrentDirectory("..\\");
			Directory.Move("RO","ORIG_RO");
			Directory.Move("CONVERT","RO");
			
		}
		/* This function will create TXT files from the Paradox database (PX2TXT.EXE) then parse
		 * them and place the RO information in the new database.
		 * 
		 * Note that the Microsoft Paradox driver could not read our Paradox BLOB fields.
		 * We had to write a program in the old Borland 16-bit environment that would basically
		 * dump the RO information into a file that the Microsoft drivers could read.  We decided
		 * to keep things simple and just dump the RO information into text files.
		 * 
		 * Each line in the text file represents a database record.  Each "field" is separated by
		 * a space.  Each "record" has the following fields:
		 * 
		 *	RECORD ID
		 *	RECORD TYPE
		 *	PARENT ID
		 *	ACCPAGE ID
		 *	DATETIME
		 *	The Paradox BLOB data
		 */
		public void TestMessage()
		{
			StatusWin.Text = "This is a TEST!";
			StatusWin.Refresh();
		}
		private void StatusMessage(String Mess1)
		{
			StatusWin.Text = Mess1;
			StatusWin.Refresh();
		}
		private void StatusMessage(String Mess1, String Mess2)
		{
			StatusWin.Text = Mess1 + Mess2;
			StatusWin.Refresh();
		}
		private void StatusMessage(String Mess1, String Mess2, String Mess3)
		{
			StatusWin.Text = Mess1 + Mess2 + Mess3;
			StatusWin.Refresh();
		}
		public void ConvertParadoxRODb(string PassedInROPath)
		{
			// Set the current working directory to my sample RO directory
//			Directory.SetCurrentDirectory("G:\\PROMSDAT\\VEWPB\\RO");
//			ROPath = "G:\\PROMSDAT\\VEBRAID\\RO";
//			Directory.SetCurrentDirectory(ROPath);
			ROPath = PassedInROPath;
			ROConvertPath = ROPath.Substring(0,ROPath.LastIndexOf("\\"));
			ROConvertPath += "\\CONVERT";
			// Create text files from the Paradox RO Database
			StatusMessage("Paradox to Text Files");
			// Get the path of where this executable resides
			AppPath = Application.StartupPath;
			// append "16\\px2txt.exe" to the path of the VE-PROMS.NET\BIN path
			// to get the resulting path: VE-PROMS.NET\BIN16\px2txt.exe
			String ToTxtAppPath = AppPath + "16\\px2txt.exe";
			/* 
			 * Before we run the PX2TXT.EXE program, delete any Paradox lock files that might
			 * be hanging around.
			 */
			File.Delete("Pdoxusrs.net");
			File.Delete("Pdoxusrs.lck");
			File.Delete("paradox.lck");
			// create a process & wait until it exits.
			Process myProcess = new Process();
			myProcess.StartInfo.FileName = ToTxtAppPath;			
			myProcess.Start();
			myProcess.WaitForExit();
			/*
			 * Now change directory to the CONVERT sub-directory that was created
			 * from PX2TXT.EXE
			 * 
			 * This directory should have the TXT files containing the RO data and
			 * the graphic files (Intergrated Graphics) used in the current RO database.
			 * Note that some of the graphic files might be converted to a different
			 * graphics format than what is used in the old RO Editor.
			 */
			Directory.SetCurrentDirectory(ROConvertPath);
			
			/* The PX2TXT.EXE program also creates a file called PXCONLST.TXT which
			 * contains a list of the TXT files that it created. 
			 * 
			 * Read the PXCONLST.TXT file.  This contains the list of TXT files
			 * that were generated as the first step to the conversion.
			 * Place these in a array list for processing.
			 * Assume that we are in the RO directory that is being converted
			 */
			string FileName = "Pxconlst.txt";
			int numDbs = 0;
			ArrayList DbList = new ArrayList();
			ArrayList DbTitleList = new ArrayList();
			if (!File.Exists(FileName))
			{
				// Conversion from Paradox to a text file failed.
				// **** provide error message ****
				StatusMessage("Conversion from Paradox to a text file failed.");
				return;
			}
			OpenTextFile(FileName);
			String FileNameTitle; // the title of the ROxxxxxx file
			String TextFileName;
			while ((FileNameTitle = TxF_ReadLine())!=null) 
			{
				DbTitleList.Add(FileNameTitle);
				TextFileName = TxF_ReadLine();
				DbList.Add(TextFileName);
				numDbs++;
			}
			CloseTextFile();
			// delete the Pxconlst.txt file if we are NOT debugging
			if (!DoDebugFile)
				File.Delete(FileName);
			// Create a new (empty) database
			CreateNewRoDatabase();
			// DEBUG - write the field names that have problem characters
			DEBUG_OpenFile("DebugFieldNames");
			// For each file listed in PXCONLST.TXT
			//    Open the file
			//    read each line
			//    (each line represents a record from the old Paradox database)
			//    translate each line into a database record
			System.Collections.IEnumerator myEnumerator = DbList.GetEnumerator();
			System.Collections.IEnumerator myTitleEnumerator = DbTitleList.GetEnumerator();
			while(myEnumerator.MoveNext()) 
			{
				myTitleEnumerator.MoveNext();
				// Clear the GroupTitles Array
				GroupTitlesList.Clear();
				// Clear the LocalFieldInfoList Array
				LocalFieldInfoList.Clear();
				TextFileName = myEnumerator.Current.ToString();
				// Get the RO Table Name (ex. ROMASTER, RO000001)
				ROTableName = myEnumerator.Current.ToString();
				int slen = ROTableName.Length;
				ROTableName = ROTableName.Remove(slen-4,4);
				// Create the database table
				MyDbRec.RODB_AddNewTable(ROTableName);
				if (StringsAreEqualU(ROTableName,"ROMASTER"))
					ProcessingMasterTable = true;
				else
					ProcessingMasterTable = false;
				String tmpMess;
				tmpMess = "Processing " + myTitleEnumerator.Current.ToString();
				StatusMessage(tmpMess);
				OpenTextFile(TextFileName); // Open the text file
				DEBUG_Write("===================");
				DEBUG_Write(tmpMess);
				DEBUG_Write(TextFileName);
				int cnt = 0;
				while ((LineOfText = TxF_ReadLine()) != null)
				{
					// Process the text line (each line is a record)
					ClearFieldStrings();
					cnt++;
					StatusMessage(tmpMess,"\nRecord: ",Convert.ToString(cnt));
					ProcessTextLine();
				}
				StatusMessage(" ");
				CloseTextFile();
				// delete the ROxxxx.txt file if we are NOT debugging
				if (!DoDebugFile)
					File.Delete(TextFileName);
				// Close database table
			}
			// DEBUG - Close Debug file
			DEBUG_CloseFile();
			CloseNewRoDatabase();
			MoveOldROData();
		}
		/* This function makes a copy of the passed in string, then replaces
		 * any blank character with an underbar ('_') character.
		 * The copy is returned.
		 */
		String StrBlanksToUnderbars(String TheString)
		{
			String RtnStr = TheString;
			RtnStr.Replace(" ", "_");
			return RtnStr;
		}
		/*
		 * This function compares a String with a (char *) array and returns
		 * true if they are equal, else it returns false
		 */
		bool StringsAreEqual(String thisStr, String thatStr)
		{
			bool StringsAreEqual = false;
			StringsAreEqual = (thisStr.CompareTo(thatStr) == 0);
			return StringsAreEqual;
		}
		/*
		 * This function will first uppercase the strings then compare them
		 * and returns true if they are equal, else it returns false
		 */
		bool StringsAreEqualU(String thisStr, String thatStr)
		{
			String ThisOne = thisStr;
			String ThatOne = thatStr;
			ThisOne.ToUpper();
			ThatOne.ToUpper();
			bool StringsAreEqual = false;
			StringsAreEqual = (ThisOne.CompareTo(ThatOne) == 0);
			return StringsAreEqual;
		}
		/*
		 * This function reads up to the next space character (or null)
		 */
		String GetFieldTxt()
		{
			String RtnStr = "0";
			char[] space = new char[1]{' '};
			int txtIdxEnd = LineOfText.IndexOfAny(space); // blank is the end of a the field
			if (txtIdxEnd > 0)
			{
				RtnStr = LineOfText.Substring(0,txtIdxEnd);
				LineOfText = LineOfText.Remove(0,txtIdxEnd+1); // remove this "field" from the string
			}
			else if (LineOfText.Length > 0) // last field in the string
			{
				RtnStr = LineOfText;
				LineOfText = "";
			}
			return RtnStr;
		}
		/*
		 * This function converts the given string to an interger
		 */
		int GetFieldInt(String TmpStr)
		{ 
			int RtnInt;
			RtnInt = Convert.ToInt32(TmpStr);
			return RtnInt;
		}
		/* This function will first read the length of the string (number of characters to get)
		 * Then get the string of that length.
		 */
		String GetStringByLength()
		{
			String RtnStr = "";
			int len = (int)GetFieldInt(GetFieldTxt());
			int LineTextLen = LineOfText.Length;
			while (len > LineTextLen)
			{
				RtnStr += LineOfText + "\r\n"; // need both a '\r' and a '\n'
				/* NumCRLFs accounts for the number of "\r\n"s that was originally counted when the text
				 * file was created.
				 */
				len -= (LineTextLen + NumCRLFs);
				LineOfText = TxF_ReadLine();
				if (len == 0)
				// end of string had \r\n still may need to remove a blank at the beginning of the string buffer
				{
					if (LineOfText.StartsWith(" "))
						LineOfText = LineOfText.Remove(0,1);
				}
				LineTextLen = LineOfText.Length;
			}
			if (len > 0)
			{
				RtnStr += LineOfText.Substring(0,len);
				LineOfText = LineOfText.Remove(0,len+1); // add one to remove following space
			}
			return RtnStr;
		}
		/*
		 * This function builds the field definition string
		 */
		String GetFieldDefinition()
		{
			String tmpStr;
			String RtnStr = "";
			String FieldTypeStr = "";
			int FieldType = 0;
			String FieldLength = "";
			String FieldName = "";
			FieldName = GetStringByLength(); // get the field name
			FieldName = FixFieldName(FieldName);
			NewDbField.SetFieldname(FieldName);
			// get the field type
			FieldTypeStr = GetFieldTxt();
			FieldType = GetFieldInt(FieldTypeStr);
			// Save the name and rec id and type in an array
			FieldInfo TheFieldInfo = new FieldInfo();
			TheFieldInfo.fldName = FieldName;
			TheFieldInfo.fldRecID = RecID;
			TheFieldInfo.fldType = FieldTypeStr;
			if (ProcessingMasterTable)
			{
				MasterFieldInfoList.Add(TheFieldInfo);
			}
			else // is a local definition
			{
				LocalFieldInfoList.Add(TheFieldInfo);
			}
			
			NewDbField.SetFieldType((uint)FieldType);
			switch (FieldType)
			{
				case 1:		// Fixed Length Text
				case 2:		// Variable Length Text
				case 8:		// X/Y Plot
				case 0x10:	// Table
					// Get the field length
					FieldLength = GetFieldTxt();
					// append/save the Field Name and Field Length to RtnStr
					RtnStr += NewDbField.MakeSchemaString(FieldName,FieldLength,"");
					break;
				case 4:		// Formatted Text
					String Pattern;
					// Get the field length
					FieldLength = GetFieldTxt();
					// Get the pattern
					Pattern = GetStringByLength();
					// append/save the Field Name, Length, and Pattern to RtnStr
					RtnStr += NewDbField.MakeSchemaString(FieldName,FieldLength,Pattern);
					break;
				case 0x20:	// Image (Intergrated Graphics)
					// append/save only the type to RtnStr
					RtnStr = NewDbField.MakeImageSchemaString(FieldName);
					break;
				case 0x40:	// Multi Field (used by Parameter Display Data)
					RtnStr += FieldName + " " + FieldTypeStr;
					tmpStr = GetFieldTxt();
					// Create a list of RecIDs separated by a blank
					while (!StringsAreEqual(tmpStr,"0")) // while not "0"
					{
						// append/save the RefRecID to Rtn Str
						RtnStr += " " + tmpStr;
						tmpStr = GetFieldTxt();
					}
					if (intRecType == 2)
					{
						RecType = "4";
						intRecType = 4;
					}
					break;
				case 0x100:	// Generic Field (reference to a field definition)
					tmpStr = GetFieldTxt();
					string tmp1 = GetSavedMasterFieldNameAndType(tmpStr).Substring(0,4);
					if (tmp1.Equals("128 "))
						RtnStr = tmpStr + " " + tmp1 + FieldName + "a";
					else
						RtnStr = tmpStr + " " + tmp1 + FieldName;
//					RtnStr = tmpStr + " " + (GetSavedMasterFieldNameAndType(tmpStr));
					break;
				case 0x80:	// Combo Field
					String SingleLineWidth = "";
					String MultiLineWidth = "";
					String TableWidth = "";
					String XYPlotWidth = "";
					bool Single = false, Multi = false, Tbl = false, XYPlt = false;
					int fldtyp;
						
					do
					{
						tmpStr = GetFieldTxt();
						fldtyp = GetFieldInt(tmpStr);
						switch (fldtyp)
						{
							case 1: // single line text
								SingleLineWidth = GetFieldTxt();
								Single = true;
								break;
								
							case 2: // multi line text
								MultiLineWidth = GetFieldTxt();
								Multi = true;
								break;
							case 8: // X/Y Plot
								XYPlotWidth = GetFieldTxt();
								XYPlt = true;
								break;
							case 16: // Table
								TableWidth = GetFieldTxt();
								Tbl = true;
								break;
							default:
								break;
						} // end switch
					} while (!StringsAreEqual(tmpStr,"0"));
					// append/save the Field Type to RtnStr
					RtnStr = NewDbField.MakeComboSchemaString(FieldName,Single,SingleLineWidth,Multi,MultiLineWidth,Tbl,TableWidth,XYPlt,XYPlotWidth);
					break;
				default: // nothing to do
					break;
			} // end switch
			return RtnStr;
		}
		String GetValueDefinition()
		{
			String RtnStr = "";
			String tmpStr = "";
			String tmpStr2 = "";
			String tmpStr3 = "";
			String RecIDRef = "";
			String MenuText = "";
			String ValueDefTypeStr = "";
			int ValueDefType = 0;
			int NumItems;
			do
			{
				ValueDefTypeStr = GetFieldTxt();
				ValueDefType = GetFieldInt(ValueDefTypeStr);
				switch (ValueDefType)
				{
					case 1:	// Record ID referencing a field record
						RecIDRef = GetFieldTxt();
						tmpStr = GetSavedLocalFieldName(RecIDRef);
						// append/save ValueDefType and RecIDRef to RtnStr
						RtnStr += "<" + tmpStr + ">";
						break;
					case 2:	// Text value
						tmpStr = GetStringByLength();
						tmpStr = FixString(tmpStr);
						// append/save ValueDefType and TxtStr to Rtn Str
						RtnStr += tmpStr;
						break;
					case 3:	// Decimal allignment field
						// append/save  RecIDRef, OffSetVal, and Width to RtnStr
						RecIDRef = GetFieldTxt();
						tmpStr = GetSavedLocalFieldName(RecIDRef);
						// append/save ValueDefType and RecIDRef to RtnStr
						RtnStr += "<" + tmpStr;
						tmpStr = GetFieldTxt();
						//int OffSetVal = GetFieldInt(tmpStr);
						RtnStr += ","+tmpStr;
						tmpStr = GetFieldTxt();
						//int Width = GetFieldInt(tmpStr);
						RtnStr += ","+tmpStr+">";
						break;
					
					case 4:	// Menu value definition
						MenuText = "";
						// number of menu items
						tmpStr = GetFieldTxt();
						NumItems = GetFieldInt(tmpStr);
						tmpStr2 = "";
						while (NumItems > 0)
						{
							// menu text string (maybe)
							tmpStr = GetFieldTxt();
							if (!StringsAreEqual(tmpStr,"0")) // do we have MenuText?
							{
								MenuText = GetStringByLength();
								MenuText = FixString(MenuText);
							}
							// append/save ValueDefType, NumItems, tmpStr, and MenuText to RtnStr
							// Get list of other ValueDefTypes
							tmpStr = GetValueDefinition(); // call itself
							// append/save tmpStr to RtnStr
							tmpStr2 += "{" + MenuText + "=" + tmpStr + "}";
							NumItems--;
						}
						RtnStr += tmpStr2;
						break;
					case 5:	// Conditional value definition
						MenuText = "";
						// number of menu items
						tmpStr = GetFieldTxt();
						NumItems = GetFieldInt(tmpStr);
						tmpStr3 = "";
						tmpStr2 = "";
						while (NumItems > 0)
						{
							// menu text string (maybe)
							tmpStr = GetFieldTxt();
							if (!StringsAreEqual(tmpStr,"0")) // do we have MenuText?
							{
								MenuText = GetStringByLength();
								MenuText = FixString(MenuText);
							}
							// append/save ValueDefType, NumItems, tmpStr, and MenuText to RtnStr
							// Get list of other ValueDefTypes
							tmpStr = GetValueDefinition(); // call itself
							// append/save tmpStr to RtnStr
							tmpStr2 += "{" + MenuText + "=" + tmpStr + "}";
							NumItems--;
						}
						// Conditional text???
						tmpStr = GetStringByLength();
						// append/save tmpStr to RtnStr
						tmpStr = FixString(tmpStr);
						
						tmpStr3 = "{" + tmpStr + tmpStr2 + "}";
						RtnStr += tmpStr3;
						break;
					case 6:	// VARUSE
						tmpStr = GetStringByLength();
						tmpStr = FixString(tmpStr);
						// append/save ValueDefType and TxtStr to RtnStr
						RtnStr += "{" + tmpStr + "}";
						break;
					case 7:	// VARDEFINE
						tmpStr2 = GetStringByLength();
						tmpStr2 = FixString(tmpStr2);
						tmpStr = GetValueDefinition(); // call itself
						// append/save ValueDefType, TxtStr, and tmpStr to RtnStr
						RtnStr += "{" + tmpStr2 + "=" + tmpStr + "}";
						break;
					default: // nothing to do
						break;
				} // end switch
			} while (ValueDefType != 0);
			/*
			 * Need to replace '<' with '<' and '>' with '>'
			 */
			RtnStr = ReplaceGreaterLessSigns(RtnStr);
			return RtnStr;
		}
		/*
		 * This function returns a string containing a value to an RO field.
		 */
		String GetFieldValue()
		{
			String RtnStr;
			String tmpStr;
			String ValueTypeStr;
			int ValueType = 0;
			ValueTypeStr = GetFieldTxt();
			ValueType = GetFieldInt(ValueTypeStr);
			CurrentFieldType = ValueType;
			RtnStr = "";
			switch (ValueType)
			{
				case 1:	// Text String
				case 2: // variable text string
					tmpStr = GetStringByLength();
					tmpStr = FixString(tmpStr);
					RtnStr += tmpStr;
					break;
				case 0x20: // Image File (Intergrated Graphics)
					tmpStr = GetStringByLength(); // image file name
					tmpStr = FixString(tmpStr);
					String DateTimeStr = GetFieldTxt();
					String ImgHeight = GetFieldTxt();
					String ImgWidth = GetFieldTxt();
					RtnStr += "\r\n" + DateTimeStr + " " + tmpStr + "\r\n";
					RtnStr += "" + ImgHeight + "\r\n";
					RtnStr += "" + ImgWidth + "\r\n";
					break;
				case 0x40: // Parameter Display Data
					tmpStr = GetFieldTxt();
					while (!StringsAreEqual(tmpStr,"0"))
					{
						tmpStr = GetFieldTxt(); // get the RecID of the field
						while (!StringsAreEqual(tmpStr,"0"))
						{
							string FieldName = GetSavedLocalFieldName(tmpStr);
							RtnStr += "<" + FieldName+ ">";
							RtnStr += GetFieldValue();
							RtnStr += "" + FieldName + ">\r\n";
							tmpStr = GetFieldTxt(); // next
						}
						tmpStr = GetFieldTxt(); // next
					}
					break;
				default:
					// should never get into here....
					tmpStr = GetStringByLength();
					tmpStr = FixString(tmpStr);
					RtnStr += tmpStr;
					break;
			} // end switch
			return RtnStr;
		}
		// Search the local list for the given Record ID
		// Return the field name
		String GetSavedLocalFieldName(String RecordID)
		{
			String RtnStr = "";
			FieldInfo TmpInfo;
			System.Collections.IEnumerator FldLstEnumerator = LocalFieldInfoList.GetEnumerator();
			while (FldLstEnumerator.MoveNext() && StringsAreEqual(RtnStr,""))
			{
				TmpInfo = (FieldInfo)FldLstEnumerator.Current;
				if (StringsAreEqual(TmpInfo.fldRecID,RecordID))
						RtnStr = TmpInfo.fldName;
			}
			return RtnStr;
		}
		// Search the master field list for a the given Record ID
		// Return the field name and type if found
		String GetSavedMasterFieldNameAndType(String RecordID)
		{
			String RtnStr = "";
			FieldInfo TmpInfo;
			System.Collections.IEnumerator FldLstEnumerator = MasterFieldInfoList.GetEnumerator();
			while (FldLstEnumerator.MoveNext() && StringsAreEqual(RtnStr,""))
			{
				TmpInfo = (FieldInfo)FldLstEnumerator.Current;
				if (StringsAreEqual(TmpInfo.fldRecID,RecordID))
				{
					RtnStr = TmpInfo.fldType.PadLeft(3,'0') + " " + TmpInfo.fldName;
					if (TmpInfo.fldType.Equals("128")) // is this a combo type?
						RtnStr += "a"; // append an "a"
				}
			}
			return RtnStr;
		}
		/* 
		 * See if the given RECID is referencing a combo field.
		 */
		bool IsComboFieldType(String fldName)
		{
			bool IsACombo = false;
			bool wasFound = false;
			FieldInfo TmpInfo;
			System.Collections.IEnumerator LocFldLstEnumerator = LocalFieldInfoList.GetEnumerator();
			System.Collections.IEnumerator FldLstEnumerator = MasterFieldInfoList.GetEnumerator();
// Bug fix: B2004-028
// Need to check local list of fields (of this group)
// Then then if not in the local list, check the "global" list
			while (!IsACombo && LocFldLstEnumerator.MoveNext())
			{
				TmpInfo = (FieldInfo)LocFldLstEnumerator.Current;
				if (StringsAreEqual(TmpInfo.fldName,fldName))
				{
					wasFound = true;
					if (StringsAreEqual(TmpInfo.fldType,"128"))
						IsACombo = true;
				}
			}
			if (!wasFound)
			{
				while (!IsACombo && FldLstEnumerator.MoveNext())
				{
					TmpInfo = (FieldInfo)FldLstEnumerator.Current;
					if (StringsAreEqual(TmpInfo.fldName,fldName) && StringsAreEqual(TmpInfo.fldType,"128"))
						IsACombo = true;
				}
			}
			return IsACombo;
		}
		String BuildComboFieldName(String FldName)
		{
			String RtnStr = FldName;
			switch (CurrentFieldType)
			{
				case 1: //Fixed Text
					RtnStr += "a";
					break;
				case 2: // Variable Text
					RtnStr += "b";
					break;
				case 16: // Table
					RtnStr += "c";
					break;
				case 8:
					RtnStr += "d";
					break;
				default:
					break;
			}
			return RtnStr;
		}
		String GetGroupTitle(String RecordID)
		{
			String RtnStr = "";
			GroupTitles Tmpitem;
			System.Collections.IEnumerator GrpLstEnumerator = GroupTitlesList.GetEnumerator();
			while (GrpLstEnumerator.MoveNext() && StringsAreEqual(RtnStr,""))
			{
				Tmpitem = (GroupTitles)GrpLstEnumerator.Current;
				if (StringsAreEqual(Tmpitem.GrpRecID,RecordID))
					RtnStr = Tmpitem.GrpTitle;
			}
			return RtnStr;
		}
		String ReplaceGreaterLessSigns(String InStr)
		{
			String OutStr = InStr;
			OutStr = OutStr.Replace("<","<");
			OutStr = OutStr.Replace(">",">");
			return OutStr;
		}
		// Replace any character less than 32 (space) or greater then
		// 126 (~) with "D;" where D is the decimal value of the character
		String ReplaceNonStandardChars(string instring)
		{
			string outstring = "";
			int len = instring.Length;
			int i=0;
			int substart=0;
			ushort decchar;
			while (i < len)
			{
				while (i < len && instring[i] < 127) i++;
				outstring += instring.Substring(substart, i-substart);
				if (i < len)
				{
					decchar = instring[i];
					outstring += "" + decchar.ToString() + ";";
					i++;
					substart = i;
				}
			}
			return outstring;
		}
		/*
		 * String cannot have '&' or '/' chars
		 * replace them with an '_'
		 */
		String FixString(String instring)
		{
			String FixedString = "";
			StringWriter strWriter = new StringWriter();
			xmlTxtWriter = new XmlTextWriter(strWriter);
			xmlTxtWriter.WriteString(instring);
			FixedString = strWriter.ToString();
			xmlTxtWriter.Close();
			// Now replace any character less than 32 (space) or greater then
			// 126 (~) with "D;" where D is the decimal value of the character
			FixedString = ReplaceNonStandardChars(FixedString);
			// need to replace the forward slash with an character reference
			FixedString = FixedString.Replace("/","/");
			/*
			 * The WriteString method is not replacing the single
			 * and double quotes.  So we will do it manually
			 */
			FixedString = FixedString.Replace("'","'");
			FixedString = FixedString.Replace("\"",""");
			return FixedString;
		}
		public string ReplaceNonValidCharsInFieldName(string fldname)
		{
			string tmpstr = fldname;
			int len = fldname.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++;
			}
			DEBUG_Write(outstr);
			return outstr;
		}
/*
 * Field names cannot have ' ', '&', or '/' characters
 */
		String FixFieldName(String fldname)
		{
			String fixedField = fldname;
			DEBUG_CheckFieldName(fldname);
			fixedField = fixedField.Replace(" ","__");
			/*
			 * If the first character is 0-9, then put a '_' in front of the field name
			 */
			if (Char.IsDigit(fixedField,0))
				fixedField = "__" + fixedField;
			fixedField = ReplaceNonValidCharsInFieldName(fixedField);
			return fixedField;
		}
		/*
		 * This function will parse the remaining information in the LineOfText string
		 * and create the value needed for the Info field of the database record.
		 */
		String ParseInfoField()
		{
			String RtnStr = "";
			String tmpStr;
			String Title;
			String ROMenuHead;
			String GrpMenuHead;
			String RoValueDef;
			String RoMenuItemDef;
			String GroupMenuItemDef;
			String RoFieldsInUse;
			String GroupFieldsInUse;
			String FieldName;
			int intRecType = GetFieldInt(RecType);
			switch (intRecType)
			{
				case 0: // Next Record Id or next database table name
					RtnStr = GetFieldTxt();
					break;
				case 1: // Database title and database name
					RtnStr = GetStringByLength(); // database title
					ROTblElmt = FixFieldName(RtnStr);
					RtnStr += "\t";
					tmpStr = GetStringByLength(); // database table name
					if (tmpStr.Length == 0)
						RtnStr += "RO000001"; // default database table name
					else
						RtnStr += tmpStr;
					break;
				case 2: // Field Description
						// get the field definition
						RtnStr = GetFieldDefinition();
					break;
				case 3: // RO Group
					GroupTitle = GetStringByLength(); // group name (title)
					ROMenuHead = GetStringByLength(); // RO menu header
					GrpMenuHead = GetStringByLength();// Group menu header
					RtnStr = "";
					// add group name
					RtnStr += FixString(GroupTitle);
					RtnStr += ""; // end group definition
					break;
				case 4: // sub group
					Title = GetStringByLength(); // sub group name (title)
					ROMenuHead = GetStringByLength(); // RO menu header
					GrpMenuHead = GetStringByLength();// sub group menu header
					// replace special chars
					Title = FixString(Title);
					// Save the group title and recid for use later on
					GroupTitles TheGroupTitle = new GroupTitles();
					TheGroupTitle.GrpRecID = RecID;
					TheGroupTitle.GrpTitle = Title;
					GroupTitlesList.Add(TheGroupTitle);
					RtnStr = "";
					// Sub Group fields and values
					String SubGrpFldsAndVals = "";
					tmpStr = GetFieldTxt();
					while (!StringsAreEqual(tmpStr,"0"))
					{
						FieldName = GetSavedLocalFieldName(tmpStr);
						SubGrpFldsAndVals += "<" + FieldName+ ">";
						SubGrpFldsAndVals += GetFieldValue();
						SubGrpFldsAndVals += "" + FieldName + ">\r\n";
						tmpStr = GetFieldTxt(); // next
					}
					RtnStr += SubGrpFldsAndVals;
					RtnStr += ""; // end group definition
					break;
				case 5: // RO Value Record
					// RO Menu Item Value
					String ROMenuItemValue = GetStringByLength();
					ROMenuItemValue = FixString(ROMenuItemValue);
					RtnStr = "<" + FixFieldName(GroupTitle) + " ";
					// RO Menu Item
					RtnStr += "MenuTitle=\"" + ROMenuItemValue + "\">\r\n";
					//RO fields and values
					String ROFldsAndVals = "";
					tmpStr = GetFieldTxt();
					while (!StringsAreEqual(tmpStr,"0"))
					{
						FieldName = GetSavedLocalFieldName(tmpStr);
						tmpStr = GetFieldValue();
						if (IsComboFieldType(FieldName))
						{
							FieldName = BuildComboFieldName(FieldName);
						}
						ROFldsAndVals += "<" + FieldName + ">";
						ROFldsAndVals += tmpStr;
						ROFldsAndVals += "" + FieldName + ">\r\n";
						tmpStr = GetFieldTxt();
					}
					RtnStr += ROFldsAndVals;
					RtnStr += "" + FixFieldName(GroupTitle) + ">\r\n";
					break;
				
				default:
					break;
			}// end switch
			return RtnStr;
		}
		/*
		 * This function will parse the given text record (string) and place the
		 * information in a database record.
		 */
		void ProcessTextLine()
		{
			/*
			 * Get the first 5 database fields and save them in string types.
			 */
			RecID= GetFieldTxt();
			RecType = GetFieldTxt();
			ParentRecID = GetFieldTxt();
			AccPageID = GetStringByLength();
			DateTime = GetFieldTxt();
			intRecType = GetFieldInt(RecType);
/***** For Debugging ***/
//			if (RecID.Equals("000000c1"))
//			{
//				int test;
//				test = 1;
//			}
//			if (AccPageID.Equals("R.29"))
//			{
//				MessageBox.Show("debug");
//			}
/**** End - for debugging ****/
			// setup a new database record
			NewDbField = new ROField("", RecID, ParentRecID, (uint)intRecType);
			/* 
			 * Now what's remaining in the txtRecord string is the BLOB info from
			 * the old Paradox database record.
			 * Parse the remaining information and build the XML or XSD string that
			 * represents the data.
			 */
			Info = ParseInfoField();
			// Fix the single quotes before writing to the database
			// MS Access complained when we tried to write "C'.'99", wanted "C''.''99" instead
			Info = Info.Replace("'","''");
			// if we are processing a record type 4, save it as a record type 3.
			// record type 4's were considered subgroups. We are going to store them
			// as groups (type 3) with the parent id pointing to the the parent group
			// or subgroup.
			if (intRecType == 4 && !StringsAreEqual(RecType,"2"))
				intRecType = 3;
			// write to database
			MyDbRec.RODB_WriteDbRecord(ROTableName,RecID,intRecType,ParentRecID,AccPageID,DateTime,Info);
			if (StringsAreEqual(RecType,"0")&& StringsAreEqual(RecID,"00000001"))
			{
				// build schema header for the table just inserted
				Info = "\r\n";
				Info += "";
				Info += "\r\n";
				Info += "";
				// write the schema header record (type 6)
				// Note that a null recid will cause RODB_WriteDbRecord to get the next
				// available one and update the "next RecID" database record.
				MyDbRec.RODB_WriteDbRecord(ROTableName,"",6,"00000000","",DateTime,Info);
				// build the schema footer for the table just inserted
				Info = "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				Info += "\r\n";
				// write the schema footer record (type 7)
				// Note that a null recid will cause RODB_WriteDbRecord to get the next
				// available one and update the "next RecID" database record.
				MyDbRec.RODB_WriteDbRecord(ROTableName,"",7,"00000000","",DateTime,Info);
			}
		}
	}
}