using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System.Globalization;
// using HtmlHelp.Storage;

namespace HtmlHelp.ChmDecoding
{
	/// <summary>
	/// Internal enumeration for specifying the type of the html-help file
	/// </summary>
	internal enum HtmlHelpFileType
	{
		/// <summary>
		/// CHM - compiled contents file
		/// </summary>
		/// <remarks>A file with this extension must always exist. If the file would be too long, some parts 
		/// can be splitted into the filestypes below.</remarks>
		CHM = 0,
		/// <summary>
		/// CHI - compiled system file
		/// </summary>
		CHI = 1,
		/// <summary>
		/// CHQ - compiled fulltext search file
		/// </summary>
		CHQ = 2,
		/// <summary>
		/// CHW - compiled index file
		/// </summary>
		CHW = 3

	}

	/// <summary>
	/// The class <c>CHMFile</c> implemts methods and properties for handling a single chmfile.
	/// </summary>
	public sealed class CHMFile : IDisposable
	{
		/// <summary>
		/// Internal member storing a reference to the hosting HtmlHelpSystem instance
		/// </summary>
		private HtmlHelpSystem _systemInstance = null;
		/// <summary>
		/// Internal flag specifying if only system data has been loaded
		/// </summary>
		private bool _onlySystem = false;
		/// <summary>
		/// Internal flag specifying if the object is going to be disposed
		/// </summary>
		private bool disposed = false;
		/// <summary>
		/// Internal arraylist containing the table of contents
		/// </summary>
		private ArrayList _toc = new ArrayList();
		/// <summary>
		/// Internal arraylist containing items of the toc which are merge-Links
		/// </summary>
		private ArrayList _mergeLinks = new ArrayList();
		/// <summary>
		/// Internal member storing the read information types
		/// </summary>
		private ArrayList _informationTypes = new ArrayList();
		/// <summary>
		/// Internal member storing the read categories
		/// </summary>
		private ArrayList _categories = new ArrayList();
		/// <summary>
		/// Internal arraylist containing the index (klinks)
		/// </summary>
		private ArrayList _indexKLinks = new ArrayList();
		/// <summary>
		/// Internal arraylist containing the index (alinks)
		/// </summary>
		private ArrayList _indexALinks = new ArrayList();
		/// <summary>
		/// Internal member storing the full filename
		/// </summary>
		private string _chmFileName = "";
		/// <summary>
		/// Internal member storing the full filename of the chi-file (includes all system files)
		/// The file name is zero-length if there is no chi-file
		/// </summary>
		private string _chiFileName = "";
		/// <summary>
		/// Internal member storing the full filename of the chw-file (includes the help index)
		/// The file name is zero-length if there is no chw-file
		/// </summary>
		private string _chwFileName = "";
		/// <summary>
		/// Internal member storing the full filename of the chq-file (includes the fulltext contents)
		/// The file name is zero-length if there is no chq-file
		/// </summary>
		private string _chqFileName = "";
		/// <summary>
		/// Internal member storing the decoded information from the internal #SYSTEM file
		/// </summary>
		private CHMSystem _systemFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal #IDXHDR file
		/// </summary>
		private CHMIdxhdr _idxhdrFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal #STRINGS file
		/// </summary>
		private CHMStrings _stringsFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal #URLSTR file
		/// </summary>
		private CHMUrlstr _urlstrFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal #URLTBL file
		/// </summary>
		private CHMUrltable _urltblFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal #TOPICS file
		/// </summary>
		private CHMTopics _topicsFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal #TOCIDX file
		/// </summary>
		private CHMTocidx _tocidxFile = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal binary index file (KLinks).
		/// </summary>
		private CHMBtree _kLinks = null;
		/// <summary>
		/// Internal member storing the decoded information from the internal binary index file (ALinks).
		/// </summary>
		private CHMBtree _aLinks = null;
		/// <summary>
		/// Internal member storing the fulltext searcher for this file
		/// </summary>
		private FullTextEngine _ftSearcher = null;
		/// <summary>
		/// Internal member storing the default encoder
		/// </summary>
		private Encoding _textEncoding = Encoding.GetEncoding(1252); // standard windows-1252 encoder
		/// <summary>
		/// Internal memebr storing the chm file info
		/// </summary>
		private ChmFileInfo _chmFileInfo = null;
		/// <summary>
		/// Internal flag specifying if the dump must be written (if enabled)
		/// </summary>
		private bool _mustWriteDump = false;
		/// <summary>
		/// Internal flag specifying if data was read using the dump
		/// </summary>
		private bool _dumpRead = false;
		/// <summary>
		/// Internal member for specifying the number of dump-reading trys.
		/// If dump-reading fails, this is used that it will not be opened a second time
		/// (in CHM-Systems with CHM, CHI, etc. files)
		/// </summary>
		private int _dumpReadTrys = 0;
		/// <summary>
		/// Internal member storing the dumping info instance
		/// </summary>
		private DumpingInfo _dmpInfo = null;

		private CHMStream.CHMStream _currentWrapper;
		private CHMStream.CHMStream _baseStream=null;
		public CHMStream.CHMStream BaseStream
		{
			get 
			{ 
				if (_baseStream==null)
					_baseStream=new CHMStream.CHMStream(this.ChmFilePath);
				return _baseStream; 
			}
		}

		/// <summary>
		/// Creates a new instance of the class
		/// </summary>
		/// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>
		/// <param name="chmFile">chm file to read</param>
		public CHMFile(HtmlHelpSystem systemInstance, string chmFile) : this(systemInstance, chmFile, false, null)
		{
		}

		/// <summary>
		/// Creates a new instance of the class
		/// </summary>
		/// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>
		/// <param name="chmFile">chm file to read</param>
		/// <param name="dmpInfo">A dumping info class</param>
		public CHMFile(HtmlHelpSystem systemInstance, string chmFile, DumpingInfo dmpInfo) : this(systemInstance, chmFile, false, dmpInfo)
		{
		}

		/// <summary>
		/// Creates a new instance of the class
		/// </summary>
		/// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>
		/// <param name="chmFile">chm file to read</param>
		/// <param name="onlySystemData">true if only system data should be extracted (no index or toc)</param>
		internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData) : this(systemInstance, chmFile, onlySystemData, null)
		{
		}

		/// <summary>
		/// Creates a new instance of the class
		/// </summary>
		/// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>
		/// <param name="chmFile">chm file to read</param>
		/// <param name="onlySystemData">true if only system data should be extracted (no index or toc)</param>
		/// <param name="dmpInfo">A dumping info class</param>
		internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData, DumpingInfo dmpInfo)
		{
			_systemInstance = systemInstance;
			_dumpReadTrys=0;

			_dmpInfo = dmpInfo;
			if(dmpInfo != null)
			{
				dmpInfo.ChmFile = this;
			}

			if( ! chmFile.ToLower().EndsWith(".chm") )
			{
				throw new ArgumentException("HtmlHelp file must have the extension .chm !", "chmFile");
			}

			_chmFileName = chmFile;
			_chiFileName = "";

			// Read the IStorage file system
			if( File.Exists(chmFile) )
			{
				_onlySystem = onlySystemData;
				
				DateTime dtStartHH = DateTime.Now;

				string sCHIName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chi";
				string sCHQName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chq";
				string sCHWName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chw";

				// If there is a CHI file present (this file includes the internal system files for the current chm file)
				if( File.Exists(sCHIName) )
				{
					_chiFileName = sCHIName;
				
					ReadFile(_chiFileName, HtmlHelpFileType.CHI);
				}

				// If there is a CHW file present (this file includes the internal binary index of the help)
				if(( File.Exists(sCHWName) ) && (!_onlySystem) )
				{
					_chwFileName = sCHWName;
				
					ReadFile(_chwFileName, HtmlHelpFileType.CHW);
				}

				// If there is a CHQ file present (this file includes the fulltext-search data)
				if(( File.Exists(sCHQName) ) && (!_onlySystem) )
				{
					_chqFileName = sCHQName;
				
					ReadFile(_chqFileName, HtmlHelpFileType.CHQ);
				}

				ReadFile(chmFile, HtmlHelpFileType.CHM);

				if(_mustWriteDump)
				{					
					_mustWriteDump = !SaveDump(dmpInfo);

				}

				// check the default-topic setting
				if(_systemFile.DefaultTopic.Length > 0)
				{
					CHMStream.CHMStream iw=null;
					iw = new CHMStream.CHMStream(chmFile);
					_currentWrapper=iw;

					// tryo to open the topic file
					MemoryStream fileObject = iw.OpenStream( _systemFile.DefaultTopic);
					if( fileObject != null)
					{
						// if succeed, the topic default topic is OK
						fileObject.Close();
					} 
					else 
					{
						// set the first topic of the toc-tree as default topic
						if(_toc.Count > 0)
						{
							_systemFile.SetDefaultTopic( ((TOCItem) _toc[0]).Local );
						}
					}
					_currentWrapper=null;
				} 
				else 
				{
					// set the first topic of the toc-tree as default topic
					if(_toc.Count > 0)
					{
						_systemFile.SetDefaultTopic( ((TOCItem) _toc[0]).Local );
					}
				}

				_chmFileInfo = new ChmFileInfo(this);
			} 
			else 
			{
				throw new ArgumentException("File '" + chmFile + "' not found !", "chmFile");
			}
		}

		/// <summary>
		/// Read a IStorage file
		/// </summary>
		/// <param name="fname">filename</param>
		/// <param name="type">type of file</param>
		private void ReadFile(string fname, HtmlHelpFileType type)
		{
			CHMStream.CHMStream iw=null;
			iw=new CHMStream.CHMStream();			
			iw.OpenCHM(fname);
			_currentWrapper=iw;
			MemoryStream fileObject=null;			

			// ITStorageWrapper iw = null;

			// Open the internal chm system files and parse their content
			// FileObject fileObject = null;
			// iw = new ITStorageWrapper(fname, false);			

			if( (type != HtmlHelpFileType.CHQ) && (type != HtmlHelpFileType.CHW) )
			{				
				fileObject = iw.OpenStream("#SYSTEM");
				if ((fileObject != null) && (fileObject.Length>0))
					_systemFile = new CHMSystem(fileObject.ToArray(), this);									

				fileObject = iw.OpenStream("#IDXHDR");
				if ((fileObject != null) && (fileObject.Length>0))
					_idxhdrFile = new CHMIdxhdr(fileObject.ToArray(), this);					
				
				// try to read Dump
				if((!_dumpRead)&&(CheckDump(_dmpInfo))&&(_dumpReadTrys==0))
				{
					_dumpReadTrys++;
					_dumpRead = LoadDump(_dmpInfo);					
				}

				if( (!_dumpRead)||(!_dmpInfo.DumpStrings) )
				{
					fileObject = iw.OpenStream( "#STRINGS");
					if ((fileObject != null) && (fileObject.Length>0))
						_stringsFile = new CHMStrings(fileObject.ToArray(), this);
				}

				if( (!_dumpRead)||(!_dmpInfo.DumpUrlStr) )
				{
					fileObject = iw.OpenStream( "#URLSTR");
					if ((fileObject != null) && (fileObject.Length>0))
						_urlstrFile = new CHMUrlstr(fileObject.ToArray(), this);
				}

				if( (!_dumpRead)||(!_dmpInfo.DumpUrlTbl) )
				{
					fileObject = iw.OpenStream( "#URLTBL");
					if ((fileObject != null) && (fileObject.Length>0))
						_urltblFile = new CHMUrltable(fileObject.ToArray(), this);
				}

				if( (!_dumpRead)||(!_dmpInfo.DumpTopics) )
				{
					fileObject = iw.OpenStream( "#TOPICS");
					if ((fileObject != null) && (fileObject.Length>0))
						_topicsFile = new CHMTopics(fileObject.ToArray(), this);
				}

				if(!_onlySystem)
				{
					if( (!_dumpRead)||(!_dmpInfo.DumpBinaryTOC) )
					{
						fileObject = iw.OpenStream( "#TOCIDX");
						if( (fileObject != null) && (fileObject.Length>0) && (_systemFile.BinaryTOC) )
						{
							_tocidxFile = new CHMTocidx(fileObject.ToArray(), this);
							_toc = _tocidxFile.TOC;							
						}
					}
				}

				if( (_systemFile != null) && (!_onlySystem) )
				{
					if(!_systemFile.BinaryTOC)
					{
						if( (!_dumpRead)||(!_dmpInfo.DumpTextTOC) )
						{
							CHMStream.chmUnitInfo HHCInfo=iw.GetFileInfoByExtension(".hhc");
							if (HHCInfo!=null)
							{
								fileObject = iw.OpenStream(HHCInfo);							
								if ((fileObject != null) && (fileObject.Length>0))
								{
									ASCIIEncoding ascii=new ASCIIEncoding();			
									string fileString =ascii.GetString(fileObject.ToArray(),0,(int)fileObject.Length);

									_toc = HHCParser.ParseHHC(fileString, this);
								}

								if(HHCParser.HasMergeLinks)
									_mergeLinks = HHCParser.MergeItems;
							}							
						}
					} 
				}
			}

			if( type != HtmlHelpFileType.CHQ ) // no index information in CHQ files (only fulltext search)
			{
				if( (_systemFile != null) && (!_onlySystem) )
				{
					if( ! _systemFile.BinaryIndex )
					{
						if( (!_dumpRead)||(!_dmpInfo.DumpTextIndex) )
						{
							fileObject = iw.OpenStream( _systemFile.IndexFile);
							if ((fileObject != null) && (fileObject.Length>0))
							{

								string fileString = this.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
								// string fileString = fileObject.ReadFromFile(this.TextEncoding);
								fileObject.Close();

								_indexKLinks = HHKParser.ParseHHK(fileString, this);
								_indexKLinks.Sort();
							}
						}
					} 
					else 
					{
						if( (!_dumpRead)||(!_dmpInfo.DumpBinaryIndex) )
						{
							fileObject=iw.OpenStream(@"$WWKeywordLinks\BTree");
							if ((fileObject != null) && (fileObject.Length>0))
							{
								_kLinks = new CHMBtree(fileObject.ToArray(), this);
								_indexKLinks = _kLinks.IndexList;
							}

							fileObject =iw.OpenStream(@"$WWAssociativeLinks\BTree");
							if ((fileObject != null) && (fileObject.Length>0))
							{									
								_aLinks = new CHMBtree(fileObject.ToArray(), this);
								_indexALinks = _aLinks.IndexList;
								_indexALinks.Sort();									
							}							
						}
					}
				}
			}

			if( (type != HtmlHelpFileType.CHI) && (type != HtmlHelpFileType.CHW) )
			{
				if( (_systemFile != null) && (!_onlySystem) )
				{
					if( _systemFile.FullTextSearch)
					{
						if( (!_dumpRead)||(!_dmpInfo.DumpFullText) )
						{
							fileObject = iw.OpenStream("$FIftiMain");
							if(( fileObject != null) && (fileObject .Length>0))
								_ftSearcher = new FullTextEngine(fileObject .ToArray(), this);							
						}
					}
				}
			}
			_currentWrapper=null;
		}

		/// <summary>
		/// Enumerates the files in the chm storage and gets all files matching a given extension.
		/// </summary>
		/// <param name="extension">extension to return</param>
		/// <returns>Returns an arraylist of filenames or null if nothing found</returns>
		/// <remarks>On large CHMs, enumerations are very slow. Only use it if necessary !</remarks>
		internal ArrayList EnumFilesByExtension(string extension)
		{
			ArrayList arrRet = new ArrayList();

			CHMStream.CHMStream iw = null;
			if(_currentWrapper == null)
				iw = new CHMStream.CHMStream(_chmFileName);
			else
				iw = _currentWrapper;

			arrRet=iw.GetFileListByExtenstion(extension);
			
			if(arrRet.Count > 0)
				return arrRet;

			return null;
		}

		/// <summary>
		/// Searches an TOC entry using the local
		/// </summary>
		/// <param name="local">local to search</param>
		/// <returns>Returns the TOC item</returns>
		internal TOCItem GetTOCItemByLocal(string local)
		{
			return GetTOCItemByLocal(this.TOC, local);
		}

		/// <summary>
		/// Recursively searches an TOC entry using its local
		/// </summary>
		/// <param name="arrTOC">toc level list</param>
		/// <param name="local">local to search</param>
		/// <returns>Returns the TOC item</returns>
		private TOCItem GetTOCItemByLocal(ArrayList arrTOC, string local)
		{
			TOCItem ret = null;
			foreach(TOCItem curItem in arrTOC)
			{
				string scL = curItem.Local.ToLower();
				string sL = local.ToLower();

				while(scL[0]=='/') // delete prefixing '/'
					scL = scL.Substring(1);

				while(sL[0]=='/') // delete prefixing '/'
					sL = sL.Substring(1);

				if(scL == sL)
					return curItem;

				if(curItem.Children.Count > 0)
				{
					ret =  GetTOCItemByLocal(curItem.Children, local);
					if(ret != null)
						return ret;
				}
			}

			return ret;
		}

		/// <summary>
		/// Removes a TOCItem from the toc
		/// </summary>
		/// <param name="rem">item to remove</param>
		/// <returns>Returns true if removed</returns>
		internal bool RemoveTOCItem(TOCItem rem)
		{
			if(rem == null)
				return false;

			return RemoveTOCItem(this.TOC, rem);
		}

		/// <summary>
		/// Recursively searches a TOCItem and removes it if found
		/// </summary>
		/// <param name="arrTOC">toc level list</param>
		/// <param name="rem">item to remove</param>
		/// <returns>Returns true if removed</returns>
		private bool RemoveTOCItem(ArrayList arrTOC, TOCItem rem)
		{
			for(int i=0; i<arrTOC.Count;i++)
			{
				TOCItem curItem = arrTOC[i] as TOCItem;

				if(curItem == rem)
				{
					arrTOC.RemoveAt(i);
					return true;
				}

				if(curItem.Children.Count > 0)
				{
					bool bRem = RemoveTOCItem(curItem.Children, rem);
					if(bRem)
						return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Returns true if the HtmlHelpSystem instance contains 1 or more information types
		/// </summary>
		public bool HasInformationTypes
		{
			get { return (_informationTypes.Count>0); }
		}

		/// <summary>
		/// Returns true if the HtmlHelpSystem instance contains 1 or more categories
		/// </summary>
		public bool HasCategories
		{
			get { return (_categories.Count>0); }
		}

		/// <summary>
		/// Gets an ArrayList of <see cref="InformationType">InformationType</see> items
		/// </summary>
		public ArrayList InformationTypes
		{
			get { return _informationTypes; }
		}

		/// <summary>
		/// Gets an ArrayList of <see cref="Category">Category</see> items
		/// </summary>
		public ArrayList Categories
		{
			get { return _categories; }
		}

		/// <summary>
		/// Gets the information type specified by its name
		/// </summary>
		/// <param name="name">name of the information type to receive</param>
		/// <returns>Returns the Instance for the name or null if not found</returns>
		public InformationType GetInformationType(string name)
		{
			if(HasInformationTypes)
			{
				for(int i=0; i<_informationTypes.Count;i++)
				{
					InformationType iT = _informationTypes[i] as InformationType;

					if(iT.Name == name)
						return iT;
				}
			}

			return null;
		}

		/// <summary>
		/// Gets the category specifiyd by its name
		/// </summary>
		/// <param name="name">name of the category</param>
		/// <returns>Returns the Instance for the name or null if not found</returns>
		public Category GetCategory(string name)
		{
			if(HasCategories)
			{
				for(int i=0; i<_categories.Count;i++)
				{
					Category cat = _categories[i] as Category;

					if(cat.Name == name)
						return cat;
				}
			}

			return null;
		}

		#region Dumping methods
		
		/// <summary>
		/// Checks if a dump for this file exists and if it can be read
		/// </summary>
		/// <param name="dmpInfo">dumping info class</param>
		/// <returns>true if it can be read</returns>
		private bool CheckDump(DumpingInfo dmpInfo)
		{
			if(_dumpReadTrys<=0)
				_mustWriteDump = false;

			if(_onlySystem)
				return false;

			if( dmpInfo != null )
			{
				if(_dumpReadTrys > 0)
					return _mustWriteDump;

				_mustWriteDump = !dmpInfo.DumpExists;
				return !_mustWriteDump;
			}

			return false;
		}

		/// <summary>
		/// Saves the the toc and index into a data dump
		/// </summary>
		/// <param name="dmpInfo">dumping info</param>
		/// <returns>true if succeed</returns>
		private bool SaveDump(DumpingInfo dmpInfo)
		{
			if(dmpInfo == null)
				return false;

			bool bRet = false;


			BinaryWriter writer = dmpInfo.Writer;

			int nCnt = 0;
			try
			{
				Debug.WriteLine("writing dump-file header");
				FileInfo fi = new FileInfo(_chmFileName);
				string ftime = fi.LastWriteTime.ToString("dd.MM.yyyy HH:mm:ss.ffffff");

				writer.Write("HtmlHelpSystem dump file 1.0");
				writer.Write(ftime);
				writer.Write(_textEncoding.CodePage);

				// strings dumping
				if(dmpInfo.DumpStrings)
				{
					writer.Write(true); // data should be in dump

					if(_stringsFile==null)
					{
						writer.Write(false); // data not supported by the chm
					} 
					else 
					{
						Debug.WriteLine("writing #STRINGS");
						writer.Write(true); // data supported and following
						_stringsFile.Dump(ref writer);
					}
				} 
				else 
				{
					writer.Write(false); // data is not in dump
				}

				// urlstr dumping
				if(dmpInfo.DumpUrlStr)
				{
					writer.Write(true);

					if(_urlstrFile==null)
					{
						writer.Write(false);
					} 
					else 
					{
						Debug.WriteLine("writing #URLSTR");
						writer.Write(true);
						_urlstrFile.Dump(ref writer);
					}
				} 
				else 
				{
					writer.Write(false);
				}

				// urltbl dumping
				if(dmpInfo.DumpUrlTbl)
				{
					writer.Write(true);

					if(_urltblFile==null)
					{
						writer.Write(false);
					} 
					else 
					{
						Debug.WriteLine("writing #URLTBL");
						writer.Write(true);
						_urltblFile.Dump(ref writer);
					}
				} 
				else 
				{
					writer.Write(false);
				}

				// topics dumping
				if(dmpInfo.DumpTopics)
				{
					writer.Write(true);

					if(_topicsFile==null)
					{
						writer.Write(false);
					} 
					else 
					{
						Debug.WriteLine("writing #TOPICS");
						writer.Write(true);
						_topicsFile.Dump(ref writer);
					}
				} 
				else 
				{
					writer.Write(false);
				}

				// ftsearch dumping
				if(dmpInfo.DumpFullText)
				{
					writer.Write(true);

					if(_ftSearcher==null)
					{
						writer.Write(false);
					} 
					else 
					{
						Debug.WriteLine("writing $FIftiMain");
						writer.Write(true);
						_ftSearcher.Dump(ref writer);
					}
				} 
				else 
				{
					writer.Write(false);
				}

				// TOC dumping
				bool bWriteTOC = false;

				if( (_systemFile.BinaryTOC) && (dmpInfo.DumpBinaryTOC) )
				{
					Debug.WriteLine("writing binary TOC");
					bWriteTOC = true;
				}

				if( (!_systemFile.BinaryTOC) && (dmpInfo.DumpTextTOC) )
				{
					Debug.WriteLine("writing text-based TOC");
					bWriteTOC = true;
				}

				writer.Write(bWriteTOC);

				if(bWriteTOC)
				{
					// write table of contents
					writer.Write( _toc.Count );

					for(nCnt=0; nCnt < _toc.Count; nCnt++)
					{
						TOCItem curItem = ((TOCItem)(_toc[nCnt]));
						curItem.Dump( ref writer );
					}
				}

				// Index dumping
				bool bWriteIdx = false;

				if( (_systemFile.BinaryIndex) && (dmpInfo.DumpBinaryIndex) )
				{
					Debug.WriteLine("writing binary index");
					bWriteIdx = true;
				}

				if( (!_systemFile.BinaryIndex) && (dmpInfo.DumpTextIndex) )
				{
					Debug.WriteLine("writing text-based index");
					bWriteIdx = true;
				}

				writer.Write(bWriteIdx);

				if(bWriteIdx)
				{
					// write index
					writer.Write( _indexALinks.Count );
					for(nCnt=0; nCnt < _indexALinks.Count; nCnt++)
					{
						IndexItem curItem = ((IndexItem)(_indexALinks[nCnt]));
						curItem.Dump( ref writer );
					}

					writer.Write( _indexKLinks.Count );
					for(nCnt=0; nCnt < _indexKLinks.Count; nCnt++)
					{
						IndexItem curItem = ((IndexItem)(_indexKLinks[nCnt]));
						curItem.Dump( ref writer );
					}
				}

				// Information types dumping
				writer.Write( _informationTypes.Count );

				Debug.WriteLine("writing " + _informationTypes.Count.ToString() + " information types");

				for(nCnt=0; nCnt<_informationTypes.Count;nCnt++)
				{
					InformationType curType = _informationTypes[nCnt] as InformationType;
					
					curType.Dump(ref writer);
				}

				// Categories dumping
				writer.Write( _categories.Count );

				Debug.WriteLine("writing " + _categories.Count.ToString() + " categories");

				for(nCnt=0; nCnt<_categories.Count; nCnt++)
				{
					Category curCat = _categories[nCnt] as Category;

					curCat.Dump( ref writer);
				}

				bRet=true;
			}
			finally
			{
				dmpInfo.SaveData();
			}

			return bRet;
		}

		/// <summary>
		/// Parses a HHC file which is located in the current CHM.
		/// </summary>
		/// <param name="hhcFile">hhc file to parse</param>
		/// <returns>an arraylist with toc items</returns>
		public ArrayList ParseHHC(string hhcFile)
		{
			ArrayList arrRet = new ArrayList();
			
			CHMStream.CHMStream iw=null;
			iw=new CHMStream.CHMStream();			
			iw.OpenCHM(_chmFileName);			
			MemoryStream fileObject=null;			

			fileObject = iw.OpenStream(hhcFile);
			if( fileObject != null)
			{	
				ASCIIEncoding ascii=new ASCIIEncoding();			
				string fileString =ascii.GetString(fileObject.ToArray(),0,(int)fileObject.Length);				
				fileObject.Close();

				arrRet = HHCParser.ParseHHC(fileString, this);

				if(HHCParser.HasMergeLinks)
				{
					foreach(TOCItem curItem in HHCParser.MergeItems)
					{
						_mergeLinks.Add(curItem);
					}
				}
			}

			return arrRet;
		}

		/// <summary>
		/// Loads the toc and index from a data dump
		/// </summary>
		/// <param name="dmpInfo">dumping info</param>
		/// <returns>true if succeed</returns>
		private bool LoadDump(DumpingInfo dmpInfo)
		{
			if(dmpInfo == null)
				return false;

			bool bRet = false;

			try
			{
				BinaryReader reader = dmpInfo.Reader;

				if(reader == null)
				{
					Debug.WriteLine("No reader returned !");
					dmpInfo.SaveData(); // closes the dump
					_mustWriteDump = true;
					return false;
				}

				int nCnt = 0;

				Debug.WriteLine("reading dump-file header");
				FileInfo fi = new FileInfo(_chmFileName);
				string ftime = fi.LastWriteTime.ToString("dd.MM.yyyy HH:mm:ss.ffffff");

				string header = reader.ReadString();

				if( header != "HtmlHelpSystem dump file 1.0")
				{
					Debug.WriteLine("Unsupported dump-file format !");
					dmpInfo.SaveData(); // closes the dump
					_mustWriteDump = true;
					return false;
				}

				string ftimecheck = reader.ReadString();

				reader.ReadInt32(); // codepage, we'll use the same as for the chm file which is already set.

//				if(ftimecheck != ftime)
//				{
//					Debug.WriteLine("Dump is out of date (CHM file changed during last dump creation) !");
//					dmpInfo.SaveData(); // closes the dump
//					_mustWriteDump = true;
//					return false; // force reload
//				}
				

				bool bFlag=false;  // true if data should be in dump
				bool bFlagSupp=false; // false if data is not supported by the chm

				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					bFlagSupp = reader.ReadBoolean();

					if(!dmpInfo.DumpStrings)
					{
						Debug.WriteLine("Dumped #STRINGS found but not expected !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(bFlagSupp)
					{
						Debug.WriteLine("reading #STRINGS");
						_stringsFile = new CHMStrings();
						_stringsFile.SetCHMFile(this);
						_stringsFile.ReadDump(ref reader);
					}
				} 
				else 
				{
					if(dmpInfo.DumpStrings)
					{
						Debug.WriteLine("Dumped #STRINGS expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					bFlagSupp = reader.ReadBoolean();

					if(!dmpInfo.DumpUrlStr)
					{
						Debug.WriteLine("Dumped #URLSTR found but not expected !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(bFlagSupp)
					{
						Debug.WriteLine("reading #URLSTR");
						_urlstrFile = new CHMUrlstr();
						_urlstrFile.SetCHMFile(this);
						_urlstrFile.ReadDump(ref reader);
					}
				}
				else 
				{
					if(dmpInfo.DumpUrlStr)
					{
						Debug.WriteLine("Dumped #URLSTR expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					bFlagSupp = reader.ReadBoolean();

					if(!dmpInfo.DumpUrlTbl)
					{
						Debug.WriteLine("Dumped #URLTBL found but not expected !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(bFlagSupp)
					{
						Debug.WriteLine("reading #URLTBL");
						_urltblFile = new CHMUrltable();
						_urltblFile.SetCHMFile(this);
						_urltblFile.ReadDump(ref reader);
					}
				}
				else 
				{
					if(dmpInfo.DumpUrlTbl)
					{
						Debug.WriteLine("Dumped #URLTBL expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					bFlagSupp = reader.ReadBoolean();

					if(!dmpInfo.DumpTopics)
					{
						Debug.WriteLine("Dumped #TOPICS found but not expected !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(bFlagSupp)
					{
						Debug.WriteLine("reading #TOPICS");
						_topicsFile = new CHMTopics();
						_topicsFile.SetCHMFile(this);
						_topicsFile.ReadDump(ref reader);
					}
				}
				else 
				{
					if(dmpInfo.DumpTopics)
					{
						Debug.WriteLine("Dumped #TOPICS expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					bFlagSupp = reader.ReadBoolean();

					if(!dmpInfo.DumpFullText)
					{
						Debug.WriteLine("Dumped $FIftiMain found but not expected !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(bFlagSupp)
					{
						Debug.WriteLine("reading $FIftiMain");
						_ftSearcher = new FullTextEngine();
						_ftSearcher.SetCHMFile(this);
						_ftSearcher.ReadDump(ref reader);
					}
				}
				else 
				{
					if(dmpInfo.DumpFullText)
					{
						Debug.WriteLine("Dumped $FIftiMain expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				// read table of contents
				_toc.Clear();
				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					if((_systemFile.BinaryTOC)&&(!dmpInfo.DumpBinaryTOC))
					{
						Debug.WriteLine("Binary TOC expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					} 

					if((!_systemFile.BinaryTOC)&&(!dmpInfo.DumpTextTOC))
					{
						Debug.WriteLine("Text-based TOC expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(_systemFile.BinaryTOC)
						Debug.WriteLine("reading binary TOC");
					else
						Debug.WriteLine("reading text-based TOC");

					int nTocCnt = reader.ReadInt32();

					for(nCnt=0; nCnt < nTocCnt; nCnt++)
					{
						TOCItem item = new TOCItem();
						item.AssociatedFile = this;
						item.ChmFile = _chmFileName;
						item.ReadDump(ref reader);
						if(item.MergeLink.Length > 0)
							_mergeLinks.Add(item);

						_toc.Add(item);
					}
				} 
				else 
				{
					if((_systemFile.BinaryTOC)&&(dmpInfo.DumpBinaryTOC))
					{
						Debug.WriteLine("Binary TOC expected but no TOC dump !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					} 

					if((!_systemFile.BinaryTOC)&&(dmpInfo.DumpTextTOC))
					{
						Debug.WriteLine("Text-based TOC expected but no TOC dump !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				// read index
				_indexALinks.Clear();
				_indexKLinks.Clear();

				bFlag = reader.ReadBoolean();

				if(bFlag)
				{
					if((_systemFile.BinaryIndex)&&(!dmpInfo.DumpBinaryIndex))
					{
						Debug.WriteLine("Binary index expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if((!_systemFile.BinaryIndex)&&(!dmpInfo.DumpTextIndex))
					{
						Debug.WriteLine("Binary index expected but not found !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}

					if(_systemFile.BinaryIndex)
						Debug.WriteLine("reading binary index");
					else
						Debug.WriteLine("reading text-based index");

					int nIndxaCnt = reader.ReadInt32();

					for(nCnt=0; nCnt < nIndxaCnt; nCnt++)
					{
						IndexItem item = new IndexItem();
						item.ChmFile = this;
						item.ReadDump(ref reader);
						_indexALinks.Add(item);
					}

				
					int nIndxkCnt = reader.ReadInt32();

					for(nCnt=0; nCnt < nIndxkCnt; nCnt++)
					{
						IndexItem item = new IndexItem();
						item.ChmFile = this;
						item.ReadDump(ref reader);
						_indexKLinks.Add(item);
					}
				}
				else 
				{
					if((_systemFile.BinaryIndex)&&(dmpInfo.DumpBinaryIndex))
					{
						Debug.WriteLine("Binary index expected but no index in dump !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					} 

					if((!_systemFile.BinaryIndex)&&(dmpInfo.DumpTextIndex))
					{
						Debug.WriteLine("Text-based index expected but no index dump !");
						dmpInfo.SaveData(); // closes the dump
						_mustWriteDump = true;
						return false; // force reload
					}
				}

				// read information types from dump
				int nITCnt = reader.ReadInt32();

				Debug.WriteLine("Reading " + nITCnt.ToString() + " information types from dump !");

				for(nCnt=0; nCnt<nITCnt; nCnt++)
				{
					InformationType newType = new InformationType();
					newType.ReadDump(ref reader);

					if( SystemInstance.GetInformationType( newType.Name ) != null)
					{
						// information type of this name already exists in the helpsystem
						InformationType sysType = SystemInstance.GetInformationType( newType.Name );
						_informationTypes.Add(sysType);
					} 
					else 
					{
						_informationTypes.Add( newType );
					}
				}

				// read categories from dump
				int nCCnt = reader.ReadInt32();

				Debug.WriteLine("Reading " + nITCnt.ToString() + " categories from dump !");

				for(nCnt=0; nCnt<nCCnt; nCnt++)
				{
					Category newCat = new Category();
					newCat.ReadDump(ref reader, this);

					if( SystemInstance.GetCategory( newCat.Name ) != null)
					{
						// category of this name already exists in the helpsystem
						Category sysCat = SystemInstance.GetCategory( newCat.Name );

						sysCat.MergeInfoTypes( newCat );
						_categories.Add( sysCat );
					} 
					else 
					{
						_categories.Add( newCat );
					}
				}

				_dumpRead = true;
				bRet = true;
			}
			catch(Exception ex)
			{
				Debug.WriteLine("###ERROR :" + ex.Message);
				_mustWriteDump = true;
			}
			finally
			{
				dmpInfo.SaveData(); // closes the dump
			}

			return bRet;
		}

		#endregion

		#region Internal properties
		/// <summary>
		/// Gets the current storage wrapper.
		/// </summary>
		/// <remarks>This property will return not null, if there are currently file read actions running !</remarks>
		internal CHMStream.CHMStream CurrentStorageWrapper
		{
			get { return _currentWrapper;}
		}
		/// <summary>
		/// Gets/sets the hosting HtmlHelpSystem instance
		/// </summary>
		internal HtmlHelpSystem SystemInstance
		{
			get { return _systemInstance; }
			set { _systemInstance = value; }
		}
		/// <summary>
		/// Gets an arraylist of TOC items which contains merg-links to other CHMs
		/// </summary>
		internal ArrayList MergLinks
		{
			get { return _mergeLinks; }
		}

		/// <summary>
		/// Gets the internal system file instance
		/// </summary>
		internal CHMSystem SystemFile
		{
			get { return _systemFile; }
		}
		
		/// <summary>
		/// Gets the internal idxhdr file instance
		/// </summary>
		internal CHMIdxhdr IdxHdrFile
		{
			get { return _idxhdrFile; }
		}

		/// <summary>
		/// Gets the internal strings file instance
		/// </summary>
		internal CHMStrings StringsFile
		{
			get { return _stringsFile; }
		}
		
		/// <summary>
		/// Gets the internal urlstr file instance
		/// </summary>
		internal CHMUrlstr UrlstrFile
		{
			get { return _urlstrFile; }
		}

		/// <summary>
		/// Gets the internal urltbl file instance
		/// </summary>
		internal CHMUrltable UrltblFile
		{
			get { return _urltblFile; }
		}

		/// <summary>
		/// Gets the internal topics file instance
		/// </summary>
		internal CHMTopics TopicsFile
		{
			get { return _topicsFile; }
		}

		/// <summary>
		/// Gets the internal tocidx file instance
		/// </summary>
		internal CHMTocidx TocidxFile
		{
			get { return _tocidxFile; }
		}

		/// <summary>
		/// Gets the internal btree file instance for alinks
		/// </summary>
		internal CHMBtree ALinksFile
		{
			get { return _aLinks; }
		}

		/// <summary>
		/// Gets the internal btree file instance for klinks
		/// </summary>
		internal CHMBtree KLinksFile
		{
			get { return _kLinks; }
		}

		/// <summary>
		/// Gets/Sets the text encoding
		/// </summary>
		internal Encoding TextEncoding
		{
			get { return _textEncoding; }
			set { _textEncoding = value; }
		}

		#endregion

		#region Properties from the #SYSTEM file
		/// <summary>
		/// Gets the file version of the chm file. 
		/// 2 for Compatibility=1.0,  3 for Compatibility=1.1
		/// </summary>
		internal int FileVersion
		{
			get 
			{ 
				if(_systemFile==null)
					return 0;

				return _systemFile.FileVersion; 
			}
		}

		/// <summary>
		/// Gets the contents file name
		/// </summary>
		internal string ContentsFile
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.ContentsFile; 
			}
		}

		/// <summary>
		/// Gets the index file name
		/// </summary>
		internal string IndexFile
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.IndexFile; 
			}
		}

		/// <summary>
		/// Gets the default help topic
		/// </summary>
		internal string DefaultTopic
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.DefaultTopic; 
			}
		}

		/// <summary>
		/// Gets the title of the help window
		/// </summary>
		internal string HelpWindowTitle
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.Title; 
			}
		}

		/// <summary>
		/// Gets the flag if DBCS is in use
		/// </summary>
		internal bool DBCS
		{
			get 
			{ 
				if(_systemFile==null)
					return false;

				return _systemFile.DBCS; 
			}
		}

		/// <summary>
		/// Gets the flag if full-text-search is available
		/// </summary>
		internal bool FullTextSearch
		{
			get 
			{ 
				if(_systemFile==null)
					return false;

				return _systemFile.FullTextSearch; 
			}
		}

		/// <summary>
		/// Gets the flag if the file has ALinks
		/// </summary>
		internal bool HasALinks
		{
			get 
			{ 
				if(_systemFile==null)
					return false;

				return _systemFile.HasALinks; 
			}
		}

		/// <summary>
		/// Gets the flag if the file has KLinks
		/// </summary>
		internal bool HasKLinks
		{
			get 
			{ 
				if(_systemFile==null)
					return false;

				return _systemFile.HasKLinks; 
			}
		}

		/// <summary>
		/// Gets the default window name
		/// </summary>
		internal string DefaultWindow
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.DefaultWindow; 
			}
		}

		/// <summary>
		/// Gets the file name of the compile file
		/// </summary>
		internal string CompileFile
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.CompileFile; 
			}
		}

		/// <summary>
		/// Gets the flag if the chm has a binary index file
		/// </summary>
		internal bool BinaryIndex
		{
			get 
			{ 
				if(_systemFile==null)
					return false;

				return _systemFile.BinaryIndex; 
			}
		}

		/// <summary>
		/// Gets the flag if the chm has a binary index file
		/// </summary>
		internal string CompilerVersion
		{
			get 
			{ 
				if(_systemFile==null)
					return String.Empty;

				return _systemFile.CompilerVersion; 
			}
		}

		/// <summary>
		/// Gets the flag if the chm has a binary toc file
		/// </summary>
		internal bool BinaryTOC
		{
			get 
			{ 
				if(_systemFile==null)
					return false;

				return _systemFile.BinaryTOC; 
			}
		}

		/// <summary>
		/// Gets the font face of the read font property.
		/// Empty string for default font.
		/// </summary>
		internal string FontFace
		{
			get
			{
				if(_systemFile==null)
					return "";
					
				return _systemFile.FontFace; 
			}
		}

		/// <summary>
		/// Gets the font size of the read font property.
		/// 0 for default font size
		/// </summary>
		internal double FontSize
		{
			get
			{
				if(_systemFile==null)
					return 0;
					
				return _systemFile.FontSize; 
			}
		}

		/// <summary>
		/// Gets the character set of the read font property
		/// 1 for default
		/// </summary>
		internal int CharacterSet
		{
			get
			{
				if(_systemFile==null)
					return 1;
					
				return _systemFile.CharacterSet; 
			}
		}

		/// <summary>
		/// Gets the codepage depending on the read font property
		/// </summary>
		internal int CodePage
		{
			get
			{
				if(_systemFile==null)
					return CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
					
				return _systemFile.CodePage; 
			}
		}

		/// <summary>
		/// Gets the assiciated culture info
		/// </summary>
		internal CultureInfo Culture
		{
			get 
			{ 
				if(_systemFile==null)
					return CultureInfo.CurrentCulture;
					
				return _systemFile.Culture; 
			}
		}

		#endregion 

		#region Properties from the #IDXHDR file
		/// <summary>
		/// Gets the number of topic nodes including the contents and index files
		/// </summary>
		internal int NumberOfTopicNodes
		{
			get 
			{
				if(_idxhdrFile==null)
					return 0;

				return _idxhdrFile.NumberOfTopicNodes; 
			}
		}

		/// <summary>
		/// Gets the ImageList string specyfied in the #IDXHDR file.
		/// </summary>
		/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
		internal string ImageList
		{
			get 
			{
				if( (_stringsFile == null) || (_idxhdrFile == null) )
					return "";

				return _stringsFile[ _idxhdrFile.ImageListOffset ];
			}
		}

		/// <summary>
		/// True if the value of the ImageType param of the 
		/// "text/site properties" object of the sitemap contents is "Folder". 
		/// </summary>
		/// <remarks>If this is set to true, the help will display folders instead of books</remarks>
		internal bool ImageTypeFolder
		{
			get 
			{ 
				if(_idxhdrFile==null)
					return false;

				return _idxhdrFile.ImageTypeFolder; 
			}
		}

		/// <summary>
		/// Gets the background setting 
		/// </summary>
		internal int Background
		{
			get 
			{ 
				if(_idxhdrFile==null)
					return 0;

				return _idxhdrFile.Background; 
			}
		}

		/// <summary>
		/// Gets the foreground setting 
		/// </summary>
		internal int Foreground
		{
			get 
			{ 
				if(_idxhdrFile==null)
					return 0;

				return _idxhdrFile.Foreground; 
			}
		}

		/// <summary>
		/// Gets the Font string specyfied in the #IDXHDR file.
		/// </summary>
		/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
		internal string FontName
		{
			get 
			{
				if( (_stringsFile == null) || (_idxhdrFile == null) )
					return "";

				return _stringsFile[ _idxhdrFile.FontOffset ];
			}
		}

		/// <summary>
		/// Gets the FrameName string specyfied in the #IDXHDR file.
		/// </summary>
		/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
		internal string FrameName
		{
			get 
			{
				if( (_stringsFile == null) || (_idxhdrFile == null) )
					return "";

				return _stringsFile[ _idxhdrFile.FrameNameOffset ];
			}
		}

		/// <summary>
		/// Gets the WindowName string specyfied in the #IDXHDR file.
		/// </summary>
		/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
		internal string WindowName
		{
			get 
			{
				if( (_stringsFile == null) || (_idxhdrFile == null) )
					return "";

				return _stringsFile[ _idxhdrFile.WindowNameOffset ];
			}
		}

		/// <summary>
		/// Gets a string array containing the merged file names
		/// </summary>
		internal string[] MergedFiles
		{
			get
			{
				if( (_stringsFile == null) || (_idxhdrFile == null) )
					return new string[0];

				string[] saRet = new string[ _idxhdrFile.MergedFileOffsets.Count ];

				for(int i=0; i < _idxhdrFile.MergedFileOffsets.Count; i++)
				{
					saRet[i] = _stringsFile[ (int)_idxhdrFile.MergedFileOffsets[i] ];
				}

				return saRet;
			}
		}
		#endregion

		/// <summary>
		/// Gets the file info associated with this instance
		/// </summary>
		public ChmFileInfo FileInfo
		{
			get { return _chmFileInfo; }
		}

		/// <summary>
		/// Gets the internal toc read from the text-based hhc file
		/// </summary>
		public ArrayList TOC
		{
			get { return _toc; }
		}

		/// <summary>
		/// Gets the internal index read from the chm file.
		/// </summary>
		public ArrayList IndexKLinks
		{
			get { return _indexKLinks; }
		}

		/// <summary>
		/// Gets the internal index read from the chm file.
		/// </summary>
		public ArrayList IndexALinks
		{
			get { return _indexALinks; }
		}

		/// <summary>
		/// Gets the full-text search engine for this file
		/// </summary>
		internal FullTextEngine FullTextSearchEngine
		{
			get { return _ftSearcher; }
		}

		/// <summary>
		/// Gets the full pathname of the file
		/// </summary>
		public string ChmFilePath
		{
			get { return _chmFileName; }
		}

		/// <summary>
		/// Gets the full pathname of the chi-file
		/// The file name is zero-length if there is no chi-file
		/// </summary>
		public string ChiFilePath
		{
			get { return _chiFileName; }
		}

		/// <summary>
		/// Gets the full pathname of the chw-file
		/// The file name is zero-length if there is no chw-file
		/// </summary>
		public string ChwFilePath
		{
			get { return _chwFileName; }
		}

		/// <summary>
		/// Gets the full pathname of the chq-file
		/// The file name is zero-length if there is no chq-file
		/// </summary>
		public string ChqFilePath
		{
			get { return _chqFileName; }
		}

		/// <summary>
		/// Forms an URL for the web browser
		/// </summary>
		/// <param name="local">local resource</param>
		/// <returns>a url for the web-browser</returns>
		internal string FormURL(string local)
		{
			if( (local.ToLower().IndexOf("http://") >= 0) ||
				(local.ToLower().IndexOf("https://") >= 0) ||
				(local.ToLower().IndexOf("mailto:") >= 0) ||
				(local.ToLower().IndexOf("ftp://") >= 0)  ||
				(local.ToLower().IndexOf("ms-its:") >= 0))				
				return local;

			return HtmlHelpSystem.UrlPrefix + _chmFileName + "::/" + local;
		}

		/// <summary>
		/// Implement IDisposable.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			// This object will be cleaned up by the Dispose method.
			// Therefore, you should call GC.SupressFinalize to
			// take this object off the finalization queue 
			// and prevent finalization code for this object
			// from executing a second time.
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Dispose(bool disposing) executes in two distinct scenarios. 
		/// If disposing equals true, the method has been called directly 
		/// or indirectly by a user's code. Managed and unmanaged resources 
		/// can be disposed. 
		/// If disposing equals false, the method has been called by the 
		/// runtime from inside the finalizer and you should not reference  
		/// other objects. Only unmanaged resources can be disposed.
		/// </summary>
		/// <param name="disposing">disposing flag</param>
		private void Dispose(bool disposing)
		{
			// Check to see if Dispose has already been called.
			if(!this.disposed)
			{
				// If disposing equals true, dispose all managed 
				// and unmanaged resources.
				if(disposing)
				{
					// Dispose managed resources.
					_toc.Clear();
					_indexKLinks.Clear();
					_indexALinks.Clear();

					if(_systemFile!=null)
						_systemFile.Dispose();

					if(_idxhdrFile != null)
						_idxhdrFile.Dispose();

					if(_stringsFile != null)
						_stringsFile.Dispose();

					if(_urlstrFile != null)
						_urlstrFile.Dispose();

					if(_urltblFile != null)
						_urltblFile.Dispose();

					if(_topicsFile != null)
						_topicsFile.Dispose();

					if(_tocidxFile != null)
						_tocidxFile.Dispose();

					if(_kLinks != null)
						_kLinks.Dispose();

					if(_aLinks != null)
						_aLinks.Dispose();

					if(_ftSearcher != null)
						_ftSearcher.Dispose();

					if(_chmFileInfo != null)
						_chmFileInfo = null;
				}
			}
			disposed = true;         
		}

	}
}