view API/Exporters/WarFoundryHtmlExporter.cs @ 472:e6c93ceba119

Re #359: Add "only contained" attribute to unit types * Make sure we add and remove from the parent unit appropriately * Make sure that setting the unit's parent to null removes it from the parent
author IBBoard <dev@ibboard.co.uk>
date Mon, 16 Apr 2012 20:14:23 +0100
parents 6da9db4a9c23
children 81f32062c9fa
line wrap: on
line source

// This file (WarFoundryHtmlExporter.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
// 
// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using IBBoard.Lang;
using IBBoard.Xml;
using IBBoard.WarFoundry.API.Objects;
using IBBoard.WarFoundry.API.Util;

namespace IBBoard.WarFoundry.API.Exporters
{
	/// <summary>
	/// Custom exporter that exports an army as a basic HTML file
	/// </summary>
	public class WarFoundryHtmlExporter : IWarFoundryExporter
	{
		private static WarFoundryHtmlExporter exporter;
		private delegate string GetStatCellTextDelegate(Stat stat);
		
		public static WarFoundryHtmlExporter GetDefault()
		{
			if (exporter == null)
			{
				exporter = new WarFoundryHtmlExporter();
			}
			
			return exporter;
		}
		
		private WarFoundryHtmlExporter()
		{
			//Hide constructor
		}
		
		public void ExportArmy(Army army, string path)
		{
			XmlDocument doc = new XmlDocument();
			CustomXmlResolver resolver = new CustomXmlResolver();
			Uri localUri = new Uri("file://" + IBBoard.Constants.ExecutablePath + "/schemas/xhtml1-strict.dtd");
			resolver.AddMapping("-//W3C//DTD XHTML 1.0 Strict//EN", localUri);
			resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", localUri);
			doc.XmlResolver = resolver;
			doc.AppendChild(doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null));
			XmlElement html = doc.CreateElement("html");
			doc.AppendChild(html);
			XmlElement head = doc.CreateElement("head");
			html.AppendChild(head);
			XmlElement title = doc.CreateElement("title");
			title.InnerXml = army.Name;
			head.AppendChild(title);
			XmlElement metaCharset = doc.CreateElement("meta");
			metaCharset.SetAttribute("http-equiv", "Content-Type");
			metaCharset.SetAttribute("content", "text/html;charset=UTF-8");
			head.AppendChild(metaCharset);
			XmlElement style = doc.CreateElement("style");
			style.InnerText = "table, th, td { border: 1px solid #000; border-spacing: 0; border-collapse: collapse; margin: 0 }\n"
				+"table table { width: 100%; border-width: 0; margin: -2px }\n"
				+"table table td { border-width:0 1px }";
			head.AppendChild(style);
			XmlElement body = doc.CreateElement("body");
			html.AppendChild(body);
			XmlElement header = doc.CreateElement("h1");
			header.InnerText = Translation.GetTranslation("armyHtmlOutputBodyHeader", "{0} - {1}pts", army.Name, army.Points);
			body.AppendChild(header);
			
			foreach (XmlElement table in CreateTables(army, doc))
			{
				if (!IsTableOnlyHeader(table))
				{
					body.AppendChild(table);
				}
			}

			File.WriteAllText(path, doc.OuterXml);
		}

		private bool IsTableOnlyHeader(XmlElement table)
		{
			return table.ChildNodes.Count == 1;
		}
		
		private XmlElement[] CreateTables(Army army, XmlDocument doc)
		{
			Dictionary<string, XmlElement> tables = new Dictionary<string, XmlElement>();
			
			foreach (SystemStats statSets in army.GameSystem.SystemStats)
			{
				tables[statSets.ID] = CreateTable(statSets, doc);
			}
			
			foreach (Unit unit in army.GetUnits())
			{
				CreateUnitRow(unit, tables[GetFirstStatType(unit)]);
			}
			
			return DictionaryUtils.ToArray(tables);
		}

		private static string GetFirstStatType(Unit unit)
		{
			string[] unitStatIDs = unit.UnitStatsArrayIDs;
			return GetFirstStatType(unitStatIDs);
		}
		
		public static string GetFirstStatType(string[] unitStatIDs)
		{
			return unitStatIDs[0];
		}
		
		private XmlElement CreateTable(SystemStats stats, XmlDocument doc)
		{
			XmlElement table = doc.CreateElement("table");
			XmlElement headerRow = doc.CreateElement("tr");
			table.AppendChild(headerRow);
			XmlElement name = doc.CreateElement("th");
			name.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitName", "name");
			headerRow.AppendChild(name);
			
			XmlElement unitTypeName = doc.CreateElement("th");
			unitTypeName.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitTypeName", "type name");
			headerRow.AppendChild(unitTypeName);
			
			foreach (StatSlot stat in stats.StatSlots)
			{
				XmlElement statHeader = doc.CreateElement("th");
				statHeader.InnerText = stat.Name;
				headerRow.AppendChild(statHeader);
			}
			
			XmlElement notes = doc.CreateElement("th");
			notes.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitNotes", "name");;
			headerRow.AppendChild(notes);
			
			XmlElement points = doc.CreateElement("th");
			points.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitPoints", "name");;
			headerRow.AppendChild(points);
			
			return table;
		}
		
		private XmlElement CreateUnitRow(Unit unit, XmlElement tableElem)
		{
			XmlDocument doc = tableElem.OwnerDocument;
			XmlElement row = doc.CreateElement("tr");
			tableElem.AppendChild(row);
			Stat[][] memberStats = unit.UnitStatsArraysWithName;
			string[] statTypeIDs = unit.UnitStatsArrayIDs;
			string defaultStatType = GetFirstStatType(statTypeIDs);
			int statRowCount = 0;
			bool hasOther = false;
			
			foreach (string statTypeID in statTypeIDs)
			{
				if (statTypeID.Equals(defaultStatType))
				{
					statRowCount++;
				}
				else if (!hasOther)
				{
					statRowCount++;
					hasOther = true;
				}
			}
			
			XmlElement name = doc.CreateElement("td");
			name.InnerText = unit.Name;
			SetRowSpan(name, statRowCount);
			row.AppendChild(name);
			CreateStatsBlock(row, memberStats, statTypeIDs);

			StringBuilder sb = new StringBuilder();
			UnitEquipmentItem[] unitEquipment = unit.GetEquipment();
			
			if (unitEquipment.Length > 0)
			{
				bool addSeparator = false;
				
				foreach (UnitEquipmentItem equip in unitEquipment)
				{
					if (!addSeparator)
					{
						addSeparator = true;
					}
					else
					{
						sb.Append(", ");
					}

					string amountString;
					double amount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);

					if (UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip))
					{

						if (amount == 100)
						{
							amountString = GetEquipmentAmountAllTranslation(unit);
						}
						else
						{
							int number = UnitEquipmentUtil.GetEquipmentAmountTaken(unit, equip);
							amountString = GetEquipmentAmountRatioTranslation(amount, number);
						}
					}
					else
					{
						if (amount == -1)
						{
							amountString = GetEquipmentAmountAllTranslation(unit);
						}
						else
						{
							amountString = GetEquipmentAmountNumberTranslation((int)amount);
						}
					}

					sb.Append(Translation.GetTranslation("armyHtmlExportEquipAmountRatio", "{0} for {1}", equip.Name, amountString));
				}
				
				sb.Append(". ");
			}
			
			ICollection<Ability> abilities = unit.Abilities;
			
			if (abilities.Count > 0)
			{
				bool addSeparator = false;
				
				foreach (Ability ability in abilities)
				{
					if (!addSeparator)
					{
						addSeparator = true;
					}
					else
					{
						sb.Append(", ");
					}
					
					sb.Append(ability.Name);
				}
				
				sb.Append(". ");
			}
			
			XmlElement notes = doc.CreateElement("td");
			notes.InnerText = sb.ToString();
			SetRowSpan(notes, statRowCount);
			row.AppendChild(notes);
			
			XmlElement points = doc.CreateElement("td");
			points.InnerText = unit.Points.ToString();
			SetRowSpan(points, statRowCount);
			row.AppendChild(points);
			
			return row;
		}
		
		private static void SetRowSpan(XmlElement xmlElement, int statRowCount)
		{
			if (statRowCount > 1)
			{
				xmlElement.SetAttribute("rowspan", statRowCount.ToString());
			}
		}
		
		private void CreateStatsBlock(XmlElement unitRow, Stat[][] memberStats, string[] statTypeIDs)
		{
			XmlDocument doc = unitRow.OwnerDocument;
			string defaultStatType = GetFirstStatType(statTypeIDs);
			
			Stat[] defaultStatLine = memberStats[0];
			int defaultStatLineCount = defaultStatLine.Length;
			AddStatCell(defaultStatLine[0].SlotValueString, unitRow);
			
			for (int i = 1; i < defaultStatLineCount; i++)
			{
				string statText = GetDefaultStatCellText(defaultStatLine[i]);
				AddStatCell(statText, unitRow);
			}
			
			int statCount = statTypeIDs.Length;
			
			if (statCount > 1)
			{
				XmlElement unitTable = (XmlElement)unitRow.ParentNode;
				Dictionary<string, XmlElement> statParents = CreateStatsParentElements(statTypeIDs, unitTable);
				
				for (int i = 1; i < statCount; i++)
				{
					Stat[] statLine = memberStats[i];
					string statTypeID = statTypeIDs[i];
					XmlElement tableElement = DictionaryUtils.GetValue(statParents, statTypeID);
					int statLineCount = statLine.Length;
					XmlElement statRow = doc.CreateElement("tr");
					tableElement.AppendChild(statRow);
					GetStatCellTextDelegate statCellTextDelegate = (statTypeID.Equals(defaultStatType) ? new GetStatCellTextDelegate(GetDefaultStatCellText) : new GetStatCellTextDelegate(GetOtherStatCellText));
					AddStatCell(statLine[0].SlotValueString, statRow);
				
					for (int j = 1; j < statLineCount; j++)
					{
						string statText = statCellTextDelegate(statLine[j]);
						AddStatCell(statText, statRow);
					}
				}
				
				if (statParents.Count > 1)
				{
					AddOtherUnitStatTables(statParents, unitTable, defaultStatLineCount);
				}
			}
		}
		
		private static void AddOtherUnitStatTables(Dictionary<string, XmlElement> statParents, XmlElement unitTable, int defaultStatLineCount)
		{
			XmlDocument doc = unitTable.OwnerDocument;
			XmlElement otherStatsRow = doc.CreateElement("tr");
			unitTable.AppendChild(otherStatsRow);
			XmlElement otherStatsCell = doc.CreateElement("td");
			otherStatsCell.SetAttribute("colspan", defaultStatLineCount.ToString());
			otherStatsRow.AppendChild(otherStatsCell);
			
			foreach (XmlElement tableElem in statParents.Values)
			{
				if (tableElem != unitTable)
				{
					otherStatsCell.AppendChild(tableElem);
				}
			}
		}

		private Dictionary<string, XmlElement> CreateStatsParentElements(string[] statTypeIDs, XmlElement parentTable)
		{
			Dictionary<string, XmlElement> statParents = new Dictionary<string, XmlElement>();
			XmlDocument doc = parentTable.OwnerDocument;
			string defaultStatTypeID = GetFirstStatType(statTypeIDs);
			statParents[defaultStatTypeID] = parentTable;
			
			foreach (string statTypeID in statTypeIDs)
			{
				if (!statParents.ContainsKey(statTypeID))
				{
					XmlElement tableElement = doc.CreateElement("table");
					statParents[statTypeID] = tableElement;
				}
			}
			
			return statParents;
		}

		private string GetDefaultStatCellText(Stat stat)
		{
			return Translation.GetTranslation("armyHtmlExportDefaultStatCellText", "{0}", stat.SlotValueString, stat.ParentSlotName);
		}

		private string GetOtherStatCellText(Stat stat)
		{
			return Translation.GetTranslation("armyHtmlExportOtherStatCellText", "{1}: {0}", stat.SlotValueString, stat.ParentSlotName);
		}
		
		private static void AddStatCell(string statValue, XmlElement row)
		{
			XmlElement statCell = row.OwnerDocument.CreateElement("td");
			statCell.InnerText = statValue;
			row.AppendChild(statCell);
		}
		
		private string GetEquipmentAmountRatioTranslation (double amount, int number)
		{
			return Translation.GetTranslation ("armyHtmlExportEquipAmountPercentage", "{0}% ({1})", amount, number);
		}
		
		private string GetEquipmentAmountNumberTranslation(int amount)
		{
			return Translation.GetTranslation("armyHtmlExportEquipAmountNumber", "{0}", amount);
		}
		
		private string GetEquipmentAmountAllTranslation(Unit unit)
		{
			return Translation.GetTranslation("armyHtmlExportEquipAmountAll", "all ({1})", 100, unit.Size);
		}
	}
}