using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;

namespace Facts
{
	public class Fact {
		[XmlAttribute] public string hide="";
		[XmlAttribute] public string wide="";
		[XmlAttribute] public string reference="";
		public string date="", text="", url="", urlText="", notes="", altUrls="", link="";

		public Fact(string date, string text, string url, string urlText, string link) {
			this.date    = date;
			this.text    = text;
			this.url     = url;
			this.urlText = urlText;
			this.link    = link;
		}
		public Fact() {}
	}
  
	
	public class Subtopic {
		[XmlAttribute] public string name="", link="", shortName="";
		[XmlAttribute] public string hide="";
		[XmlAttribute] public string forcebg="";
		[XmlAttribute] public string nocomment="";

		public Fact[] facts = new Fact[0];
		public Subtopic(string name, string link, string shortName, Fact[] facts) {
			this.shortName = shortName;
			this.name  = name;
			this.link  = link;
			this.facts = facts;
		}
		public Subtopic() {}
	}
	
	public class Topic {
		[XmlAttribute] public string name="";
		[XmlAttribute] public string hide="";

		public Subtopic[] subtopics = new Subtopic[0];
		public Topic(string name, Subtopic[] subtopics) {
			this.name      = name;
			this.subtopics = subtopics;
		}
		public Topic() {}
	}
	public class Supertopic {
		[XmlAttribute] public string name="";
		[XmlAttribute] public string image="";
		[XmlAttribute] public string imageheight="";
		[XmlAttribute] public string imagewidth="";
		public Topic[] topics = new Topic[0];
		public Supertopic() {}	
	}
	
	public class Facts {

		public Supertopic[] supertopics = new Supertopic[0];
		public Topic[] topics;
		//public string[] topics;

		public Facts(Topic[] topics) { this.topics = topics; }
		public Facts() {}

		static public Facts LoadFacts() { return LoadFacts("facts.xml"); }
		static public Facts LoadFacts(string filename) {
			
			// first preprocess file, replacing & with &amp; and < with &lt; where appropriate
			{
				FileStream   fs = new FileStream(filename, FileMode.Open);
				StreamReader sr = new StreamReader(fs);
				string data = sr.ReadToEnd();
				
				data = data.Replace("&", "&amp;");
				string[] tokens = { "td", "tr", "br", "table", "ul", "li", "b", "p", "i", "a", "!--", "script", "img", "span" };
				for(int i=0; i<tokens.Length; i++) {
					data = data.Replace("<" +tokens[i]+">", "&lt;" +tokens[i]+">");
					data = data.Replace("<" +tokens[i]+" ", "&lt;" +tokens[i]+" ");
					data = data.Replace("</"+tokens[i]+">", "&lt;/"+tokens[i]+">");

					tokens[i] = tokens[i].ToUpper();
					data = data.Replace("<" +tokens[i]+">", "&lt;" +tokens[i]+">");
					data = data.Replace("<" +tokens[i]+" ", "&lt;" +tokens[i]+" ");
					data = data.Replace("</"+tokens[i]+">", "&lt;/"+tokens[i]+">");
				}
	
				sr.Close();
				StreamWriter sw = new StreamWriter(filename+"-tmp");
				sw.WriteLine(data);
				sw.Close();			
			}			
			
			// now read xml into class
			Facts f = null;
			XmlSerializer serializer = new XmlSerializer(typeof(Facts));
			try {
				FileStream fs = new FileStream(filename+"-tmp", FileMode.Open);
				f = (Facts) serializer.Deserialize(fs);
				fs.Close();
			}
			catch(Exception e) {
				Console.Write("couldn't load facts from " + filename);
				Console.Write(e.Message);
				Console.Write(e.StackTrace);

				// create example
				Fact[]  facts  = new Fact[]  { new Fact ("July 1999", "example fact", "www.facts.org", "facts home", "") };
				f = new Facts(new Topic[] { new Topic("example topic", new Subtopic[] {new Subtopic("example subtopic", "", "", facts)}) });
			}
			
			
			
			// remove hidden topics + subtopics
			for(int st=0; st<f.supertopics.Length; st++) {
				ArrayList topics = new ArrayList(f.supertopics[st].topics);
				bool found = true;
				while(found) {
					found = false;
					foreach(Topic t in topics)
						if(t.hide.Trim().Length > 0) { topics.Remove(t); found = true; break; }
				}
	
				// remove hidden subtopics
				foreach(Topic t in topics) {
					ArrayList subtopics = new ArrayList(t.subtopics);
					found = true;
					while(found) {
						found = false;
						foreach(Subtopic stopic in subtopics)
							if(stopic.hide.Trim().Length > 0) {	subtopics.Remove(stopic);	found = true;	break; }
					}
					t.subtopics = (Subtopic[]) subtopics.ToArray(typeof(Subtopic));
				}
				f.supertopics[st].topics = (Topic[]) topics.ToArray(typeof(Topic));
			}			
			
			// flatten topics (until rewrite properly)
			ArrayList allTopics = new ArrayList();
			for(int i=0; i<f.supertopics.Length; i++) {
				for(int j=0; j<f.supertopics[i].topics.Length; j++) {
					allTopics.Add(f.supertopics[i].topics[j]);
				}				
			}
			f.topics = (Topic[]) allTopics.ToArray(typeof(Topic)); //f.supertopics[1].topics;
			return f;
		}

		public void Save() { Save("facts.xml"); }
		public void Save(string filename) {
			// backup the file
			FileInfo fi = new FileInfo(filename);
			if(fi.Exists) fi.CopyTo(fi.FullName+".bak", true);
			
			// serialise class to file (stores in xml)
			XmlSerializer serializer = new XmlSerializer(typeof(Facts));
			StreamWriter sw = new StreamWriter(filename);
			serializer.Serialize(sw, this);  
			sw.Close();
		}
	}

	class Generate {

		static string globalOutputPath = "usa\\";
	
		static public bool altDesign = true;
		
		static string GetLink(Topic t) {
			string link = "";
			int pos = t.name.IndexOf(" ");
			if(pos != -1) link = t.name.Substring(0, pos);
			else          link = t.name;
			return link.ToLower().Trim();
		}
		
		static string GetLink(Subtopic st) {
			string link = st.link.Trim();
			if(link == "") {
				int pos = st.name.IndexOf(" ");
				if(pos != -1) link = st.name.Substring(0, pos);
				else          link = st.name;
				//Console.Write("link = '"+link+"'\n");
			}
			return link.ToLower().Trim();
		}
		
		static string CreateLinks(Facts f, string text) {
			int pos1=-1, pos2=-1, pos3=-1;
			for(;;) {
				pos1 = text.IndexOf("|", pos3+1);
				if(pos1 == -1) break;
		        pos2 = text.IndexOf(" ", pos1+1);
				if(pos2 == -1) { Console.Write("Error(1) in CreateLinks"); break; }
				pos3 = text.IndexOf("|", pos2+1);
				if(pos3 == -1) { Console.Write("Error(2) in CreateLinks"); break; }

				string linkToFind = text.Substring(pos1+1, pos2-pos1-1).ToLower().Trim();
				//Console.Write("    looking for link: '" + linkToFind + "'");

				bool found = false;
				bool foundTopic = false;
				int page = -1;
				for(int i=0; i<f.topics.Length; i++) {
					
					// check for link to a topic
					Topic t = f.topics[i];
					//Console.Write("topic link = " + GetLink(t) + "\n");
					if(GetLink(t) == linkToFind) {
						string replaceWith = "<a href=\"topic"+i+".php\">"+text.Substring(pos2+1, pos3-pos2-1)+"</a>";
						text = text.Substring(0, pos1) + replaceWith + text.Substring(pos3+1);
						pos3 = pos1;
						foundTopic = true;
						break;
					}			
					for(int j=0; j<t.subtopics.Length; j++) {
						Subtopic st = t.subtopics[j];
						if(GetLink(st) == linkToFind) {
							found = true;
							break;
						}
						for(int k=0; k<st.facts.Length; k++) {
							if(st.facts[k].link.ToLower().Trim() == linkToFind) {
								found = true;
								break;
							}
						}
						if(found) break;
					}
					if(found) { page = i; break; }
				}
				if(page != -1 && !foundTopic) {
					string replaceWith = "<a href=\"topic"+page+".php#"+linkToFind+"\">"+text.Substring(pos2+1, pos3-pos2-1)+"</a>";
					//Console.Write(" replacing ... \n");
					text = text.Substring(0, pos1) + replaceWith + text.Substring(pos3+1);
					pos3 = pos1;
				}
				else if(!foundTopic) {
					Console.Write("    cannot find! " + linkToFind + "\n");
					return text.Substring(0, pos1) + text.Substring(pos2+1, pos3-pos2-1) + text.Substring(pos3+1);
				}
				
				// topic+page+.php#linktofind
			}
			return text;
		}
		
		static Fact FindFact(string reference, Facts f) {
			string linkToFind = reference.ToLower().Trim();

			Fact ret = null;
			for(int i=0; i<f.topics.Length; i++) {
				Topic t = f.topics[i];
				for(int j=0; j<t.subtopics.Length; j++) {
					Subtopic st = t.subtopics[j];
					for(int k=0; k<st.facts.Length; k++)
						if(st.facts[k].link.ToLower().Trim() == linkToFind)
							ret = st.facts[k];
				}
			}
			return ret;
		}
		
		static string DoTableHead(string link, string commentsLink, string prompt, int width, bool showBackground) {
			string w     = "width="+width;
			string ret   = "<br>";
			int    debug = 0;
			
			ret+="<table border="+debug+" cellspacing=0 cellpadding=0 "+w+">\n"+link;
			ret+="<tr><td rowspan=5 height=15>&nbsp;&nbsp;&nbsp;&nbsp;</td></tr>";
			//ret+="<tr><td height=10>&nbsp;</td></tr>";
			ret+="<tr><td colspan=2><table border="+debug+" cellpadding=3 "+w+"><tr><td class=tablehead2><b>"+prompt+"</b></td>";
			if(commentsLink.Length > 0) 
				ret+="  <td align=right valign=top><a href=\"#comments_"+commentsLink+"\"><img src=\"arrowd.gif\" width=18 height=8 border=0>View&nbsp;comments</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
			ret+="</tr></table></td></tr>";
			ret+="<tr><td height=5></td></tr>";
			ret+="<tr><td>&nbsp;</td><td><table border="+debug;
			if(showBackground) ret+=" background=\"usflag.jpg\" ";
			ret +=" cellpadding=2 "+w+">\n";
			return ret;
		}
		static string DoTableFoot() {
			if(!altDesign) return "  </table></td></tr></table></td></tr></table>";
			else      return "  </table></td></tr></table>";

		}

		static void AddFileReplace(StreamWriter sw, string filename, string path, int i) {
			FileStream   fs = new FileStream(path+filename, FileMode.Open);
			StreamReader sr  = new StreamReader(fs);
	
			string data = sr.ReadToEnd();
			data = data.Replace("<num>", i.ToString());
			sw.WriteLine(data);
			sr.Close();
		}

		static void AddFileReplace(StreamWriter sw, string filename, string path, String links) {
			FileStream   fs = new FileStream(path+filename, FileMode.Open);
			StreamReader sr  = new StreamReader(fs);
	
			string data = sr.ReadToEnd();
			data = data.Replace("<links>", links);
			sw.WriteLine(data);
			sr.Close();
		}


		static void AddFileReplaceF(StreamWriter sw, string filename, string path, int i) {
			FileStream   fs = new FileStream(path+filename, FileMode.Open);
			StreamReader sr  = new StreamReader(fs);
	
			string data = sr.ReadToEnd();
			data = data.Replace("topic<num>.php", "/phorum/index.php");
			data = data.Replace("<num>", i.ToString());
			sw.WriteLine(data);
			sr.Close();
		}
		
		static void AddFile(StreamWriter sw, string filename, string path) {
			FileStream   fs = new FileStream(path+filename, FileMode.Open);
			StreamReader sr = new StreamReader(fs);
			
			string data = sr.ReadToEnd();
			sw.WriteLine(data);
			sr.Close();
		}

		static String ReadWholeFile(String filename) {
			FileStream   fs = new FileStream(filename, FileMode.Open);
			StreamReader sr = new StreamReader(fs);
			string data = sr.ReadToEnd();
			sr.Close();
			return data;
		}
		
		static void CompressLinks(String hashname, String hash) {
			if(true) return;
			//if(hashname.IndexOf("222702733") == -1) return;
			try {
				String outputPath = "compressed";
				
				Hashtable h = new Hashtable();
				
				Console.Write("\nParsing file " + hashname);
				FileInfo fi = new FileInfo(hashname);
				if(fi.Exists) {
					FileStream   fs = new FileStream(hashname, FileMode.Open);
					StreamReader sr = new StreamReader(fs);
	
					string data = sr.ReadToEnd();
					sr.Close();
				
					int pos = -1;
					for(;;) {	
	
						pos = data.IndexOf("<img", pos+1);
						if(pos == -1) break;
						
						int pos2 = data.IndexOf("src", pos);
						if(pos2 == -1) {
							Console.Write("\n  Error parsing file " + hashname + " pos2 == -1");
							continue;
						}
						int pos3 = data.IndexOf("=", pos2);
						if(pos3 == -1) {
							Console.Write("\n  Error parsing file " + hashname + " pos3 == -1");
							continue;
						}
						
						// find bounds of actual image name
						int realStart = -1;
						int start = pos3+1;
						for(;;) {
							if(data[start] == '"') { realStart = start+1; break; }
							else if(data[start] != ' ') { realStart = start; break; }
							else start++;
						}
						int end = realStart+1;
						int realEnd = -1;
						for(;;) {
							if(data[end] == '"') { realEnd = end; break; }
							else if(data[end] == ' ') { realEnd = end; break; }
							else end++;
						}
						string fileName=data.Substring(realStart, realEnd-realStart);
						//Console.Write("\n  Found image: " + fileName);
						if(fileName.StartsWith("http")) continue;
						if(fileName.Length < 5 || fileName[0] < '0' || fileName[0] > '9') {
							Console.Write("\n  Invalid filename " + fileName + " ignored");
							continue;
						}
						
						if(h.ContainsKey(fileName)) {
							string hRef = h[fileName].ToString();	
							//Console.Write("\nfile + " + fileName + " found in hashmap with value " + hRef + " !!!");
							data = data.Substring(0,realStart) + hRef + data.Substring(realEnd);	
							continue;
						}					
						else {
							try {
								// Search through img dirs until either file existing matching file, or place to copy file
								for(int j=0;;j++) {
									int posSlash = fileName.IndexOf("/");
									if(posSlash == -1)
										Console.Write("\n  Filename " + fileName + " has no slash!?!?!?!??!");
										
									String fileNam = fileName.Substring(posSlash+1);
									
									String outputFile = outputPath + "\\img"+j + "\\"+ fileNam;
									String inputFile  = globalOutputPath + fileName.Substring(0, posSlash) + "\\" + fileName.Substring(posSlash+1);
									String hRef =  "img"+j + "/" + fileNam;
									
									//Console.Write("\n    output="+outputFile+" input="+inputFile);
									FileInfo fiOut = new FileInfo(outputFile);
									FileInfo fiIn  = new FileInfo(inputFile);
									
									if(!fiIn.Exists) {
										Console.Write("\n  File " + inputFile + " not found???");
										break;
									}
									
									// file of same name already exists - see if contents match
									if(fiOut.Exists) {
										if(fiIn.Length != fiOut.Length) {
											Console.Write(",");
										}
										else {
											String data2 = ReadWholeFile(outputFile);
											String data3 = ReadWholeFile(inputFile);
											
											if(data2 == data3) {
												//Console.Write("\n  Found existing match: " + outputFile);
												data = data.Substring(0,realStart) + hRef + data.Substring(realEnd);	
												h.Add(fileName, hRef);
												break;
											}
											else
												Console.Write(".");
										}
									}
									
									// file of same name doesn't exist - copy into output dif
									else {
										//Console.Write("\n  No match found - copying file " + outputFile);
			
										DirectoryInfo di = new DirectoryInfo(outputPath + "\\img"+j);
										if(!di.Exists)
											di.Create();
			
										fiIn.CopyTo(outputFile, true);
										data = data.Substring(0,realStart) + hRef + data.Substring(realEnd);	
										h.Add(fileName, hRef);
										break;
									}
								}
							  }
							catch(Exception e) {
								Console.Write("\n\n\nError parsing " + fileName + " in " + hashname);
								Console.Write("\n" + e.Message);
								Console.Write("\n" + e.StackTrace + "\n\n\n");
							}
						}
					}
					int posH = hashname.IndexOf("\\");
					if(posH == -1) {
						Console.Write("\n  Fatal Error finding \\ in hash " + hashname);
						return;
					}
					String s = hashname.Substring(posH+1);
					StreamWriter sw = new StreamWriter(outputPath + "\\" + s);
					sw.Write(data);
					sw.Close();			
				}
			}
			catch(Exception e) {
				Console.Write("\n\n\nFatal Error parsing " + hashname);
				Console.Write("\n" + e.Message);
				Console.Write("\n" + e.StackTrace + "\n\n\n");
			}
		}

		// inserts code to jump into the frame set for all cached .htm pages
		// - disable this function to make the program run a lot faster!		
		static void InsertJumpCode(string hashname, string hash) {
			
			if(true) return; // function disabled
			
			// add code to page to make it jump into frameset !?
			FileInfo fi = new FileInfo(hashname);
			if(fi.Exists) {
				FileStream   fs = new FileStream(hashname, FileMode.Open);
				StreamReader sr = new StreamReader(fs);

				string data = sr.ReadToEnd();
				sr.Close();
				string startCheck = "<!-- code added to page to jump into frameset (rest of page unchanged) -->";
				string endCheck   = "<!-- end of code insert -->";
				string insert = "<script language=\"Javascript\">;\n";
				insert += "if(top == self || parent.frames[2].name != 'fhcontent')\n";
				insert += "  top.location.href = 'index0.php?page="+hash+"';\n";
				insert += "</script>\n";
				
				// add the jump code to the page
				if(!data.StartsWith(startCheck)) {
					data = startCheck + "\n" + insert + endCheck + "\n\n" + data;
				}
				
				// update the jump code within the page
				else {
					int pos = data.IndexOf(endCheck);
					data = startCheck + "\n" + insert + data.Substring(pos);
				}
								
				// backup the file
				FileInfo fi2 = new FileInfo(hashname);
				if(fi2.Exists) fi2.CopyTo(fi2.FullName+".bak", true);

				FileStream   fs2= new FileStream(hashname, FileMode.Create);
				StreamWriter sw = new StreamWriter(fs2);
				sw.WriteLine(data);
				sw.Close();			
			}
			else {
				Console.Write("  File " + hashname + " does not exist!\n"); 
			}
		}

		[STAThread]
		static void Main(string[] args) {
			Facts f = Facts.LoadFacts();
			String path = globalOutputPath;
			//f.Save();

			Console.Write("number of topics: " + f.topics.Length + "\n"); 
			
			Console.Write("number of pruned topics: " + f.topics.Length + "\n"); 


			// ************************************** alt design ********************************************
			{
				
				// ******************* create indexX.php frames *********************
				for(int i=-1; i<f.topics.Length; i++) { 
					string filename = "index.php";
					if(i!=-1) filename = "index"+i.ToString()+".php";
					FileStream   fs = new FileStream(path+filename, FileMode.Create);
					StreamWriter sw = new StreamWriter(fs);
					
					if(i > -1 && f.topics[i].name == "Forum") 
						AddFileReplaceF(sw, "index.php.inc", path, Math.Max(i,0));					
					else
						AddFileReplace(sw, "index.php.inc", path, Math.Max(i,0));					

					sw.Close();
				}

				// ******************* create indexX.htm files (jump to corres. php ones) *********************
				for(int i=0; i<f.topics.Length; i++) { 
					string filename = "index"+i.ToString()+".htm";
					FileStream   fs = new FileStream(path+filename, FileMode.Create);
					StreamWriter sw = new StreamWriter(fs);
					
					sw.Write("<html><head><script language=javascript>document.location.href='index"+i.ToString()+".php'</script></head></html>");
					
					sw.Close();
				}


				// ******************* create topX.htm frames *********************
				int absoluteTopic = 0;
				for(int st=0; st<f.supertopics.Length; st++) {
					Topic[] curtopics = f.supertopics[st].topics;
					for(int t=0; t<curtopics.Length; t++) { 
						//Console.Write("\nabtop: " + absoluteTopic + " - " + curtopics[t].name);
						
						string filename = "top"+absoluteTopic+".htm";
						if(st == f.supertopics.Length-1 && t == curtopics.Length-1) filename = "topF.htm";
						
						FileStream   fs = new FileStream(path+filename, FileMode.Create);
						StreamWriter sw = new StreamWriter(fs);
	
						String links = "";
						int offset=0;
						for(int st2=0; st2<f.supertopics.Length; st2++) {
							if(st2 > 0) links += " | ";
							if(st == st2) {
								links += f.supertopics[st2].name;
							}	
							else {
								string url = "index"+offset+".php\" target=\"_parent";
								if(f.supertopics[st2].name == "Forum") {
									url = "/phorum/index.php\" target=\"fhcontent";
								}
								links += "<a href=\""+url+"\">"+f.supertopics[st2].name+"</a>";
							}
							offset += f.supertopics[st2].topics.Length;
						}
						AddFileReplace(sw, "top.htm.inc", path, links);					
	
						sw.Close();
						absoluteTopic++;
					}
				}

					
				// ******************* left nav begins *********************
				absoluteTopic=-1;
				for(int st=0; st<f.supertopics.Length; st++) {
					
					Topic[] curtopics = f.supertopics[st].topics;
					
					int start = -1;
					if(st > 0) start = 0;
					
					int superTopicOffset = absoluteTopic;
					if(absoluteTopic == -1) superTopicOffset = 0;
					
					for(int ct=start; ct<curtopics.Length; ct++) { 
						
						string curFilename = "left"+absoluteTopic+".htm";
						if(absoluteTopic == -1) curFilename = "leftS.htm";
						
						FileStream   fs = new FileStream(path+curFilename, FileMode.Create);
						StreamWriter sw = new StreamWriter(fs);
		
						//AddFile(sw, "altheader.inc", path);
						AddFileReplace(sw, "altheader.inc", path, absoluteTopic);					
		
						sw.WriteLine("<td valign=top cellpadding=2  class=leftframe>");
						sw.WriteLine("  <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"185\">");
						sw.WriteLine("    <tr><td>&nbsp;</td></tr>");
						
						String pictureLink = "<a href=\"topic"+superTopicOffset+".htm\" target=\"fhcontent\">";
						if(f.supertopics[st].image.Trim().Length > 0) {
							sw.Write("    <tr><td colspan=2 bgcolor=#FFFFFF align=center>");
							sw.Write(pictureLink+"<img border=0 align=center ");
							sw.Write(" src=\""+f.supertopics[st].image.Trim() + "\"");
							sw.Write(" width=\""+f.supertopics[st].imagewidth+"\"");
							sw.Write(" height=\""+f.supertopics[st].imageheight+"\"></a></td></tr>");
						}
						else {
							sw.WriteLine("    <tr><td colspan=2 bgcolor=#FFFFFF align=center>"+pictureLink+"<img border=0 align=center src=\"sam-small.gif\" width=134 height=158></a></td></tr>");
						}
						sw.WriteLine("    <tr height=11><td>&nbsp;</td></tr>");
		
						for(int j=0; j<curtopics.Length; j++) {
							Topic curtopic = curtopics[j];
							
							// don't include skeletons
							if(j==0 && curtopic.subtopics.Length == 1 && curtopic.name.Trim() == "Introduction") continue;
							
							// if matches
							if(j == ct) {
								
								if(curtopics.Length > 1) {
									sw.WriteLine("<tr><td class=tablehead   height=4 colspan=2><img src=\"dot.gif\" height=\"4\"></td></tr>");
									sw.WriteLine("<tr><td class=tablehead   cellspacing=\"0\" cellpadding=\"6\" border=\"0\">&nbsp;&nbsp;&nbsp;</td>");
									sw.WriteLine("    <td align=left class=tablehead cellspacing=\"0\" cellpadding=\"6\" border=\"0\">");		
									sw.WriteLine("  <table border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"arrowd.gif\" width=18 height=8></td><td><b>"+curtopic.name+"</b></td></tr></table>");
									sw.WriteLine("</td></tr>");
									sw.WriteLine("<tr><td class=tablehead   height=4 colspan=2><img src=\"dot.gif\" height=\"4\"></td></tr>");
								}
																
								// add subtopics!
								if(curtopic.subtopics.Length > 1) {
									for(int k=0; k<curtopic.subtopics.Length; k++) {
										Subtopic stopic = curtopic.subtopics[k];
										string name = stopic.shortName;
										if(name == "") name = stopic.name;
										String link = "<a href=\"topic"+absoluteTopic+".php"+"#" + GetLink(stopic) + "\" target=fhcontent>";
										sw.WriteLine("<tr><td colspan=2><table cellpadding=0 cellspacing=0><tr><td><img src=\"dot.gif\" width=\"26\" height=\"1\"></td><td>"+link+"<img src=\"bullet.gif\" border=0 width=18 height=8></a></td>");
										sw.WriteLine("    <td valign=top><img src=\"dot.gif\" width=\"3\" height=\"1\"></td><td>"+link+name+"</a></td></tr></table></td></tr>");
										sw.WriteLine("<tr><td height=2 colspan=2><img src=\"dot.gif\" height=\"2\"></td></tr>");
									}
								}
							}
							
							// doesn't match
							else {
								string name = "index.php";
								if(j+superTopicOffset!=0) name = "index"+(j+superTopicOffset)+".php";
								sw.WriteLine("<tr><td>&nbsp;&nbsp;&nbsp;</td><td>");
								sw.WriteLine("  <table border=0 cellspacing=0 cellpadding=0><tr>");
								sw.WriteLine("    <td><a href=\""+name+"\" target=\"_top\"><img src=\"arrowr.gif\" border=0 width=18 height=8></td>");
								sw.WriteLine("    <td><a href=\""+name+"\" target=\"_top\"><b>"+curtopic.name+"</b></a></td>");
								sw.WriteLine("  </tr></table>");
								sw.WriteLine("</td></tr>");	 
							}
							sw.WriteLine("<tr><td height=5><img src=\"dot.gif\" height=\"5\"></td></tr>");
						}
						sw.WriteLine("  <tr><td height=18>&nbsp;</td></tr> ");
						sw.Write    ("  <form target=\"fhcontent\" method=\"get\" action=\"search.php\">");
						sw.Write    ("	<tr><td colspan=2 align=center>Search Site:<br>");
						sw.Write    ("    <input type=hidden name=\"start\" value=\"1\">");
						sw.Write    ("    <input type=text name=\"searchstring\" size=\"14\">");
						sw.WriteLine("  </td></tr>");
						sw.WriteLine("  </form>");
						
						sw.WriteLine("  <tr><td height=20>&nbsp;</td></tr> ");
						sw.WriteLine("  <tr><td colspan=2 width=\"260\" align=center>");
						DateTime d = System.DateTime.Now;
						string s   = d.ToShortDateString();
						int pos    = s.IndexOf(",");
						s = s.Substring(pos+1);
						s = s.Replace("/", ".");
							
						sw.WriteLine("    <span class=small>(c) Mike Bevin "+s+"</span>");
						sw.WriteLine("	</td></tr>	");
						sw.WriteLine("</table>");
						sw.WriteLine("</td>");
						sw.WriteLine("<td valign=top>&nbsp;&nbsp;</td>");
						sw.WriteLine("<td valign=top width=100%>");
		
						AddFile(sw, "altfooter.inc", path);
		
						sw.Close();
						
						absoluteTopic++;
					}
				}
				// ********************* left nav ends ***********************
	
	
				// ********************* topics begin ***********************
				for(int i=0; i<f.topics.Length; i++) {
					Topic t= f.topics[i];
					FileStream   fs = new FileStream(path+"topic"+i+".php", FileMode.Create);
					StreamWriter sw = new StreamWriter(fs);
	
					AddFileReplace(sw, "altheader-right.php.inc", path, i);					
	
					if(t.subtopics.Length>0) {
						string link = GetLink(t.subtopics[0]);
						sw.WriteLine("<a name=\""+link+"\"></a>");
					}

					sw.WriteLine("<td valign=top><table cellspacing=0 cellpadding=0>");
	
					Console.Write("  number of subtopics: " + t.subtopics.Length + "\n");
					bool inForce = false;
					for(int j=0; j<t.subtopics.Length; j++) {
	
						string link = GetLink(t.subtopics[j]);
						if(j>0) sw.WriteLine("<a name=\""+link+"\"></a>");
						
						string commentLink = GetLink(t.subtopics[j]);
						if(t.subtopics[j].nocomment.Length > 0)
							commentLink = "";
							
						if(t.subtopics[j].forcebg.Trim() == "share") {
							sw.WriteLine("<table background=\"usflag.jpg\" border=0 cellspacing=0 cellpadding=0><tr><td>");
							sw.WriteLine(DoTableHead("", commentLink, t.subtopics[j].name, 720, false));
							inForce = true;
						}
						else {
							sw.WriteLine(DoTableHead("", commentLink, t.subtopics[j].name, 720, !inForce && (t.subtopics[j].facts.Length>5 || t.subtopics[j].forcebg.Length>0)));
							if(inForce) {
								inForce = false;
								sw.WriteLine("</td></tr></table>");
							}
						}
						sw.WriteLine("	<tr height=\"5\"><td height=\"5\"></td></tr> ");
						
						for(int k=0; k<t.subtopics[j].facts.Length; k++) {
							
							Fact fact = t.subtopics[j].facts[k];
							if(fact.reference.Trim().Length > 0) {
								fact = FindFact(fact.reference, f);
								if(fact == null) continue;
							}
							if(fact.hide.Trim().Length > 0) continue;
	
							// output info to be used by search function
							if(fact.link == "") fact.link = (i*20000+j*500+k).ToString();
							sw.Write("<!--factbeginstopic"+i+".php#"+fact.link+"|");
							sw.Write("<a href=\"topic"+i+".php\">"+t.name+"</a>|");
							sw.Write("<a href=\"topic"+i+".php#"+GetLink(t.subtopics[j])+"\">"+t.subtopics[j].name+"</a>|-->");
							
							if(fact.wide.Trim().Length > 0)
								sw.WriteLine("<tr><td valign=top colspan=3>" + CreateLinks(f, fact.text)+"</td></tr>");
							else {

								// new style
								{
									bool linkWritten = false;
									
									// write date+urls
									if(fact.date.Trim().Length > 0 || fact.urlText.Trim().Length > 0) {
										sw.WriteLine("<tr> ");
										
										// date
									//sw.WriteLine("  <td valign=top colspan=2 width=300><span class=date>");
										sw.WriteLine("  <td valign=top colspan=4><table width=700><tr><td><span class=date>");
										
										// anchor
										if(fact.link != "") {
											sw.WriteLine("<a name=\""+fact.link+"\"></a>");
											linkWritten = true;
										}
										
										string date = CreateLinks(f, fact.date);
										//date = date.Replace(" ", "&nbsp;");
										sw.WriteLine(date+"</span></td>");
										
										// link
										sw.WriteLine("<td valign=top align=right colspan=2>");
	
										if(fact.urlText.Trim().Length>0) {
											//sw.WriteLine("<td valign=top width=150>");
											string url = fact.urlText;
											
											// if not internal url, create href using url-text+url, else just do replace link on url-text
											if(fact.url.Trim().Length>0) {
												url = url.Replace(" ", "&nbsp;");
												sw.Write("<a href=\""+fact.url+"\">"+url+"</a>");
											}
											else {
												//Console.Write("creating links for internal url: " + url);
												sw.Write(CreateLinks(f, url));
											}
											
											if(fact.url.IndexOf("/") != -1) {
												string hash = Math.Abs(fact.url.GetHashCode()).ToString();
												if(fact.url.EndsWith(".pdf")) hash += ".pdf";
												else {
													hash += ".htm";
													string hashname = globalOutputPath+hash;
													InsertJumpCode(hashname, hash);
													CompressLinks(hashname, hash);
												}
												sw.Write("&nbsp;(&nbsp;<a href=\""+hash+"\">cached</a>&nbsp;)");
											}
										}
			
										if(fact.altUrls.Trim().Length>0) {
											string[] elements = fact.altUrls.Split(new char[] {' '});
											sw.Write("&nbsp;See&nbsp;also:&nbsp;");
											int n=1;
											for(int l=0; l<elements.Length; l++) {
												if(elements[l].Trim().Length > 0) {
													sw.Write("<a href=\""+elements[l]+"\">"+n+"</a>&nbsp;");
													n++;
												}
											}
										}
									//sw.WriteLine("</td></tr>");
										sw.WriteLine("</td></tr></table></td></tr>");
									}
																		
									// fact
									if(fact.text.Trim().Length > 0) {
										sw.WriteLine("<tr>");
										sw.WriteLine("  <td colspan=4 width=100%>");
										if(fact.link != "" && !linkWritten) sw.WriteLine("<a name=\""+fact.link+"\"></a>");
										sw.WriteLine("		<table cellpadding=0 cellspacing=0><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td><td>");
										sw.WriteLine(CreateLinks(f, fact.text));
										sw.WriteLine("  </td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td></tr></table>");
										sw.WriteLine("</tr>");
									}
									sw.WriteLine("<tr><td height=3><img src=\"dot.gif\" height=6></td></tr>");
								}
							}
							sw.WriteLine("<!--factends-->");
						}

						if(t.subtopics[j].nocomment.Length == 0) {
							sw.WriteLine("<tr><td height=1><a name=\"comments_"+GetLink(t.subtopics[j])+"\"></a></td></tr>");
							
							// add comments, and link to add comment ....
							FileStream   fs2 = new FileStream(path+"topiccomment.inc", FileMode.Open);
							StreamReader sr2 = new StreamReader(fs2);
							
							string sname = t.subtopics[j].shortName;
							if(sname == "") sname = t.subtopics[j].name;
							
							string data = sr2.ReadToEnd();
							data = data.Replace("<topicid>", GetLink(t.subtopics[j]));
							data = data.Replace("<topicname>", sname);
							sw.WriteLine(data);
							sr2.Close();
						}
	
						//sw.WriteLine("	<tr height=\"8\"><td height=\"8\">&nbsp;</td></tr> ");
						sw.WriteLine(DoTableFoot()+"<br>");
					}
					if(t.subtopics.Length>1) sw.WriteLine("<tr><td height=50>&nbsp;</td></tr></table></td>");
	
					AddFile(sw, "altfooter-right.inc", path);
	
					sw.Close();
				} 
				// ********************* topics end ***********************
			}
			
			{
				// create searchable file with all content inside
				FileStream   fs = new FileStream(path+"all.htm", FileMode.Create);
				StreamWriter sw = new StreamWriter(fs);
				for(int i=0; i<f.topics.Length; i++) { 
					AddFile(sw, "topic"+i+".php", path);
				}
				sw.Close();
			}
			
			Console.Write("Done");
		}
	}
}
