/*********************************************************************************************
 * Copyright 2002 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: RO_FST.cs $     $Revision: 15 $
 * $Author: Jsj $   $Date: 8/23/06 11:33a $
 *
 * $History: RO_FST.cs $
 * 
 * *****************  Version 15  *****************
 * User: Jsj          Date: 8/23/06    Time: 11:33a
 * Updated in $/EXE/RefObj/ROEditor
 * debug statements commented out
 * 
 * *****************  Version 14  *****************
 * User: Jsj          Date: 5/03/05    Time: 11:48a
 * Updated in $/EXE/RefObj/ROEditor
 * 2005 upgrade, move some ROFST logic to ROFST library
 * 
 * *****************  Version 13  *****************
 * User: Jsj          Date: 5/11/04    Time: 9:29a
 * Updated in $/EXE/RefObj/ROEditor
 * Bug fix B2004-011, could not create FST file if user opened nodes on
 * tree.
 * 
 * *****************  Version 12  *****************
 * User: Jsj          Date: 4/08/04    Time: 9:48a
 * Updated in $/EXE/RefObj/ROEditor
 * Modified code to speed up the creation of the RO.FST file
 * 
 * *****************  Version 11  *****************
 * User: Jsj          Date: 6/30/03    Time: 1:20p
 * Updated in $/EXE/RefObj/ROEditor
 * a NULL Accessory Page ID  was giving problems in creating a new RO.FST
 * file.
 * 
 * *****************  Version 10  *****************
 * User: Kathy        Date: 6/11/03    Time: 2:01p
 * Updated in $/EXE/RefObj/ROEditor
 * Fix bug B2003-045
 * 
 * *****************  Version 9  *****************
 * User: Kathy        Date: 5/30/03    Time: 12:48p
 * Updated in $/EXE/RefObj/ROEditor
 * B2003-044: sync up xml with UI tree view
 * 
 * *****************  Version 8  *****************
 * User: Kathy        Date: 5/21/03    Time: 12:51p
 * Updated in $/EXE/RefObj/ROEditor
 * B2003-034: process data if only one field for RO & also, convert return
 * value for xml save
 * 
 * *****************  Version 7  *****************
 * User: Jsj          Date: 4/14/03    Time: 3:02p
 * Updated in $/EXE/RefObj/ROEditor
 * Speed up the creation of the RO.FST file
 * 
 * *****************  Version 6  *****************
 * User: Kathy        Date: 4/04/03    Time: 9:41a
 * Updated in $/EXE/RefObj/ROEditor
 * B2003-030 convert new top group name to user readable for ro.fst
 * 
 * *****************  Version 5  *****************
 * User: Jsj          Date: 2/21/03    Time: 9:51a
 * Updated in $/EXE/RefObj/ROEditor
 * added RO FST completed message
 * 
 * *****************  Version 4  *****************
 * User: Jsj          Date: 1/02/03    Time: 9:31a
 * Updated in $/EXE/RefObj/ROEditor
 * Save Graphics file date in RO.FST file
 * 
 * *****************  Version 3  *****************
 * User: Jsj          Date: 12/17/02   Time: 4:54p
 * Updated in $/EXE/RefObj/ROEditor
 * save real date for graphic files
 * 
 * *****************  Version 2  *****************
 * User: Jsj          Date: 12/06/02   Time: 3:26p
 * Updated in $/EXE/RefObj/ROEditor
 * parameter display data fix (ingoring this data for now)
 * 
 * *****************  Version 1  *****************
 * User: Jsj          Date: 11/27/02   Time: 12:53p
 * Created in $/EXE/RefObj/ROEditor
 * Modification to create the RO.FST file
 *********************************************************************************************/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Xml;
using System.Xml.Schema;
using System.Text;
using System.IO;
using RODBInterface;
using System.Runtime.InteropServices;
using VlnStatus;
using System.Collections.Specialized;
using ROFST_FILE;
//using VlnProfiler; //don't forget to add VlnProfiler to the reference list
namespace ROEditor
{
	/// 
	/// This creates an RO.FST file
	/// 
	class FstTmpSTRC
	{
		public ushort thistype;
		public uint thisoff;
		public string title;
		public FstTmpSTRC(ushort type, uint offset, string tl)
		{
			thistype = type;
			thisoff = offset;
			title = tl;
		}
		void WriteString(BinaryWriter bw, string str)
		{
			int n=str.Length;
			byte [] b = new byte[n+1];
			for(int i =0; i < n; i++)
			{
				b[i] = (byte)str[i];
			}
			bw.Write(b,0,n+1); // +1 to include null
			/**
			byte nullbyte = 0;
			int i;
			for(i =0; i < str.Length; i++)
			{
				byte WrByte;
				WrByte = (byte)str[i];
				bw.Write(WrByte);
			}
			bw.Write(nullbyte);
***/
		}
		public void Write(BinaryWriter bw)
		{
			bw.Write(thisoff);
			bw.Write(thistype);
			WriteString(bw,title);
		}
	}
// The Sorted Array was not sorting via the ASCII value of each character in a given string.
// This sorting function compares two strings by doing a character by character comparison.
	public class MyComparer : IComparer
	{
		public int Compare (object x, object y)
		{
			int rtnval = 0;
			int xcnt=0,ycnt=0;
			int xlen,ylen;
			byte xbyte, ybyte;
			string xbuff = x.ToString();
			string ybuff = y.ToString();
			xlen = xbuff.Length;
			ylen = ybuff.Length;
			if (xbuff[0] == '<')
				rtnval = 0;
			if (ybuff[0] == '<')
				rtnval = 0;
			while ((rtnval==0) && ((xcnt < xlen) || (ycnt < ylen)))
			{
				xbyte = (xcnt == xlen)? (byte)0 : (byte)xbuff[xcnt++];
				ybyte = (ycnt == ylen)? (byte)0 : (byte)ybuff[ycnt++];
				rtnval = xbyte - ybyte;
			}
				
			return rtnval;
		}
	}
	public class RO_FST
	{
		private FST_FileHeader FstHeader;
		private string FstDir; // path to the RO directory
		private string FstPath; // RO directory Path and RO.FST file name
		private string FstOld; // ROFST.Old file name (backup of previous fst file)
		private string FstNew; // ROFST.New hold the RO.FST info during creation
		private BinaryWriter fhFST; // write handle to FST file being created
		private VlnStatusBar StatusWin;
		private RODB ROdatabase;
		private XmlDocument FSTroXmlDoc;
//		private HybridDictionary dicTiming;
//		private HybridDictionary dicDuration;
		ushort widestAcPgId;
		SortedList IdsAndAccPgIds;
		SortedList IdsAndOffsets;
		ushort AllTypesUsed;
		private Stack InUseListStack;
		private Stack RtnValTmplateStack;
//		private DateTime dtLast=DateTime.Now;
		public RO_FST(RODB TheROdb, XmlDocument TheXmlDoc)
		{
			ROdatabase = TheROdb; // point to the RO database
			FSTroXmlDoc = TheXmlDoc;
		}
		public bool Create()
		{
			bool RtnStat = true;
//			dicDuration=new HybridDictionary();
//			dicTiming=new HybridDictionary();
//			Profiler.Reset();
			// position to the top of the tree
			//			CurrentNode = roTree.TopNode;
			// Setup the paths to the FST files
			FstDir = Directory.GetCurrentDirectory();
			FstPath = FstDir + "\\RO.FST";
			FstOld = FstDir + "\\ROFST.Old";
			FstNew = FstDir + "\\ROFST.New";
			// open a temporary file for building the RO.FST
			try
			{
				fhFST = new BinaryWriter(File.Open(FstNew,System.IO.FileMode.Create,System.IO.FileAccess.ReadWrite));
			}
			catch (Exception fhExc)
			{
				string errmsg = fhExc.Message;
				MessageBox.Show(errmsg,"Error Creating RO.FST file",System.Windows.Forms.MessageBoxButtons.OK);
				RtnStat = false;
			}
			// If we were able to open a new ROFST.NEW file then go ahead and put the
			// new FST information into it.
			if (RtnStat)
				RtnStat = BuildNewRoFST();
			if (RtnStat) // successful in creating new RO FST file?
			{
				//If an RO.FST file already exists,
				// rename the existing file for safe keeping
				if (File.Exists(FstPath))
				{
					// copy the existing RO.FST to ROFST.OLD
					// - overrite ROFST.OLD if it already exists
					File.Copy(FstPath,FstOld,true); // save RO.FST as ROFST.OLD
					File.Delete(FstPath);  // remove RO.FST
				}
				File.Copy(FstNew,FstPath,true); // save ROFST.NEW as RO.fST
				File.Delete(FstNew); // remove ROFST.NEW
				MessageBox.Show("New RO.FST file created successfully.","Create RO.FST file",System.Windows.Forms.MessageBoxButtons.OK);
			}
//			MessageBox.Show(Profiler.ToString("000.00%"),"Timings");
//			dicDuration=null;
//			dicTiming=null;
			return RtnStat;
		}
		private bool BuildNewRoFST()
		{
			bool RtnVal = true;
			int i;
			int numDatabases;
			// write the empty header - save space for the header info
			FstHeader = new FST_FileHeader();
			FstHeader.Write(fhFST);
			// point to the ROMASTER and get the number of databases it has
			VlnXmlElement ROdbtables = (VlnXmlElement) FSTroXmlDoc.FirstChild;
			numDatabases = ROdbtables.ChildNodes.Count;
			// Allocate a list of database info structures
			ROFST_DbInfo[] dbinfo = new ROFST_DbInfo[numDatabases];
			//For each RO database, process each group
			XmlNode  ROtableNode = ROdbtables;
			ROtableNode = ROtableNode.FirstChild;
			StatusWin = new VlnStatusBar("Creating RO.FST file");
			for (i=0; i < numDatabases && RtnVal; i++)
			{
				dbinfo[i] = new ROFST_DbInfo();
				RtnVal = SaveToFSTFile(ROtableNode,dbinfo[i]);
				dbinfo[i].dbiType = AllTypesUsed;
				ROtableNode = ROtableNode.NextSibling;
			}
			if (RtnVal)
			{
				VlnXmlElement tblnode = (VlnXmlElement) ROdbtables.FirstChild;
				StatusWin.BarMax = numDatabases * 2;
				StatusWin.BarStepValue = 1;
				StatusWin.BarValue = 0;
				// All of the RO database, group, and return value information
				//  was saved to the FST file.  Now save the information needed
				//  to read the FST file.
				// Save DB list
				uint lngbuf = 0;
				uint dblength = (uint)fhFST.BaseStream.Position;
				fhFST.Write(lngbuf);
				ushort intbuf = Convert.ToUInt16(numDatabases);
				fhFST.Write(intbuf);
				// The old code did a sizeof() on a structure containing the ROFSTDatabaseInfo (dbi)
				// You cannot read/write a Struct type in C# like you can with C++.  The C# books suggest
				// creating a Class to replace the Struct, then create methods that perform the binary
				// read/write.  So I created a method that calculates the "struct" size to simulate the
				// sizeof(struct dbi)
				uint lngval = (dbinfo[0].GetStructSize()) * (uint)numDatabases;
				for (i=0; i=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= 1) && !TheMenuTitle.Equals(""))
							{
								curOffset = (uint)fhFST.BaseStream.Position;
								curType = SaveROToFST(chldnode,InUseList,RtnValTmplate,AccPageIDTplate);
							}
							else
							{
								SkipThisOne = true;
							}
						}
						if (!SkipThisOne)
						{
							string tmpstr = ChildElem.GetAttribute("MenuTitle");
							tmpfststrc = new FstTmpSTRC(curType,curOffset,tmpstr);
							FstTmp.Add(tmpfststrc);
							typ |= curType;
							numgroups++;
						}
					} // end if VlnXmlElement
				
					// Get next child
					chldnode = chldnode.NextSibling;
				}// end while
			} // if child is not null
			// pop the InUseList stack
			if (PopInUseStack)
				InUseListStack.Pop();
			// pop the return valuse template stack
			if (PopRtnValStack)
				RtnValTmplateStack.Pop();
			// save the current position of the FST file for the return value
			RtnVal = (uint)fhFST.BaseStream.Position;
			// Save the ID and offset entry for the current ID
			IdsAndOffsets.Add(curRecID,RtnVal);
			fhFST.Write(curRecID);
			fhFST.Write(elemParentID);
			fhFST.Write(numgroups);
			// write the FstTmp
			for (int i=0; i < FstTmp.Count; i++)
			{
				tmpfststrc = (FstTmpSTRC)FstTmp[i];
				tmpfststrc.Write(fhFST);
			}
			FstTmp.Clear();
			return RtnVal;
		}
	
		public void WriteGraphicsReturnValue(string OrigStr)
		{
			string text, tmptext, hgttxt, widtxt, imgdateStr;
			int idx;
			int hgt, wid;
			int imgdateInt;
			byte newline = 10;
			char[] tmpbuf = OrigStr.ToCharArray();
			// remove the trailing "\r\n"
			tmptext = OrigStr.Substring(0,OrigStr.Length-2);
			// parse out the image width
			idx = tmptext.LastIndexOf("\r\n");
			widtxt = tmptext.Substring(idx+2,tmptext.Length-(idx+2));
			wid = Convert.ToInt32(widtxt,10);
			widtxt = wid.ToString("x4");
			tmptext = tmptext.Substring(0,idx);
			// parse out the image height
			idx = tmptext.LastIndexOf("\r\n");
			hgttxt = tmptext.Substring(idx+2,tmptext.Length-(idx+2));
			hgt = Convert.ToInt32(hgttxt,10);
			hgttxt = hgt.ToString("x4");
			tmptext = tmptext.Substring(0,idx);
			// parse out the image file name and image date
			idx = tmptext.IndexOf(" ");
			text = tmptext.Substring(idx+1);
			if (tmptext.StartsWith("\r\n"))
				imgdateStr = tmptext.Substring(2,idx-2); // first 2 chars are "\r\n"
			else
			{
				if(idx < 1)
					imgdateStr=null;
				else
					imgdateStr = tmptext.Substring(0,idx);
			}
			if (imgdateStr == null|| imgdateStr=="")
				imgdateInt = 0;
			else
				imgdateInt = Convert.ToInt32(imgdateStr,10);
			 // write graphics file name
			WriteString(text,false);
			fhFST.Write(newline);
			// write dummy date (old FST file had only zeros!
//			WriteString("00000000",false);
			WriteString(imgdateInt.ToString("x8"),false);
			fhFST.Write(newline);
			// write the image height
			WriteString(hgttxt,false);
			fhFST.Write(newline);
			//write the image width
			WriteString(widtxt,true);
		}
		private ushort SaveROToFST(XmlNode RONode,ArrayList InUseList,string RtnValTmplate, string AccPageIDTplate)
		{
			ushort RtnVal;
			uint startFST = (uint)fhFST.BaseStream.Position;
			uint RORecID;
			uint ParID;
			byte nullbyte=0;
			VlnXmlElement elem;
			elem = (VlnXmlElement) RONode;
//			string dummy = "";  // need for RODB_GetFIeldsInUse call, won't be used.
//			ArrayList AvailList, InUseList;
//			//Get the "In Use" field list
//			AvailList = ROdatabase.RODB_GetFields(elem, (uint) RecordType.Schema);
//			InUseList = ROdatabase.RODB_GetFieldsInUse(elem, AvailList,"FieldsInUse", ref dummy);
			//Write the RO's record ID
			string RecIdStr = elem.GetAttribute("RecID");
			RORecID = System.Convert.ToUInt16(RecIdStr,16);
			fhFST.Write(RORecID);
// **** Debug
//			if (RecIdStr.Equals("000000a6"))
//				nullbyte=0;
// ***
			// Get the Accessory Page ID
//			string AccPageIDTplate = elem.GetAccPageIDTemplate();
			string AccPageID = elem.GetAccPageIDString(AccPageIDTplate);
// **** Debug
//			if (AccPageID.Equals("S:1"))
//				nullbyte=0;
// *****
			//Write the parent ID
			string ParIDstr = elem.GetAttribute("ParentID");
			ParID = System.Convert.ToUInt16(ParIDstr,16);
			fhFST.Write(ParID);
			// not sure why, was in old FST code but no comment!
			// I assume that it's some sort of separater
			short none = -1;
			fhFST.Write(none);
			//Get the RO Return value and return type
//			string RtnValTmplate = elem.GetReturnValueTemplate();
			RtnVal = 0;
			string tablename = elem.GetAttribute("Table");
//			if (RtnValTmplate.Equals(""))
//			{
//				int i;
//				i =0;
//			}
			string cvttmp= CvtFldToUserFld(RtnValTmplate);
			string RORtnVal = elem.GetReturnValue(ROdatabase,tablename,cvttmp,InUseList,ref RtnVal);
			// Write the field type to the FST
			fhFST.Write(RtnVal);
			if (RtnVal == 8) // is this an Image (graphics) record?
				WriteGraphicsReturnValue(RORtnVal);
			else
				WriteString(RORtnVal);
			// Write the Accessory Page ID
			// Fix for Bug B2003-039. Added check for a NULL AccPageID
			// If is null, then assign a blank string
			if (AccPageID != null)
				AccPageID = AccPageID.Trim();
			else
				AccPageID = " ";
			WriteString(AccPageID);
			// Save the ID and offset entry for the current ID
			IdsAndOffsets.Add(RORecID,startFST);
			// Save the RecID and Accessory Page id
			IdsAndAccPgIds[AccPageID] = RORecID;
			
			// Save the widest AccPageID width
			int acclen = AccPageID.Length;
			if (acclen > widestAcPgId)
				widestAcPgId = (ushort)acclen;
//			if(dtLast.AddSeconds(.3) < DateTime.Now)
//			{
				StatusWin.StatMsg = AccPageID;
				StatusWin.PerformStep();
//				dtLast=DateTime.Now;
//			}
			return RtnVal;
		}
		public void WriteString(string str)
		{
			WriteString(str,true);
		}
		public void WriteString(string str,bool AddNull)
		{
			int n=str.Length;
			if (AddNull) 
				n++;
			byte [] b = new byte[n];
			for(int i =0; i < str.Length; i++)
			{
				b[i] = (byte)str[i];
			}
			fhFST.Write(b,0,n);
		}
	}
}