Click here to Skip to main content
15,891,473 members
Articles / Programming Languages / C#

Mars Mission (5) : the Chemistry of Fuel

Rate me:
Please Sign up or sign in to vote.
4.70/5 (10 votes)
18 Jul 2011CPOL37 min read 29.4K   6.2K   17  
adding chemical elements the ships can use as fuel, and a new auto-pilot feature
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;
using CK_Controls;
using GraphicText;

namespace Mars_Mission
{
	/// minerals
	/// http,//www.johnbetts-fineminerals.com/jhbnyc/referenc.htm
	///
	/// Robert Zubrin : A Case For Mars
	/// Mars covered w/ Fe2O3 hematite
	/// w/ some alumina Al2O3
	/// SiO2 silicon dioxide (needed for photovoltaic panels)
	/// MgO magnesium oxide
	/// fuels
	/// SiH4 silane, B2H6 diborane (combustible in CO2)
	/// 
	/// 
	/// 
	/// nuclear fuel cycle
	/// mined uranium ore			U3O8	Triuranium octoxide - olive-green-to-black odorless solid 
	/// after enrichment			UF6		uranium hexafluoride  - 
	/// after fabrication			UO2		uranium dioxide - pressed into pellet form
	/// spent fuel					High_Level_Waste (HLW) - Eu, Kr, Cd, Sr, Cs, Sn, Sm
	/// </summary>
	/// making propellants on Mars(Robert Zubrin)
	///	1	Sabatier reaction produces methane and water from carbon dioxide and hydrogen
	///				CO2 + 4H2 -> CH4 + 2H2O + heat(>)
	///					- occurs spontaneously in the presence of a nickel or ruthenium catalyst (nickel cheaper, ruthenium better)
	///	2	hydrolysis
	///				2H2O + eVolt -> 2H2 + O2
	///	3	reduction of Carbon dioxide
	///				2CO2 -> 2CO + O2
	///	4	"water-gas shift" reaction 
	///				heat + CO2 + H2 -> CO + H2O
	///					- iron-chrome catalyst
	///			1 & 4 togethher
	///				3CO2 + 6H2 --> CH4 + 2CO + 4H2O
	///	pyrolyze (1)
	///				CH4 -> C + 2H2
	///	
	/// manufacturing plastics 
	/// synthetic ethylene by "reverse water-gas shift" (RWGS) reaction 
	///			3:1 ratio
	///		6H2 + 2CO2 -> 2H2O + 2CO + 4H2
	///		2CO + 4H2 -> C2H4 + 2H2O + HEAT(>>)
	///					C2H4 is ethylene key to fuel & plastics
	///					
	/// Fe2O3 + 3CO -> 2Fe + 3CO2 + heat
	/// Fe2O3 + 3H2 -> 2Fe + 3H2O + heat
	/// Al2O3 + 3C -> 2Al + 3CO
	/// (>>)Heat + SiO2 + 2C -> Si + 2CO 
	///  (combustion) SiH4 + 2CO2 -> SiO2 + 2C + 2H2O + HEAT 
	public class classChemistry
	{
		static bool bolLoaded = false;
		public static bool Loaded { get { return bolLoaded; } }
		public static void Load()
		{
			classChemical_Element.buildElementTree();

			classChemical.load();

			classPropellant.load();
			bolLoaded = true;

		}

		public static double getMassFromComponentList(ref List<classMoleculeComponent> lMolecularComponents)
		{
			double dblRetVal = 0;
			for (int intComponentCounter = 0; intComponentCounter < lMolecularComponents.Count; intComponentCounter++)
				dblRetVal += lMolecularComponents[intComponentCounter].intNumber * lMolecularComponents[intComponentCounter].cElement.Mass;
			return dblRetVal;
		}

		public static List<classMoleculeComponent> getMoleculeComponents(string strMolecule)
		{
			List<classMoleculeComponent> lRetVal = new List<classMoleculeComponent>();
			int intStartNewSymbol = 0;
			while (intStartNewSymbol < strMolecule.Length)
			{
				if (cLibString.isNumeral(strMolecule[intStartNewSymbol]))
				{
					int intEndNumeral = intStartNewSymbol;
					while (cLibString.isNumeral(strMolecule[intEndNumeral])) intEndNumeral++;

					string strCoefficient = strMolecule.Substring(intStartNewSymbol, intEndNumeral - intStartNewSymbol);
					int intCoefficient = Convert.ToInt16(strCoefficient);
					string strSubFormula = strMolecule.Substring(intEndNumeral);
					List<classMoleculeComponent> lTerminatingList = getMoleculeComponents(strSubFormula);
					addTwoComponentLists(ref lRetVal, ref lTerminatingList, intCoefficient);
					return lRetVal;
				}
				else if (strMolecule[intStartNewSymbol] == '(')
				{
					int intOpenCount = 1;
					int intCloseCount = 0;
					int intCharCounter = intStartNewSymbol + 1;
					while (intOpenCount != intCloseCount && intCharCounter < strMolecule.Length)
					{
						if (strMolecule[intCharCounter] == '(') intOpenCount++;
						if (strMolecule[intCharCounter] == ')') intCloseCount++;
						intCharCounter++;
					}

					int intCloseBracket = intCharCounter;
					string strSubMolecule = strMolecule.Substring(intStartNewSymbol + 1, intCharCounter - intStartNewSymbol - 2);

					while (intCharCounter < strMolecule.Length && cLibString.isNumeral(strMolecule[intCharCounter])) intCharCounter++;
					int intFactor = 1;
					if (intCloseBracket != intCharCounter)
					{
						string strFactor = strMolecule.Substring(intCloseBracket, intCharCounter - intCloseBracket);
						intFactor = Convert.ToInt16(strFactor);
					}

					List<classMoleculeComponent> lSubList = getMoleculeComponents(strSubMolecule);
					addTwoComponentLists(ref lRetVal, ref lSubList, intFactor);
					intStartNewSymbol = intCharCounter;
				}
				else
				{
					int intSymbol = intStartNewSymbol + 1;
					while (intSymbol < strMolecule.Length && cLibString.isAlpha(strMolecule[intSymbol]) && !cLibString.isUpperCase(strMolecule[intSymbol]))
						intSymbol++;
					string strSymbol = strMolecule.Substring(intStartNewSymbol, intSymbol - intStartNewSymbol);
					classChemical_Element cElement = classChemical_Element.getElementFromSymbol(strSymbol);

					int intNumber = 1;
					int intNumIndex = intSymbol;
					while (intNumIndex < strMolecule.Length && cLibString.isNumeral(strMolecule[intNumIndex])) intNumIndex++;
					if (intNumIndex > intSymbol)
					{
						string strNumber = strMolecule.Substring(intSymbol, intNumIndex - intSymbol);
						intNumber = Convert.ToInt16(strNumber);
					}

					classMoleculeComponent cNewMolecule = new classMoleculeComponent(ref cElement, intNumber);
					addComponentToList(ref lRetVal, ref cNewMolecule);

					intStartNewSymbol = intNumIndex;
				}
			}

			// debug- test list
			/*
			for (int intComponentCounter = 0; intComponentCounter < lRetVal.Count; intComponentCounter++)
			{
				if (lRetVal[intComponentCounter].cElement == null)
				{
					MessageBox.Show("invalid molecule component");
				}
			}
			*/

			return lRetVal;
		}
		/// <summary>
		/// multiplies the components of subList then adds product to mainList
		/// </summary>
		static void addTwoComponentLists(ref List<classMoleculeComponent> lcMainList, ref List<classMoleculeComponent> lSubList, int intMultiplesOfSubList)
		{
			for (int intComponentCounter = 0; intComponentCounter < lSubList.Count; intComponentCounter++)
			{
				classMoleculeComponent cThisComponent = lSubList[intComponentCounter];
				cThisComponent.intNumber *= intMultiplesOfSubList;
				addComponentToList(ref lcMainList, ref cThisComponent);
			}
		}

		static void addComponentToList(ref List<classMoleculeComponent> lcMoleculeComponents, ref classMoleculeComponent cNewComponent)
		{
			for (int intComponentCounter = 0; intComponentCounter < lcMoleculeComponents.Count; intComponentCounter++)
			{
				classMoleculeComponent cThisComponent = lcMoleculeComponents[intComponentCounter];
				if (cThisComponent.cElement == cNewComponent.cElement)
				{
					cThisComponent.intNumber += cNewComponent.intNumber;
					return;
				}
			}
			lcMoleculeComponents.Add(cNewComponent);
		}
	}

	public class classChemical
	{
		public const string conXmlMolecules_Root = "root";
		public const string conXmlMolecules_Molecule = "Molecule";
		public const string conXmlMolecules_Formula = "Formula";
		public const string conXmlMolecules_Name = "Name";
		public const string conXmlMolecules_Mass = "Mass";

		public const string conChemicalsFlename = "Chemicals.xml";

		Bitmap bmp = null;
		static GraphicText.classMyFont cGrFont = new GraphicText.classMyFont(new Font("courier",12), Color.Black);
		public Bitmap image
		{
			get
			{
				if (bmp == null)
				{
					if (Formula.Contains("("))
					{ }
					bmp = GraphicText.classGraphicText.getImageChemicalFormula(Formula, new Font("courier", 12), Color.Black);
				}
				return bmp;
			}
		}

		public string Formula;
		public string Name;
		public double Mass;
		public List<classMoleculeComponent> lComponents = new List<classMoleculeComponent>();

		static classTernaryTreeLeaf cTernaryTree_Name = null;
		static classTernaryTreeLeaf cTernaryTree_Formula = null;
		static classTernaryTreeLeaf cMinerals_TernaryTree = null;

		public static void clearTrees()
		{
			cTernaryTree_Name = null;
			cTernaryTree_Formula = null;
		}

		public static void load()
		{
			XmlDocument xDocChemicals = new XmlDocument();
			xDocChemicals.Load(formMarsMission.strWorkingDirectory + conChemicalsFlename);
			XmlNode xRoot = xDocChemicals.FirstChild;
			XmlNodeList xlstChemicals = xRoot.ChildNodes;
			
			for (int intChemicalCounter = 0; intChemicalCounter < xlstChemicals.Count; intChemicalCounter++)
			{
				XmlNode xChemical = xlstChemicals[intChemicalCounter];
				classChemical cChemical = classChemical.fromXmlNode(ref xChemical); // adds this chemical to the trees
			}
			XmlDocument xDocMinerals = new XmlDocument();
			xDocMinerals.Load(formMarsMission.strWorkingDirectory + "Minerals.xml");
			XmlNode xRoot_Minerals = xDocMinerals.FirstChild;
			XmlNodeList xlstMinerals = xRoot_Minerals.ChildNodes;
			for (int intChemicalCounter = 0; intChemicalCounter < xlstMinerals.Count; intChemicalCounter++)
			{
				XmlNode xMineral = xlstMinerals[intChemicalCounter];
				classChemical cMineral = classChemical.fromXmlNode(ref xMineral); // adds this chemical to the trees
				InsertIntoTernaryTree_Formula(ref cMinerals_TernaryTree, ref cMineral);
			}

		}

		public static List<classChemical> getMinerals()
		{
			List<classChemical> lcRetVal = new List<classChemical>();
			getSubTree_Formula(ref cMinerals_TernaryTree, "");
			return lcRetVal;
		}

		public static bool isAMineral(string strFormula)
		{
			classChemical cMineral = GetChemical_ByFormula(ref cMinerals_TernaryTree, strFormula);
			return (cMineral != null);
		}

		public classChemical(string formula, string name)
		{
			Formula = formula;
			Name = name;
			lComponents = classChemistry.getMoleculeComponents(formula);

			Mass = classChemistry.getMassFromComponentList(ref lComponents);

			classChemical cMyReference = this;
			InsertIntoTernaryTree_Name(ref cMyReference);
			InsertIntoTernaryTree_Formula(ref cMyReference);
		}

		public classChemical(string formula, string name, double mass)
		{
			Formula = formula;
			Name = name;
			Mass = mass;
			lComponents = classChemistry.getMoleculeComponents(formula);

			classChemical cMyReference = this;
			InsertIntoTernaryTree_Name(ref cMyReference);
			InsertIntoTernaryTree_Formula(ref cMyReference);
		}

		public class classTernaryTreeLeaf
		{
			public classTernaryTreeLeaf up;
			public classTernaryTreeLeaf down;
			public classTernaryTreeLeaf next;

			public char chr;
			public classChemical Chemical;

			public classTernaryTreeLeaf(char c) 
			{
				chr = c;
			}
		}

		/// <summary>
		/// returns a list of chemicals that have formulae which start with the input parameter string
		/// </summary>
		/// <param name="strFormula"></param>
		/// <returns></returns>
		static List<classChemical> getSubTree_Formula(string strFormula) { return getSubTree_Formula(ref cTernaryTree_Formula, strFormula); }
		static List<classChemical> getSubTree_Formula(ref classTernaryTreeLeaf cTree, string strFormula)
		{			
			classTernaryTreeLeaf cThisLeaf = cTree;
			classTernaryTreeLeaf cPreviousLeaf = cThisLeaf;
			int intCharCounter = 0;
			while (intCharCounter < strFormula.Length && cThisLeaf != null)
			{
				if (strFormula[intCharCounter] > cThisLeaf.chr)
				{
					if (cThisLeaf.down == null) 
						goto traverseSubTree;
					else
						cThisLeaf = cThisLeaf.down;
				}
				else if (strFormula[intCharCounter] < cThisLeaf.chr)
				{
					if (cThisLeaf.up == null) 
						goto traverseSubTree;
					else
						cThisLeaf = cThisLeaf.up;
				}
				else
				{
					cPreviousLeaf = cThisLeaf;
					intCharCounter++;
					if (intCharCounter < strFormula.Length)
					{
						if (cThisLeaf.next == null) goto traverseSubTree;
						cThisLeaf = cThisLeaf.next;
					}
				} 
			}

		traverseSubTree:
			List<classChemical> lcRetVal = new List<classChemical>();
			if (cPreviousLeaf != null)
			{
				if (cPreviousLeaf.Chemical != null 
					&& cPreviousLeaf.Chemical.Formula.Length >= strFormula.Length 
					&& cPreviousLeaf.Chemical.Formula.Substring(0, strFormula.Length) == strFormula)
					lcRetVal.Add(cPreviousLeaf.Chemical);
				if (cPreviousLeaf.next != null)
				getSubTree_Formula(ref cPreviousLeaf.next, ref lcRetVal, strFormula);
			}
			return lcRetVal;
		}

		static List<classChemical> lcChemicalList = null;
		public static List<classChemical> ChemicalList
		{
			get
			{
				if (lcChemicalList == null)
				{
					lcChemicalList = new List<classChemical>();
					getSubTree_Formula(ref cTernaryTree_Formula, ref lcChemicalList, "");
				}
				return lcChemicalList;
			}
		}

		static void getSubTree_Formula(ref classTernaryTreeLeaf cTLeaf, ref List<classChemical> lcChemicals, string strSearchKey)
		{
			if (cTLeaf.up != null) getSubTree_Formula(ref cTLeaf.up, ref lcChemicals, strSearchKey);
			if (cTLeaf.Chemical != null && cTLeaf.Chemical.Formula.Length >= strSearchKey.Length && cTLeaf.Chemical.Formula.Substring(0, strSearchKey.Length) == strSearchKey) lcChemicals.Add(cTLeaf.Chemical);
			if (cTLeaf.next != null) getSubTree_Formula(ref cTLeaf.next, ref lcChemicals, strSearchKey);
			if (cTLeaf.down != null) getSubTree_Formula(ref cTLeaf.down, ref lcChemicals, strSearchKey);
		}

		public static List<classChemical> getSubTree(string strSearchKey) { return getSubTree(strSearchKey, enuChemistry_TypeChemcalTreeSearch.Formula); }
		public static List<classChemical> getSubTree(string strSearchKey, enuChemistry_TypeChemcalTreeSearch eSearchMode)
		{
			switch (eSearchMode)
			{
				case enuChemistry_TypeChemcalTreeSearch.Formula:
					return getSubTree_Formula(strSearchKey);

				case enuChemistry_TypeChemcalTreeSearch.Name:
					strSearchKey = strSearchKey.ToLower();
					return getSubTree_Name(strSearchKey);

				default:
				case enuChemistry_TypeChemcalTreeSearch.Mineral:
					return new List<classChemical>();
			}
		}

		/// <summary>
		/// returns a list of chemicals that have Namee which start with the input parameter string
		/// </summary>
		/// <param name="strName"></param>
		/// <returns></returns>
		static List<classChemical> getSubTree_Name(string strName) { return getSubTree_Name(ref cTernaryTree_Name, strName); }
		static List<classChemical> getSubTree_Name(ref classTernaryTreeLeaf cTree, string strName)
		{	
			classTernaryTreeLeaf cThisLeaf = cTree;
			classTernaryTreeLeaf cPreviousLeaf = cThisLeaf;
			int intCharCounter = 0;
			while (intCharCounter < strName.Length && cThisLeaf != null)
			{
				if (strName[intCharCounter] > cThisLeaf.chr)
				{
					if (cThisLeaf.down == null)
						goto traverseSubTree;
					else
						cThisLeaf = cThisLeaf.down;
				}
				else if (strName[intCharCounter] < cThisLeaf.chr)
				{
					if (cThisLeaf.up == null)
						goto traverseSubTree;
					else
						cThisLeaf = cThisLeaf.up;
				}
				else
				{
					cPreviousLeaf = cThisLeaf;
					intCharCounter++;
					if (intCharCounter < strName.Length)
					{
						if (cThisLeaf.next == null) goto traverseSubTree;
						cThisLeaf = cThisLeaf.next;
					}
				}
			}

		traverseSubTree:
			List<classChemical> lcRetVal = new List<classChemical>();
			if (cPreviousLeaf != null)
			{
				if (cPreviousLeaf.Chemical != null
					&& cPreviousLeaf.Chemical.Name.Length >= strName.Length
					&& cPreviousLeaf.Chemical.Name.Substring(0, strName.Length) == strName)
					lcRetVal.Add(cPreviousLeaf.Chemical);
				if (cPreviousLeaf.next != null)
					getSubTree_Name(ref cPreviousLeaf, ref lcRetVal, strName);
			}
			return lcRetVal;
		}

		static void getSubTree_Name(ref classTernaryTreeLeaf cTLeaf, ref List<classChemical> lcChemicals, string strSearchKey)
		{
			if (cTLeaf.up != null) getSubTree_Name(ref cTLeaf.up, ref lcChemicals, strSearchKey);
			if (cTLeaf.Chemical != null && cTLeaf.Chemical.Name.Length >= strSearchKey.Length && cTLeaf.Chemical.Name.ToLower().Substring(0, strSearchKey.Length) == strSearchKey.ToLower()) lcChemicals.Add(cTLeaf.Chemical);
			if (cTLeaf.next != null) getSubTree_Name(ref cTLeaf.next, ref lcChemicals, strSearchKey);
			if (cTLeaf.down != null) getSubTree_Name(ref cTLeaf.down, ref lcChemicals, strSearchKey);
		}

		public static bool InsertIntoTernaryTree(ref classChemical cNewChemical, enuChemistry_TypeChemcalTreeSearch TypeTree)
		{
			switch (TypeTree)
			{
				case enuChemistry_TypeChemcalTreeSearch.Name:
					return InsertIntoTernaryTree_Name(ref cNewChemical);

				case enuChemistry_TypeChemcalTreeSearch.Formula:
					return InsertIntoTernaryTree_Formula(ref cNewChemical);
			}
			return false;
		}

		static bool InsertIntoTernaryTree_Formula(ref classChemical cNewChemical) { return InsertIntoTernaryTree_Formula(ref cTernaryTree_Formula, ref cNewChemical); }
		static bool InsertIntoTernaryTree_Formula(ref classTernaryTreeLeaf cTree, ref classChemical cNewChemical)
		{
			if (cTree == null)
			{
				classTernaryTreeLeaf cTLeaf
					= cTree
					= new classTernaryTreeLeaf(cNewChemical.Formula[0]);
				int intCharCounter = 1;

				while (intCharCounter < cNewChemical.Formula.Length)
				{
					cTLeaf.next = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
					cTLeaf = cTLeaf.next;
				}
				cTLeaf.Chemical = cNewChemical;
				return true;
			}
			else
			{
				classTernaryTreeLeaf cTLeaf = cTree;

				int intCharCounter = 0;
				while (intCharCounter < cNewChemical.Formula.Length)
				{
					if (cNewChemical.Formula[intCharCounter] > cTLeaf.chr)
					{
						if (cTLeaf.down == null)
						{
							cTLeaf.down = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
							cTLeaf = cTLeaf.down;
							while (intCharCounter < cNewChemical.Formula.Length)
							{
								cTLeaf.next = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
								cTLeaf = cTLeaf.next;
							}
							cTLeaf.Chemical = cNewChemical;
							return true;
						}
						else
							cTLeaf = cTLeaf.down;
					}
					else if (cNewChemical.Formula[intCharCounter] < cTLeaf.chr)
					{
						if (cTLeaf.up == null)
						{
							cTLeaf.up = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
							cTLeaf = cTLeaf.up;
							while (intCharCounter < cNewChemical.Formula.Length)
							{
								cTLeaf.next = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
								cTLeaf = cTLeaf.next;
							}
							cTLeaf.Chemical = cNewChemical;
							return true;
						}
						else
							cTLeaf = cTLeaf.up;
					}
					else
					{
						intCharCounter++;
						if (intCharCounter < cNewChemical.Formula.Length)
						{
							if (cTLeaf.next == null)
							{
								cTLeaf.next = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
								cTLeaf = cTLeaf.next;
								while (intCharCounter < cNewChemical.Formula.Length)
								{
									cTLeaf.next = new classTernaryTreeLeaf(cNewChemical.Formula[intCharCounter++]);
									cTLeaf = cTLeaf.next;
								}
								cTLeaf.Chemical = cNewChemical;
								return true;
							}
							else
								cTLeaf = cTLeaf.next;
						}
						else
							cTLeaf.Chemical = cNewChemical;
					}
				}
				return false;
			}
		}

		static bool InsertIntoTernaryTree_Name(ref classChemical cNewChemical)
		{
			//Debugger.classDebug.clear();
			if (cNewChemical.Name.Contains(" "))
			{
			}

			string strLCName = cNewChemical.Name.ToLower();

			//string strTabs = "";
			if (cTernaryTree_Name == null)
			{
				classTernaryTreeLeaf cTLeaf
					= cTernaryTree_Name
					= new classTernaryTreeLeaf(strLCName[0]);
				int intCharCounter = 1;

				while (intCharCounter < cNewChemical.Name.Length)
				{
					cTLeaf.next = new classTernaryTreeLeaf(strLCName[intCharCounter]);
					if (intCharCounter < cNewChemical.Name.Length)
						cTLeaf = cTLeaf.next;
					intCharCounter++;
				}
				cTLeaf.Chemical = cNewChemical;
				return true;
			}
			else
			{
				classTernaryTreeLeaf cTLeaf = cTernaryTree_Name;

				int intCharCounter = 0;
				//Debugger.classDebug.append(//strTabs+"root (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
				while (intCharCounter < cNewChemical.Name.Length)
				{
					if (strLCName[intCharCounter] > cTLeaf.chr)
					{
						if (cTLeaf.down == null)
						{
							cTLeaf.down = new classTernaryTreeLeaf(strLCName[intCharCounter++]);
							cTLeaf = cTLeaf.down;
							//Debugger.classDebug.append(//strTabs+"add down (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
							while (intCharCounter < cNewChemical.Name.Length)
							{
								cTLeaf.next = new classTernaryTreeLeaf(strLCName[intCharCounter++]);
								//strTabs += "\t";
								cTLeaf = cTLeaf.next;
								//Debugger.classDebug.append(//strTabs + "add next (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
							}
							cTLeaf.Chemical = cNewChemical;
							return true;
						}
						else
						{
							cTLeaf = cTLeaf.down;
							//Debugger.classDebug.append(//strTabs+"move down (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
						}
					}
					else if (strLCName[intCharCounter] < cTLeaf.chr)
					{
						if (cTLeaf.up == null)
						{
							cTLeaf.up = new classTernaryTreeLeaf(strLCName[intCharCounter++]);
							cTLeaf = cTLeaf.up;
							//Debugger.classDebug.append(//strTabs+"add up (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
							while (intCharCounter < cNewChemical.Name.Length)
							{
								cTLeaf.next = new classTernaryTreeLeaf(strLCName[intCharCounter++]);
								//strTabs += "\t";
								cTLeaf = cTLeaf.next;
								//Debugger.classDebug.append(//strTabs + "add next (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
							}
							cTLeaf.Chemical = cNewChemical;
							return true;
						}
						else
						{
							cTLeaf = cTLeaf.up;
							//Debugger.classDebug.append(//strTabs+"move up (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
						}
					}
					else
					{
						intCharCounter++;
						if (intCharCounter < cNewChemical.Name.Length)
						{
							if (cTLeaf.next == null)
							{
								cTLeaf.next = new classTernaryTreeLeaf(strLCName[intCharCounter++]);
								//strTabs += "\t";
								cTLeaf = cTLeaf.next;
								//Debugger.classDebug.append(//strTabs + "add next (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
								while (intCharCounter < cNewChemical.Name.Length)
								{
									cTLeaf.next = new classTernaryTreeLeaf(strLCName[intCharCounter++]);
									//strTabs += "\t";
									cTLeaf = cTLeaf.next;
									//Debugger.classDebug.append(//strTabs + "add next (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
								}
								cTLeaf.Chemical = cNewChemical;
								return true;
							}
							else
							{
								cTLeaf = cTLeaf.next;
								//strTabs += "\t";
								//Debugger.classDebug.append(//strTabs+"move next (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
							}
						}
						else
						{
							//Debugger.classDebug.append(//strTabs+"end (" + intCharCounter.ToString() + "):" + cTLeaf.chr);
							cTLeaf.Chemical = cNewChemical;
						}
					}
				}
				return false;
			}
		}

		public static classChemical GetChemical(string strSearchKey, enuChemistry_TypeChemcalTreeSearch TypeSearch)
		{
			switch (TypeSearch)
			{
				case enuChemistry_TypeChemcalTreeSearch.Name:
					return GetChemical_ByName(strSearchKey);

				case enuChemistry_TypeChemcalTreeSearch.Formula:
					return GetChemical_ByFormula(strSearchKey);

				case enuChemistry_TypeChemcalTreeSearch.Mineral:
					return GetChemical_ByFormula(ref cMinerals_TernaryTree, strSearchKey);
			}
			return null;
		}

		static classChemical GetChemical_ByName(string strName)
		{
			classTernaryTreeLeaf cThisLeaf = cTernaryTree_Name;
			int intCharCounter = 0;

			strName = strName.ToLower();

			while (intCharCounter < strName.Length && cThisLeaf != null)
			{
				if (strName[intCharCounter] > cThisLeaf.chr)
				{
					if (cThisLeaf.down == null) return null;
					else cThisLeaf = cThisLeaf.down;
				}
				else if (strName[intCharCounter] < cThisLeaf.chr)
				{
					if (cThisLeaf.up == null) return null;
					else cThisLeaf = cThisLeaf.up;
				}
				else
				{
					intCharCounter++;
					if (intCharCounter < strName.Length)
					{
						if (cThisLeaf.next == null) return null;
						cThisLeaf = cThisLeaf.next;
					}
				}
			}
			return cThisLeaf.Chemical;
		}
		static classChemical GetChemical_ByFormula(string strFormula) { return GetChemical_ByFormula(ref cTernaryTree_Formula, strFormula); }
		static classChemical GetChemical_ByFormula(ref classTernaryTreeLeaf cTree,string strFormula)
		{
			classTernaryTreeLeaf cThisLeaf = cTree;
			int intCharCounter = 0;
			while (intCharCounter < strFormula.Length && cThisLeaf != null)
			{
				if (strFormula[intCharCounter] > cThisLeaf.chr)
				{
					if (cThisLeaf.down == null) return null;
					else cThisLeaf = cThisLeaf.down;
				}
				else if (strFormula[intCharCounter] < cThisLeaf.chr)
				{
					if (cThisLeaf.up == null) return null;
					else cThisLeaf = cThisLeaf.up;
				}
				else
				{
					intCharCounter++;
					if (intCharCounter < strFormula.Length)
					{
						if (cThisLeaf.next == null) return null;
						cThisLeaf = cThisLeaf.next;
					}
				}
			}
			if (cThisLeaf == null) return null;
			return cThisLeaf.Chemical;
		}

		public static classChemical fromXmlNode(ref XmlNode xNode)
		{
			XmlNode xFormula = xNode.FirstChild;
			XmlNode xName = xFormula.NextSibling;
			XmlNode xMass = xName.NextSibling;
			classChemical cRetVal = new classChemical(xFormula.InnerText, xName.InnerText, Convert.ToDouble(xMass.InnerText));
			return cRetVal;
		}

		public XmlNode getXmlNode(ref XmlDocument xDoc)
		{
			XmlNode xRetVal = xDoc.CreateElement(conXmlMolecules_Molecule);
			XmlNode xFormula = xDoc.CreateElement(conXmlMolecules_Formula); xFormula.InnerText = Formula;
			XmlNode xName = xDoc.CreateElement(conXmlMolecules_Name); xName.InnerText = Name;
			XmlNode xMass = xDoc.CreateElement(conXmlMolecules_Mass); xMass.InnerText = Mass.ToString("f5");

			xRetVal.AppendChild(xFormula);
			xRetVal.AppendChild(xName);
			xRetVal.AppendChild(xMass);

			return xRetVal;
		}
	}

	public class classMoleculeComponent
	{
		public classChemical_Element cElement;
		public int intNumber;

		public classMoleculeComponent(ref classChemical_Element cChemicalElement, int Number)
		{
			cElement = cChemicalElement;
			intNumber = Number;
		}
	}

	public class classChemical_Element
	{
		public string Name;
		public string Symbol;
		public double Mass;
		public int intIndex;

		public classChemical_Element Left;
		public classChemical_Element Right;
		static public List<classChemical_Element> lElements = new List<classChemical_Element>();

		public classChemical_Element(classChemical cChemical)
		{
			Name = cChemical.Name;
			Symbol = cChemical.Formula;
			Mass = cChemical.Mass;
		}
		public static classChemical_Element cElementTree;
		public static void buildElementTree()
		{
			string strSourceFile = formMarsMission.strWorkingDirectory + "Chemical_Elements.xml";
			lElements = new List<classChemical_Element>();
			if (System.IO.File.Exists(strSourceFile))
			{
				XmlDocument xDoc = new XmlDocument();
				xDoc.Load(strSourceFile);
				XmlNodeList xlstChemicalElement = xDoc.FirstChild.ChildNodes;
				cElementTree = null;
				for (int intElementCounter = 0; intElementCounter < xlstChemicalElement.Count; intElementCounter++)
				{
					XmlNode xElement = xlstChemicalElement[intElementCounter];
					classChemical_Element cNewElement = new classChemical_Element(classChemical.fromXmlNode(ref xElement)); // inserts into Chemical trees
					
					cNewElement.intIndex = intElementCounter;
					
					lElements.Add(cNewElement);

					if (cElementTree == null)
						cElementTree = cNewElement;
					else
					{
						classChemical_Element cThisElement = cElementTree;
						while (true)
						{
							if (cNewElement.Symbol.CompareTo(cThisElement.Symbol) > 0)
							{
								if (cThisElement.Right == null)
								{
									cThisElement.Right = cNewElement;
									break;
								}
								cThisElement = cThisElement.Right;
							}
							else if (cNewElement.Symbol.CompareTo(cThisElement.Symbol) < 0)
							{
								if (cThisElement.Left == null)
								{
									cThisElement.Left = cNewElement;
									break;
								}
								cThisElement = cThisElement.Left;
							}
							else
							{ // this should not happen
								MessageBox.Show("error : classChemistry.buildElementTree() -> trying to add the same element to the tree more than once");
								break;
							}
						}
					}
				}
				for (int intElementCounter = 0; intElementCounter < lElements.Count; intElementCounter++)
				{
					classChemical cChemicalElement = classChemical.GetChemical(lElements[intElementCounter].Symbol, enuChemistry_TypeChemcalTreeSearch.Formula);
					if (cChemicalElement == null)
					{
						MessageBox.Show("Error : classChemistry.buildElementTree() assigning classChemical to all elements -> NULL result");
					}
					else
					{
						cChemicalElement.lComponents[0].cElement = lElements[intElementCounter];
					}
				}
			}
			else
			{
				System.Windows.Forms.MessageBox.Show("Error : chemical-elements file not found \"" + strSourceFile + "\"", "File Not Found");
			}
		}

		public static classChemical_Element getElementFromSymbol(string strSymbol)
		{
			classChemical_Element cThisElement = cElementTree;
			while (cThisElement != null)
			{
				if (strSymbol.CompareTo(cThisElement.Symbol) > 0)
				{
					if (cThisElement.Right == null) return null;
					cThisElement = cThisElement.Right;
				}
				else if (strSymbol.CompareTo(cThisElement.Symbol) < 0)
				{
					if (cThisElement.Left == null) return null;
					cThisElement = cThisElement.Left;
				}
				else
				{
					return cThisElement;
				}
			}
			return cThisElement;
		}

	}

	public class classPropellant
	{
		public bool bolFlag = false;
		classChemical cOxidizer;
		public classChemical Oxidizer { get { return cOxidizer; } }

		classChemical cFuel;
		public classChemical Fuel { get { return cFuel; } }

		double dblForce;
		public double Force { get { return dblForce; } }

		double dblRatio;
		public double Ratio { get { return dblRatio; } }

		string strTernaryTreeSearchKey = "";
		public string TernaryTreeSearchKey
		{
			get
			{
				return strTernaryTreeSearchKey;
			}
		}

		public static classPropellant fromXmlNode(ref XmlNode xNode)
		{
			XmlNode xOxidizer = xNode.FirstChild;
			XmlNode xFuel = xOxidizer.NextSibling;
			XmlNode xForce = xFuel.NextSibling;
			XmlNode xRatio = xForce.NextSibling;

			classPropellant cRetVal = new classPropellant(xOxidizer.InnerText, xFuel.InnerText, Convert.ToDouble(xForce.InnerText), Convert.ToDouble(xRatio.InnerText));
			return cRetVal;
		}

		public classPropellant()
		{
			strTernaryTreeSearchKey = "+";
		}

		public static void load()
		{
			XmlDocument xDocPropellants = new XmlDocument();
			string strFilename = formMarsMission.strWorkingDirectory + "bipropellants.xml";
			if (System.IO.File.Exists(strFilename))
			{
				xDocPropellants.Load(strFilename);
				XmlNode xRoot = xDocPropellants.DocumentElement;
				XmlNodeList xlstPropellants = xRoot.ChildNodes;
				for (int intPropellantCounter = 0; intPropellantCounter < xlstPropellants.Count; intPropellantCounter++)
				{
					XmlNode xThisPropellant = xlstPropellants[intPropellantCounter];
					classPropellant cPropellant = classPropellant.fromXmlNode(ref xThisPropellant);
					InsertIntoTernaryTree(ref cPropellant);
				}
			}
		}

		classPropellant(string strOxidizer, string strFuel, double _dblForce, double _dblRatio)
		{
			cOxidizer = classChemical.GetChemical(strOxidizer, enuChemistry_TypeChemcalTreeSearch.Formula);
			cFuel = classChemical.GetChemical(strFuel, enuChemistry_TypeChemcalTreeSearch.Formula);
			dblForce = _dblForce;
			dblRatio = _dblRatio;
			strTernaryTreeSearchKey = Oxidizer.Formula + "+" + Fuel.Formula;
		}

		#region "ternary tree"
		static classTernaryTreeLeaf cTree =null;
		public class classTernaryTreeLeaf
		{
			public classTernaryTreeLeaf up;
			public classTernaryTreeLeaf down;
			public classTernaryTreeLeaf next;

			public char chr;
			public classPropellant Propellant;

			public classTernaryTreeLeaf(char c) { chr = c; }
		}

		static bool InsertIntoTernaryTree(ref classPropellant cNewPropellant)
		{
			if (cTree == null)
			{
				classTernaryTreeLeaf cTLeaf
					= cTree
					= new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[0]);
				int intCharCounter = 1;

				while (intCharCounter < cNewPropellant.TernaryTreeSearchKey.Length)
				{
					cTLeaf.next = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
					cTLeaf = cTLeaf.next;
				}
				cTLeaf.Propellant = cNewPropellant;
				return true;
			}
			else
			{
				classTernaryTreeLeaf cTLeaf = cTree;

				int intCharCounter = 0;
				while (intCharCounter < cNewPropellant.TernaryTreeSearchKey.Length)
				{
					if (cNewPropellant.TernaryTreeSearchKey[intCharCounter] > cTLeaf.chr)
					{
						if (cTLeaf.down == null)
						{
							cTLeaf.down = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
							cTLeaf = cTLeaf.down;
							while (intCharCounter < cNewPropellant.TernaryTreeSearchKey.Length)
							{
								cTLeaf.next = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
								cTLeaf = cTLeaf.next;
							}
							cTLeaf.Propellant = cNewPropellant;
							return true;
						}
						else
							cTLeaf = cTLeaf.down;
					}
					else if (cNewPropellant.TernaryTreeSearchKey[intCharCounter] < cTLeaf.chr)
					{
						if (cTLeaf.up == null)
						{
							cTLeaf.up = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
							cTLeaf = cTLeaf.up;
							while (intCharCounter < cNewPropellant.TernaryTreeSearchKey.Length)
							{
								cTLeaf.next = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
								cTLeaf = cTLeaf.next;
							}
							cTLeaf.Propellant = cNewPropellant;
							return true;
						}
						else
							cTLeaf = cTLeaf.up;
					}
					else
					{
						intCharCounter++;
						if (intCharCounter < cNewPropellant.TernaryTreeSearchKey.Length)
						{
							if (cTLeaf.next == null)
							{
								cTLeaf.next = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
								cTLeaf = cTLeaf.next;
								while (intCharCounter < cNewPropellant.TernaryTreeSearchKey.Length)
								{
									cTLeaf.next = new classTernaryTreeLeaf(cNewPropellant.TernaryTreeSearchKey[intCharCounter++]);
									cTLeaf = cTLeaf.next;
								}
								cTLeaf.Propellant = cNewPropellant;
								return true;
							}
							else
								cTLeaf = cTLeaf.next;
						}
						else
							cTLeaf.Propellant = cNewPropellant;
					}
				}
				return false;
			}
		}

		public static classPropellant GetPropellant(ref classChemical cOxidizer, ref classChemical cFuel){return GetPropellant(cOxidizer.Formula, cFuel.Formula);}
		public static classPropellant GetPropellant(string strOxidizer, string strFuel) { return GetPropellant(strOxidizer + "+" + strFuel);}
		public static classPropellant GetPropellant(string strSearchKey)
		{	
			classTernaryTreeLeaf cThisLeaf = cTree;
			int intCharCounter = 0;
			while (intCharCounter < strSearchKey.Length && cThisLeaf != null)
			{
				if (strSearchKey[intCharCounter] > cThisLeaf.chr)
				{
					if (cThisLeaf.down == null) return null;
					else cThisLeaf = cThisLeaf.down;
				}
				else if (strSearchKey[intCharCounter] < cThisLeaf.chr)
				{
					if (cThisLeaf.up == null) return null;
					else cThisLeaf = cThisLeaf.up;
				}
				else
				{
					intCharCounter++;
					if (intCharCounter < strSearchKey.Length)
					{
						if (cThisLeaf.next == null) return null;
						cThisLeaf = cThisLeaf.next;
					}
				}
			}
			if (cThisLeaf == null) return null;
			if (cThisLeaf.Propellant.TernaryTreeSearchKey != strSearchKey) MessageBox.Show("error: classPropellant.GetPropellant() -> returning incorrect value \r\n search for " + strSearchKey + "\r\nreturning " + cThisLeaf.Propellant.TernaryTreeSearchKey);
			return cThisLeaf.Propellant;
		}
		#endregion
	}

	public class classChemicalInventory
	{
		public List<classChemicalInventoryItem> lcInventory = new List<classChemicalInventoryItem>();
		classChemicalInventoryItemTernaryTreeLeaf cTree;
		ulong ulngCapacity = 10000000;
		public ulong Capacity
		{
			get { return ulngCapacity; }
			set
			{
				ulngCapacity = value;
			}
		}
		ulong ulngContent = 0;

		formChemicalInventory _frmChemicalInventory;
		public formChemicalInventory frmChemicalInventory
		{
			get { return _frmChemicalInventory; }
			set
			{
				_frmChemicalInventory = value;
				for (int intItemCounter = 0; intItemCounter < lcInventory.Count; intItemCounter++)
					lcInventory[intItemCounter].frmChemicalInventory = _frmChemicalInventory;
			}
		}

		public classChemicalInventory Copy()
		{
			classStructureInterior cSI_reference = SI;
			classChemicalInventory cRetVal = new classChemicalInventory(ref cSI_reference);

			for (int intChemCounter = 0; intChemCounter < lcInventory.Count; intChemCounter++)
			{
				cRetVal.addChemicalInventory(lcInventory[intChemCounter].Formula, lcInventory[intChemCounter].Quantity);
			}
			return cRetVal;
		}
		
		public void killEvents()
		{
			for (int intItemCounter = 0; intItemCounter < lcInventory.Count; intItemCounter++)
			{
				lcInventory[intItemCounter] = lcInventory[intItemCounter].Copy();
			}
			rebuildChemTree();
			if (SI != null)
			{
				if (SI.cShip != null)
				{
					if (SI.cShip.grbShipData != null)
					{
						SI.cShip.grbShipData.grbResources.lblwFuelGuage_Value.Ship = SI.cShip;
					}
				}
			}
		}

		public void setChemicalInventory(string strFormula, long lngQuantity)
		{
			classChemicalInventoryItem cExistingChemItem = GetChemicalItem(strFormula);
			if (cExistingChemItem != null)
			{
				long lngNewContent = (long)ulngContent - cExistingChemItem.Quantity + lngQuantity;
				if (lngNewContent < 0) lngNewContent = 0;
				else if (lngNewContent > (long)ulngCapacity) lngQuantity -= ((long)Math.Abs(lngNewContent - (long)ulngCapacity));
				ulngContent = (ulong)lngNewContent;
				cExistingChemItem.Quantity = lngQuantity;
			}
			else
			{
				if ((ulong)lngQuantity + ulngContent > ulngCapacity)
					lngQuantity -= (long)Math.Abs((double)lngQuantity + (double)ulngContent - (double)ulngCapacity);
				addChemicalInventory(strFormula, lngQuantity);
				ulngContent += (ulong)lngQuantity;
			}
		}

		public void addChemicalInventory(ref classChemicalInventoryItem cNewChemItem)
		{
			classChemicalInventoryItem cExistingChemItem = GetChemicalItem(cNewChemItem.Formula);

			if ((ulong)cNewChemItem.Quantity + ulngContent > ulngCapacity)
				cNewChemItem.Quantity -= (long)Math.Abs((double)cNewChemItem.Quantity + (double)ulngContent - (double)ulngCapacity);

			if (cExistingChemItem != null)
			{
				cExistingChemItem.Add(cNewChemItem.Quantity);
			}
			else
			{
				InsertIntoTernaryTree(ref cNewChemItem);
				lcInventory.Add(cNewChemItem);
			}
			ulngContent += (ulong)cNewChemItem.Quantity;
		}

		public void addChemicalInventory(string strFormula, long lngQuantity)
		{
			if ((ulong)lngQuantity + ulngContent > ulngCapacity)
				lngQuantity -= (long)Math.Abs((double)lngQuantity + (double)ulngContent - (double)ulngCapacity);

			classChemicalInventoryItem cNewChemItem = GetChemicalItem(strFormula);
			if (cNewChemItem == null)
			{
				cNewChemItem = new classChemicalInventoryItem(strFormula, lngQuantity);
				lcInventory.Add(cNewChemItem);
				InsertIntoTernaryTree(ref cNewChemItem);
				ulngContent += (ulong)cNewChemItem.Quantity;
				return;
			}
			else
			{
				cNewChemItem.Add(lngQuantity);
				ulngContent += (ulong)lngQuantity;
			}
		}

		public  long Available(string strFormula)
		{
			classChemicalInventoryItem cChemical = GetChemicalItem(strFormula);
			if (cChemical != null) return cChemical.Quantity;
			else return 0;
		}

		void rebuildChemTree()
		{
			cTree = null;
			for (int intChemCounter = 0; intChemCounter < lcInventory.Count; intChemCounter++)
			{
				classChemicalInventoryItem cThisChemItem = lcInventory[intChemCounter];
				InsertIntoTernaryTree(ref cThisChemItem);
			}
		}

		public bool ChemItemInInventory(string strFormula, int intQuantityNeeded)
		{
			classChemicalInventoryItem cChemInvItem = GetChemicalItem(strFormula);
			if (cChemInvItem != null) return cChemInvItem.Quantity >= intQuantityNeeded;
			else return false;
		}

		public void subChemInvItem(string strFormula, long lngQuantity)
		{
			classChemicalInventoryItem cChemInvItem = GetChemicalItem(strFormula);
			if (cChemInvItem != null)
			{
				cChemInvItem.Sub(lngQuantity);
				long lngNewContent = (long)ulngContent - lngQuantity;
				if (lngNewContent < 0) lngNewContent = 0;
				ulngContent = (ulong)lngNewContent;
				if (cChemInvItem.Quantity <= 0)
				{
					lcInventory.Remove(cChemInvItem);
					rebuildChemTree();
				}
			}
		}

		public classChemicalInventory(ref classStructureInterior cStructureInterior) { cSI = cStructureInterior; }
		public classChemicalInventory() { }

		classStructureInterior cSI = null;
		public classStructureInterior SI 
		{
			get { return cSI; }
			set { cSI = value; }
		}

		public static classChemicalInventory fromXmlNode(ref XmlNode xNode)
		{
			classChemicalInventory cRetVal = new classChemicalInventory();
			if (xNode != null)
			{
				for (int intChemicalCounter = 0; intChemicalCounter < xNode.ChildNodes.Count; intChemicalCounter++)
				{
					XmlNode xChemical = xNode.ChildNodes[intChemicalCounter];
					classChemicalInventoryItem cChemicalInventoryItem = classChemicalInventoryItem.fromXmlNode(ref xChemical);
					cRetVal.addChemicalInventory(ref cChemicalInventoryItem);
				}
			}
			return cRetVal;
		}

		public XmlNode getSaveGameXmlNode(ref XmlDocument xDoc)
		{
			XmlNode xRetVal = xDoc.CreateElement(classSaveGame.conXmlField_ChemicalInventory);
			if (lcInventory != null && lcInventory.Count > 0)
			{
				for (int intChemicalCounter = 0; intChemicalCounter < lcInventory.Count; intChemicalCounter++)
					xRetVal.AppendChild(lcInventory[intChemicalCounter].getSaveGameXmlNode(ref xDoc));
			}
			return xRetVal;
		}

		public bool InsertIntoTernaryTree(ref classChemicalInventoryItem cNewChemInvItem)
		{
			string strTabs = "";
			if (cTree == null)
			{
				classChemicalInventoryItemTernaryTreeLeaf cTLeaf
					= cTree
					= new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[0]);
				int intCharCounter = 1;

				while (intCharCounter < cNewChemInvItem.Formula.Length)
				{
					cTLeaf.next = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter]);
					if (intCharCounter < cNewChemInvItem.Formula.Length)
						cTLeaf = cTLeaf.next;
					intCharCounter++;
				}
				cTLeaf.cChemInvItem = cNewChemInvItem;
				return true;
			}
			else
			{
				classChemicalInventoryItemTernaryTreeLeaf cTLeaf = cTree;

				int intCharCounter = 0;
				while (intCharCounter < cNewChemInvItem.Formula.Length)
				{
					if (cNewChemInvItem.Formula[intCharCounter] > cTLeaf.chr)
					{
						if (cTLeaf.down == null)
						{
							cTLeaf.down = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter++]);
							cTLeaf = cTLeaf.down;
							while (intCharCounter < cNewChemInvItem.Formula.Length)
							{
								cTLeaf.next = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter++]);
								strTabs += "\t";
								cTLeaf = cTLeaf.next;
							}
							cTLeaf.cChemInvItem = cNewChemInvItem;
							return true;
						}
						else
						{
							cTLeaf = cTLeaf.down;
						}
					}
					else if (cNewChemInvItem.Formula[intCharCounter] < cTLeaf.chr)
					{
						if (cTLeaf.up == null)
						{
							cTLeaf.up = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter++]);
							cTLeaf = cTLeaf.up;
							while (intCharCounter < cNewChemInvItem.Formula.Length)
							{
								cTLeaf.next = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter++]);
								strTabs += "\t";
								cTLeaf = cTLeaf.next;
							}
							cTLeaf.cChemInvItem = cNewChemInvItem;
							return true;
						}
						else
						{
							cTLeaf = cTLeaf.up;
						}
					}
					else
					{
						intCharCounter++;
						if (intCharCounter < cNewChemInvItem.Formula.Length)
						{
							if (cTLeaf.next == null)
							{
								cTLeaf.next = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter++]);
								strTabs += "\t";
								cTLeaf = cTLeaf.next;
								while (intCharCounter < cNewChemInvItem.Formula.Length)
								{
									cTLeaf.next = new classChemicalInventoryItemTernaryTreeLeaf(cNewChemInvItem.Formula[intCharCounter++]);
									strTabs += "\t";
									cTLeaf = cTLeaf.next;
								}
								cTLeaf.cChemInvItem = cNewChemInvItem;
								return true;
							}
							else
							{
								cTLeaf = cTLeaf.next;
								strTabs += "\t";
							}
						}
						else
						{
							cTLeaf.cChemInvItem = cNewChemInvItem;
						}
					}
				}
				return false;
			}
		}

		public classChemicalInventoryItem GetChemicalItem(string strFormula)
		{
			classChemicalInventoryItemTernaryTreeLeaf cThisLeaf = cTree;
			int intCharCounter = 0;
			while (intCharCounter < strFormula.Length && cThisLeaf != null)
			{
				if (strFormula[intCharCounter] > cThisLeaf.chr)
				{
					if (cThisLeaf.down == null) return null;
					else cThisLeaf = cThisLeaf.down;
				}
				else if (strFormula[intCharCounter] < cThisLeaf.chr)
				{
					if (cThisLeaf.up == null) return null;
					else cThisLeaf = cThisLeaf.up;
				}
				else
				{
					intCharCounter++;
					if (intCharCounter < strFormula.Length)
					{
						if (cThisLeaf.next == null) return null;
						cThisLeaf = cThisLeaf.next;
					}
				}
			}
			if (cThisLeaf == null) return null;
			return cThisLeaf.cChemInvItem;
		}
	}

	public class formChemicalInventory : Form
	{
		public static formChemicalInventory frmChemicalInventory = new formChemicalInventory();
		public panelChemicalInventory pnlChemicalInventory = new panelChemicalInventory();
		public classChemicalInventory Inventory
		{
			get { return pnlChemicalInventory.Inventory; }
			set { pnlChemicalInventory.Inventory = value; }
		}

		public static void placeForm()
		{
			formChemicalInventory.frmChemicalInventory.ShowData();
			formChemicalInventory.frmChemicalInventory.Width = 245;
			formChemicalInventory.frmChemicalInventory.Left = Control.MousePosition.X + 10;
			if (formChemicalInventory.frmChemicalInventory.Right > Screen.PrimaryScreen.WorkingArea.Width)
				formChemicalInventory.frmChemicalInventory.Left = Screen.PrimaryScreen.WorkingArea.Width - formChemicalInventory.frmChemicalInventory.Width - 10;

			formChemicalInventory.frmChemicalInventory.Top = Control.MousePosition.Y + 10;
			if (formChemicalInventory.frmChemicalInventory.Bottom > Screen.PrimaryScreen.WorkingArea.Height)
				formChemicalInventory.frmChemicalInventory.Top = Control.MousePosition.Y - formChemicalInventory.frmChemicalInventory.Height - 5;
		}
	
		public static void buildFormChemicalInventory(ref classChemicalInventory cChemInv)
		{
			frmChemicalInventory = new formChemicalInventory();
			frmChemicalInventory.Width = 245;
			frmChemicalInventory.Left = Control.MousePosition.X + 10;
			if (frmChemicalInventory.Right > Screen.PrimaryScreen.WorkingArea.Width)
				frmChemicalInventory.Left = Screen.PrimaryScreen.WorkingArea.Width - frmChemicalInventory.Width - 10;

			frmChemicalInventory.Top = Control.MousePosition.Y + 10;
			if (frmChemicalInventory.Bottom > Screen.PrimaryScreen.WorkingArea.Height)
				frmChemicalInventory.Top = Control.MousePosition.Y - frmChemicalInventory.Height - 5;
			 
			frmChemicalInventory.Inventory = cChemInv;
		}

		public formChemicalInventory()
		{
			Size = new System.Drawing.Size(250, 180);
			Controls.Add(pnlChemicalInventory);
			pnlChemicalInventory.Dock = DockStyle.Fill;
			ShowInTaskbar = false;
			TopMost = true;
			FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
			pnlChemicalInventory.MouseLeave += new EventHandler(pnlChemicalInventory_MouseLeave);
			MouseLeave += new EventHandler(formChemicalInventory_MouseLeave);
			VisibleChanged += new EventHandler(formChemicalInventory_VisibleChanged);
			Disposed += new EventHandler(formChemicalInventory_Disposed);
		}

		void formChemicalInventory_Disposed(object sender, EventArgs e)
		{
			formMarsMission.frmMarsMission.grbShipData.grbResources.lbtnChemicalInventory.reset();
			formMarsMission.frmMarsMission.grbBuildingData.grbResources.lbtnChemicalInventory.reset();
		}

		void formChemicalInventory_VisibleChanged(object sender, EventArgs e)
		{
			if (Visible)
			{
				frmChemicalInventory.ShowData();
			}
			else
			{
			
			}
		}

		void formChemicalInventory_MouseLeave(object sender, EventArgs e) 
		{
				Hide();
		}

		public void ShowData()
		{
			if (!Visible)
			{
				Visible = true;
			}
			Left = MousePosition.X - 10;
			if (Right > Screen.PrimaryScreen.WorkingArea.Width)
				Left = Screen.PrimaryScreen.WorkingArea.Width - Width;
			Top = MousePosition.Y - 10;
			if (Bottom > Screen.PrimaryScreen.WorkingArea.Height)
				Top = Screen.PrimaryScreen.WorkingArea.Height - Height;
			pnlChemicalInventory.showData();
			int intHeightEachItem = 18, intMaxDisplay = 10;
			if (pnlChemicalInventory.Inventory.lcInventory.Count > intMaxDisplay) Height = intMaxDisplay * intHeightEachItem;
			else Height = pnlChemicalInventory.Inventory.lcInventory.Count * intHeightEachItem;			
		}

		void pnlChemicalInventory_MouseLeave(object sender, EventArgs e) 
		{
			Hide();
		}
	}

	public class panelChemicalInventory : CK_Controls.pictureboxVSB
	{
		public class classChemicalInventory_DisplayItemInfo
		{
			public event EventHandler<EventArgs> QuantityChanged;
			protected virtual void OnQuantityChanged(EventArgs e) { if (QuantityChanged != null) { QuantityChanged(this, e); } }
			
			public SolidBrush brBackColorBrush = null;
			public classChemicalInventoryItem cChemicalInventoryItem;
			
			public Rectangle recDrawQuantity = new Rectangle(0, 0, 60, 19);
			public classChemicalInventory_DisplayItemInfo(ref classChemicalInventoryItem ChemInvItem)
			{
				cChemicalInventoryItem = ChemInvItem;
				cChemicalInventoryItem.ValueChanged += new EventHandler<EventArgs>(cChemicalInventoryItem_ValueChanged);
			}

			void cChemicalInventoryItem_ValueChanged(object sender, EventArgs e) { OnQuantityChanged(new EventArgs()); }
		}

		classChemicalInventory cInventory;
		public classChemicalInventory Inventory
		{
			get { return cInventory; }
			set
			{
				if (cInventory != null) cInventory.killEvents();
				cInventory = value;
				if (formChemicalInventory.frmChemicalInventory  != null)
				{
					if (formChemicalInventory.frmChemicalInventory.pnlChemicalInventory == this)
					{
						if (formChemicalInventory.frmChemicalInventory.Inventory.SI != null && formChemicalInventory.frmChemicalInventory.Inventory.SI.cShip != null)
							formChemicalInventory.frmChemicalInventory.Text = formChemicalInventory.frmChemicalInventory.Inventory.SI.cShip.Name;
						else if (formChemicalInventory.frmChemicalInventory.Inventory.SI != null && formChemicalInventory.frmChemicalInventory.Inventory.SI.cBuilding != null)
							formChemicalInventory.frmChemicalInventory.Text = formChemicalInventory.frmChemicalInventory.Inventory.SI.cBuilding.Name;
					}
				}
				showData();
			}
		}
		public List<classChemicalInventory_DisplayItemInfo> lcChemInventory = new List<classChemicalInventory_DisplayItemInfo>();
		Bitmap bmpOutput = null;
		public const int conItem_Height = 17;
		bool bolNeedsRedraw = false;
		Graphics g = null;

		public panelChemicalInventory()
		{
			Width = 225;
			Height = 0;
		}

		public void addChemicalInventoryItem(ref classChemicalInventoryItem cNewChemInvItem)
		{
			classChemicalInventory_DisplayItemInfo cNewComponent = new classChemicalInventory_DisplayItemInfo(ref cNewChemInvItem);
			cNewComponent.recDrawQuantity.X = 110;
			cNewComponent.recDrawQuantity.Y = lcChemInventory.Count * conItem_Height;
			cNewComponent.QuantityChanged +=new EventHandler<EventArgs>(cNewComponent_QuantityChanged); 
			cNewComponent.brBackColorBrush = (lcChemInventory.Count % 2 == 0) ? classPensAndBrushes.brWhite : classPensAndBrushes.brYellow;
			lcChemInventory.Add(cNewComponent);
			bolNeedsRedraw = true;
		}

		void cNewComponent_QuantityChanged(object sender, EventArgs e)
		{
			if (bolNeedsRedraw) { showData(); }
			else
			{
				classChemicalInventory_DisplayItemInfo cChangedComponent = (classChemicalInventory_DisplayItemInfo)sender;
				drawComponentQuantity(ref cChangedComponent);
				setImage(ref bmpOutput);
			}
		}

		classMyFont cgrFont = new classMyFont(new Font("courier", 8), Color.Black);
		void redraw()
		{
			if (!Visible) return;
			bolNeedsRedraw = false;
			bool bolRebuildList = (lcChemInventory.Count != Inventory.lcInventory.Count);
			if (!bolRebuildList)
			{
				for (int intInventoryItemCounter =0; intInventoryItemCounter < Inventory.lcInventory.Count; intInventoryItemCounter++)
					if (lcChemInventory[intInventoryItemCounter].cChemicalInventoryItem != Inventory.lcInventory[intInventoryItemCounter])
					{
						bolRebuildList = true;
						break;
					}
			}
			if (bolRebuildList)
			{
				Inventory.killEvents();
				lcChemInventory = new List<classChemicalInventory_DisplayItemInfo>();
				for (int intInventoryItemCounter =0; intInventoryItemCounter < Inventory.lcInventory.Count; intInventoryItemCounter++)
				{
					classChemicalInventoryItem cChemInvItem = Inventory.lcInventory[intInventoryItemCounter];
					classChemicalInventory_DisplayItemInfo cChemInvDisplayInfo = new classChemicalInventory_DisplayItemInfo(ref cChemInvItem);

					cChemInvDisplayInfo.recDrawQuantity.Y = intInventoryItemCounter * conItem_Height;
					cChemInvDisplayInfo.recDrawQuantity.X = 120;
					cChemInvDisplayInfo.brBackColorBrush = intInventoryItemCounter % 2 == 0 
																				? classPensAndBrushes.brWhite 
																				: classPensAndBrushes.brLightGray;
					cChemInvDisplayInfo.QuantityChanged += new EventHandler<EventArgs>(cNewComponent_QuantityChanged);

					lcChemInventory.Add(cChemInvDisplayInfo);
				}
			}

			g.FillRectangle(classPensAndBrushes.brWhite, new Rectangle(0, 0, bmpOutput.Width, bmpOutput.Height));
			for (int intComponentCounter = 0; intComponentCounter < lcChemInventory.Count; intComponentCounter++)
			{
				classChemicalInventory_DisplayItemInfo cThisComponent = lcChemInventory[intComponentCounter];

				g.FillRectangle(cThisComponent.brBackColorBrush, new Rectangle(0, cThisComponent.recDrawQuantity.Y, Width, panelChemicalInventory.conItem_Height));

				Bitmap bmpFormula = cThisComponent.cChemicalInventoryItem.Chemical.image;
				double dblAspectRation_formula_YOverX = (double)bmpFormula.Height / (double)bmpFormula.Width;
				Rectangle recDest = new Rectangle(0, cThisComponent.recDrawQuantity.Y, (int)(dblAspectRation_formula_YOverX * bmpFormula.Width), conItem_Height);
				Rectangle recSrc = new Rectangle(0, 0, bmpFormula.Width, bmpFormula.Height);
				g.DrawImage(bmpFormula, recDest, recSrc, GraphicsUnit.Pixel);
				
				Bitmap bmpName = classGraphicText.getWordImage(cThisComponent.cChemicalInventoryItem.Chemical.Name, cgrFont);
				g.DrawImage(bmpName, new Point(bmpOutput.Width - bmpName.Width - 2, cThisComponent.recDrawQuantity.Y));

				drawComponentQuantity(ref cThisComponent);
			}
			setImage(ref bmpOutput);
		}

		void drawComponentQuantity(ref classChemicalInventory_DisplayItemInfo cComponentDisplayInfo)
		{
			if (!Visible) return;
			Bitmap bmpQuantity = classGraphicText.getWordImage(cComponentDisplayInfo.cChemicalInventoryItem.Quantity > 0 
																										? cComponentDisplayInfo.cChemicalInventoryItem.Quantity.ToString("###,###,###,###")
																										: "0",
															   cgrFont);
			g.FillRectangle(cComponentDisplayInfo.brBackColorBrush, new Rectangle(cComponentDisplayInfo.recDrawQuantity.X - cComponentDisplayInfo.recDrawQuantity.Width , cComponentDisplayInfo.recDrawQuantity.Y, cComponentDisplayInfo.recDrawQuantity.Width, conItem_Height));
			g.DrawImage(bmpQuantity, new Point(cComponentDisplayInfo.recDrawQuantity.Location.X - bmpQuantity.Width, cComponentDisplayInfo.recDrawQuantity.Location.Y));
		}

		public void clear()
		{
			lcChemInventory = new List<classChemicalInventory_DisplayItemInfo>();
			bolNeedsRedraw = true;
			showData();
		}

		public void showData()
		{
			if (Inventory != null && Inventory.lcInventory.Count > 0)
			{
				pic.Height = Inventory.lcInventory.Count * conItem_Height;
				bmpOutput = new Bitmap(pic.Width, pic.Height);
				g = Graphics.FromImage(bmpOutput);

				redraw();
			}
			else
				bmpOutput = null;
		}
	}

	public class classChemicalInventoryItem
	{
		public static int intUniqueID = 0;
		public int intMyUniqueID = intUniqueID++;
		public static classChemicalInventoryItem cDebugItem_Original = null, cDebugItem_Copy=null;

		public event EventHandler<EventArgs> ValueChanged;
		protected virtual void OnValueChanged(EventArgs e) { if (ValueChanged != null) { ValueChanged(this, e); } }
		public Label lblInventory = null;

		public string Formula;
		long lngQuantity;
		public long Quantity
		{
			get { return lngQuantity; }
			set
			{
				lngQuantity = value;
				OnValueChanged(new EventArgs());
			}
		}

		public void Add(long lngAdd)
		{
			lngQuantity += lngAdd;
			OnValueChanged(new EventArgs());
		}

		public void Sub(long lngSub)
		{
			if (lngQuantity < lngSub)
				lngSub = lngQuantity;
			lngQuantity -= lngSub;
			OnValueChanged(new EventArgs());
		}

		public classChemical Chemical;
		formChemicalInventory _frmChemicalInventory;
		public formChemicalInventory frmChemicalInventory
		{
			get { return _frmChemicalInventory; }
			set
			{
				_frmChemicalInventory = value;
			}
		}

		public classChemicalInventoryItem Copy()
		{
			return new classChemicalInventoryItem(Formula, Quantity);
		}

		public static classChemicalInventoryItem fromXmlNode(ref XmlNode xNode)
		{
			XmlNode xChemcalInventoryItem_Formula = xNode.FirstChild;
			XmlNode xChemicalInventorItem_Quantity = xChemcalInventoryItem_Formula.NextSibling;

			return new classChemicalInventoryItem(xChemcalInventoryItem_Formula.InnerText, Convert.ToInt64(xChemicalInventorItem_Quantity.InnerText));
		}

		public XmlNode getSaveGameXmlNode(ref XmlDocument xDoc)
		{
			XmlElement xChemicalInventoryItem = xDoc.CreateElement(classSaveGame.conXmlField_ChemicalInventoryItem);

			XmlNode xChemicalInventoryItem_Formula = xDoc.CreateElement(classSaveGame.conXmlField_ChemicalInventoryItem_Formula);
			xChemicalInventoryItem_Formula.InnerText = Formula;
			xChemicalInventoryItem.AppendChild(xChemicalInventoryItem_Formula);

			XmlNode xChemicalInventoryItem_Quantity = xDoc.CreateElement(classSaveGame.conXmlField_ChemicalInventoryItem_Quantity);
			xChemicalInventoryItem_Quantity.InnerText = Quantity.ToString();
			xChemicalInventoryItem.AppendChild(xChemicalInventoryItem_Quantity);

			return xChemicalInventoryItem;
		}

		public classChemicalInventoryItem(string strFormula, long lngQuantity)
		{
			Formula = strFormula;
			Quantity = lngQuantity;
			Chemical = classChemical.GetChemical(Formula, enuChemistry_TypeChemcalTreeSearch.Formula);
		}
	}

	public class classChemicalInventoryItemTernaryTreeLeaf
	{
		public classChemicalInventoryItemTernaryTreeLeaf up;
		public classChemicalInventoryItemTernaryTreeLeaf down;
		public classChemicalInventoryItemTernaryTreeLeaf next;

		public char chr;
		public classChemicalInventoryItem cChemInvItem;

		public classChemicalInventoryItemTernaryTreeLeaf(char c)
		{
			chr = c;
		}
	}

	public class classEnergy
	{
		public enuEnergy eEnergy;
		public long lngQuantity;

		public classEnergy(enuEnergy EnergyType, long Quantity)
		{
			eEnergy = EnergyType;
			lngQuantity = Quantity;
		}
		public classEnergy() { }

		public classEnergy Copy()
		{
			classEnergy cRetVal = new classEnergy(eEnergy, lngQuantity);
			return cRetVal;
		}

		public XmlNode getSaveGameXmlNode(ref XmlDocument xDoc)
		{
			XmlNode xEnergyItem = xDoc.CreateElement("EnergyItem");

			XmlNode xEnergyItem_Type = xDoc.CreateElement("Type");
			xEnergyItem_Type.InnerText = eEnergy.ToString();
			xEnergyItem.AppendChild(xEnergyItem_Type);

			XmlNode xEnergyItem_Quantity = xDoc.CreateElement("Quantity");
			xEnergyItem_Quantity.InnerText = lngQuantity.ToString();
			xEnergyItem.AppendChild(xEnergyItem_Quantity);

			return xEnergyItem;
		}

		public static classEnergy fromXmlNode(ref XmlNode xNode)
		{
			XmlNode xEnergy_quantity = xNode.FirstChild;
			XmlAttribute xAtt_Type = xNode.Attributes[0];
			classEnergy cRetVal = new classEnergy();
			
			cRetVal.eEnergy = getEnergyTypeFromString(xAtt_Type.Value);
			cRetVal.lngQuantity = Convert.ToInt64(xEnergy_quantity.InnerText);

			return cRetVal;
		}

		public static enuEnergy getEnergyTypeFromString(string strEnergyType)
		{
			for (enuEnergy eEnergyCounter = (enuEnergy)0; eEnergyCounter < enuEnergy._numEnergy; eEnergyCounter++)
			{
				if (strEnergyType == eEnergyCounter.ToString()) return eEnergyCounter;
			}
			return enuEnergy._numEnergy;
		}
	}

	public class classEnergyInventory
	{
		bool bolWarnEnergyShortage = false;
		public bool WarnEnergyShortage
		{
			get { return bolWarnEnergyShortage; }
			set { bolWarnEnergyShortage = value; }
		}

		public groupbox_EnergyResources grbEnergyResources = null;
		public List<classEnergy> lcEnergy = new List<classEnergy>();
		List<long> llngCapacty = new List<long>();
		public classEnergyInventory()
		{
			for (enuEnergy eEnergyCounter = (enuEnergy)0; eEnergyCounter < enuEnergy._numEnergy; eEnergyCounter++)
			{
				lcEnergy.Add(new classEnergy(eEnergyCounter, 0));
			}
			defaultCapacity();
		}

		void defaultCapacity() { for (enuEnergy eEnergyCounter = (enuEnergy)0; eEnergyCounter < enuEnergy._numEnergy; eEnergyCounter++) llngCapacty.Add(1000); }

		public void setCapacity(enuEnergy eEnergy, long MaxCapacity) { llngCapacty[(int)eEnergy] = MaxCapacity; }
		public long getCapacity(enuEnergy eEnergy) { return llngCapacty[(int)eEnergy]; }

		public long Available(enuEnergy eEnergy) { return lcEnergy[(int)eEnergy].lngQuantity; }

		long lngPaucityLevel = 5000;
		public classEnergy subEnergy(string strEnergy, int intQuantity) { return subEnergy(classEnergy.getEnergyTypeFromString(strEnergy), intQuantity); }
		public classEnergy subEnergy(enuEnergy eEnergy, int intQuantity)
		{
			classEnergy cEnergyInStock = lcEnergy[(int)eEnergy];
			
			bool bolAbovePaucityLevel = cEnergyInStock.lngQuantity > lngPaucityLevel;
			if (cEnergyInStock.lngQuantity > intQuantity)
			{
				cEnergyInStock.lngQuantity -= intQuantity;
				if (bolAbovePaucityLevel && cEnergyInStock.lngQuantity < lngPaucityLevel)
					WarnEnergyShortage = WarnEnergyShortage || true;
				classEnergy cRetVal = new classEnergy(eEnergy, intQuantity);
				setGroupbox();
				return cRetVal;
			}
			else
				return null;
		}

		public void setEnergy(string strEnergy, long lngQuantity) { setEnergy(classEnergy.getEnergyTypeFromString(strEnergy), lngQuantity); }
		public void setEnergy(enuEnergy eEnergy, long lngQuantity)
		{
			classEnergy cEnergyInStock = lcEnergy[(int)eEnergy];
			cEnergyInStock.lngQuantity = lngQuantity;
			setGroupbox();
		}

		public void addEnergy(string strEnergy, long lngQuantity) { addEnergy(classEnergy.getEnergyTypeFromString(strEnergy), lngQuantity); }
		public void addEnergy(ref classEnergy cEnergy) { addEnergy(cEnergy.eEnergy, cEnergy.lngQuantity); }
		public void addEnergy(enuEnergy eEnergy, long lngQuantity)
		{
			if (eEnergy < enuEnergy._numEnergy)
			{
				classEnergy cEnergyInStock = lcEnergy[(int)eEnergy];
				cEnergyInStock.lngQuantity += lngQuantity;
				if (cEnergyInStock.lngQuantity > llngCapacty[(int)cEnergyInStock.eEnergy])
					cEnergyInStock.lngQuantity = llngCapacty[(int)cEnergyInStock.eEnergy];
				setGroupbox();
			}
		}

		public XmlNode getSaveGameXmlNode(ref XmlDocument xDoc)
		{
			XmlNode xRetVal = xDoc.CreateElement("EnergyInventory");

			XmlNode xInventory = xDoc.CreateElement("Inventory");
			foreach (classEnergy cEnergyItem in lcEnergy)
			{
				XmlNode xEnergyItem = xDoc.CreateElement("EnergyItem");

				xEnergyItem.InnerText = cEnergyItem.lngQuantity.ToString();
				XmlAttribute xAtt_Type = xDoc.CreateAttribute("type");
				xAtt_Type.Value = cEnergyItem.eEnergy.ToString();
				xEnergyItem.Attributes.Append(xAtt_Type);

				xInventory.AppendChild(xEnergyItem);
			}
			xRetVal.AppendChild(xInventory);

			XmlNode xCapacities = xDoc.CreateElement("Capacities");
			for (enuEnergy eEnergyCounter = (enuEnergy)0; eEnergyCounter < enuEnergy._numEnergy; eEnergyCounter++)
			{
				XmlNode xEnergyCapacity = xDoc.CreateElement("Capacity");
				xEnergyCapacity.InnerText = llngCapacty[(int)eEnergyCounter].ToString();
				XmlAttribute xAtt_Energy = xDoc.CreateAttribute("Energy");
				xAtt_Energy.Value = eEnergyCounter.ToString();
				xEnergyCapacity.Attributes.Append(xAtt_Energy);

				xCapacities.AppendChild(xEnergyCapacity);
			}
			xRetVal.AppendChild(xCapacities);

			return xRetVal;
		}

		public static classEnergyInventory fromXmlNode(ref XmlNode xNode)
		{
			classEnergyInventory cRetVal = new classEnergyInventory();

			XmlNode xInventory = xNode.FirstChild;
			XmlNode xCapacities = xInventory.NextSibling;

			for (enuEnergy eEnergyCounter = (enuEnergy)0; eEnergyCounter < enuEnergy._numEnergy; eEnergyCounter++)
			{
				XmlNode xCapacity = xCapacities.ChildNodes[(int)eEnergyCounter];
				cRetVal.llngCapacty[(int)eEnergyCounter] = Convert.ToInt64(xCapacity.InnerText);
			}
			for (int intEnergyItemCounter = 0; intEnergyItemCounter < xInventory.ChildNodes.Count; intEnergyItemCounter++)
			{
				XmlNode xEnergyItem = xInventory.ChildNodes[intEnergyItemCounter];
				classEnergy cEnergyItem = classEnergy.fromXmlNode(ref xEnergyItem);
				cRetVal.addEnergy(ref cEnergyItem);
			}

			return cRetVal;
		}

		public classEnergyInventory Copy()
		{
			classEnergyInventory cRetVal = new classEnergyInventory();

			foreach (classEnergy cEnergyItem in lcEnergy)
				cRetVal.addEnergy(cEnergyItem.eEnergy, cEnergyItem.lngQuantity);

			for (enuEnergy eEnergyCounter = (enuEnergy)0; eEnergyCounter < enuEnergy._numEnergy; eEnergyCounter++)
				cRetVal.llngCapacty[(int)eEnergyCounter] = llngCapacty[(int)eEnergyCounter];

			return cRetVal;
		}

		void setGroupbox()
		{
			if (grbEnergyResources != null)
			{
				grbEnergyResources.ShowData();
			}
		}
	}
	
	public class formSelectBiPropellantForCalbration : Form
	{
		public classPropellant cPropellant = null;
		List <classPropellantDisplayInfo> lcPropellantDisplayInfo = new List<classPropellantDisplayInfo>();
		CK_Controls.pictureboxVSB picVSB = new CK_Controls.pictureboxVSB();
		classShip cShip = null;

		int intHeightPerPropellant = 22;

		public formSelectBiPropellantForCalbration(ref classShip Ship)
		{
			cShip = Ship;
			ShowInTaskbar = false;

			Controls.Add(picVSB);
			picVSB.Dock = DockStyle.Fill;
			picVSB.MouseClick+=new EventHandler<MouseEventArgs>(picVSB_MouseClick);
			Text = "Select Bi-Propellant Fuel";

			XmlDocument xDocPropellants = new XmlDocument();
			string strFilename = formMarsMission.strWorkingDirectory + "bipropellants.xml";
			if (System.IO.File.Exists(strFilename))
			{
				xDocPropellants.Load(strFilename);
				XmlNode xRoot = xDocPropellants.DocumentElement;
				XmlNodeList xlstPropellants = xRoot.ChildNodes;
				Panel pnlTitle = new Panel();
				classPropellant cPropellant_Title = new classPropellant();
				cPropellant_Title.bolFlag = true;
				classPropellantDisplayInfo cProDisInfo = new classPropellantDisplayInfo(ref cPropellant_Title, ref Ship.cSI.cChemicalInventory);
				lcPropellantDisplayInfo.Add(cProDisInfo);
				
				for (int intPropellantCounter = 0; intPropellantCounter < xlstPropellants.Count; intPropellantCounter++)
				{
					XmlNode xThisPropellant = xlstPropellants[intPropellantCounter];
					classPropellant cThisPropellant = classPropellant.fromXmlNode(ref xThisPropellant);
					lcPropellantDisplayInfo.Add(new classPropellantDisplayInfo(ref cThisPropellant, ref Ship.cSI.cChemicalInventory));
				}
			}
			SizeChanged += new EventHandler(formSelectBiPropellantForCalbration_SizeChanged);
			VisibleChanged += new EventHandler(formSelectBiPropellantForCalbration_VisibleChanged);
		}

		void picVSB_MouseClick(object sender, MouseEventArgs e)
		{
			int intIndexPropellant = (int)Math.Floor((double)e.Y / (double)intHeightPerPropellant);
			classPropellant cNewPropellant = lcPropellantDisplayInfo[intIndexPropellant].cPropellant;
			if (cNewPropellant != null)
			{
				long lngOxidizer_Available = cShip.cSI.cChemicalInventory.Available(cNewPropellant.Oxidizer.Formula);
				long lngFuel_Available = cShip.cSI.cChemicalInventory.Available(cNewPropellant.Fuel.Formula);
				System.Windows.Forms.DialogResult dr = System.Windows.Forms.DialogResult.No;
				if (lngFuel_Available == 0 || lngOxidizer_Available == 0)
					dr = MessageBox.Show("Calibrate engines to burn "
												+ cNewPropellant.Oxidizer.Name
												+ "("
												+ cNewPropellant.Oxidizer.Formula
												+ ") and "
												+ cNewPropellant.Fuel.Name
												+ "("
												+ cNewPropellant.Fuel.Formula
												+ ") ?",
										"this fuel not in stock",
										MessageBoxButtons.YesNo,
										MessageBoxIcon.Error);
				else
					dr = MessageBox.Show("Calibrate engines to burn "
												+ cNewPropellant.Oxidizer.Name
												+ "("
												+ cNewPropellant.Oxidizer.Formula
												+ ") and "
												+ cNewPropellant.Fuel.Name
												+ "("
												+ cNewPropellant.Fuel.Formula
												+ ") ?",
										"calibrate new fuel",
										MessageBoxButtons.YesNo,
										MessageBoxIcon.Question);
				if (dr == System.Windows.Forms.DialogResult.Yes)
				{
					cPropellant = cNewPropellant;
					Dispose();
				}
			}
		}

		void formSelectBiPropellantForCalbration_VisibleChanged(object sender, EventArgs e)
		{
			if (Visible)
			{
				bolIgnoreSizeChange = true;
				Left= (int)(Screen.PrimaryScreen.WorkingArea.Width * .15);
				Top = (int)(Screen.PrimaryScreen.WorkingArea.Height * .15);
				Height = (int)(Screen.PrimaryScreen.WorkingArea.Height - 2 * Top);
				bolIgnoreSizeChange = false;

				Width = (int)(Screen.PrimaryScreen.WorkingArea.Width - 2 * Left);
			}
		}
		bool bolIgnoreSizeChange = false;
		void formSelectBiPropellantForCalbration_SizeChanged(object sender, EventArgs e)
		{
			if (bolIgnoreSizeChange) return;
			// draw image
			Bitmap bmpOutput = new Bitmap(picVSB.Width, lcPropellantDisplayInfo.Count * intHeightPerPropellant);
			using (Graphics g = Graphics.FromImage(bmpOutput))
			{
				for (int intPropellantCounter = 0; intPropellantCounter < lcPropellantDisplayInfo.Count; intPropellantCounter++)
				{
					Color clrBackground = Color.Yellow;
					classPropellantDisplayInfo cThisPropellantInfo = lcPropellantDisplayInfo[intPropellantCounter];
					long lngFuelAvailable = 0, lngOxidizerAvailable = 0;
					if (cThisPropellantInfo.cPropellant != null)
					{
						if (cThisPropellantInfo.cOxidizerAvailable == null)
							cThisPropellantInfo.cOxidizerAvailable = cThisPropellantInfo.cChemicalInventory.GetChemicalItem(cThisPropellantInfo.cPropellant.Oxidizer.Formula);
						if (cThisPropellantInfo.cOxidizerAvailable != null)
							lngOxidizerAvailable = cThisPropellantInfo.cChemicalInventory.Available(cThisPropellantInfo.cPropellant.Oxidizer.Formula);
						if (lngOxidizerAvailable > 0)
							cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text = lngOxidizerAvailable.ToString();
						else
							cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text = "---";
						cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].bmp = GraphicText.classGraphicText.getWordImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text, cgrFont);

						if (cThisPropellantInfo.cFuelAvailable == null)
							cThisPropellantInfo.cFuelAvailable = cThisPropellantInfo.cChemicalInventory.GetChemicalItem(cThisPropellantInfo.cPropellant.Fuel.Formula);

						if (cThisPropellantInfo.cFuelAvailable != null)
							lngFuelAvailable = cThisPropellantInfo.cChemicalInventory.Available(cThisPropellantInfo.cPropellant.Fuel.Formula);
						if (lngFuelAvailable > 0)
							cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text = lngFuelAvailable.ToString();
						else
							cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text = "---";
						cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].bmp = GraphicText.classGraphicText.getWordImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text, cgrFont);
						if (lngFuelAvailable == 0 || lngOxidizerAvailable == 0) clrBackground = (intPropellantCounter % 2 == 0 ? Color.Pink : Color.Plum);
						else clrBackground = (intPropellantCounter % 2 == 0 ? Color.White : Color.Beige);
					}

					int intOxidizer = (int)((double)picVSB.Width * .42);
					int intOxidizerQuantity_Left = intOxidizer / 2;
					int intFuelQuantity_Left = intOxidizer + intOxidizerQuantity_Left;
					int intFormulaTop = 1;
					double dblShiftFormulaFromCenter = 0.03;
					int intBaseY = intPropellantCounter * intHeightPerPropellant;
					using (SolidBrush br = new SolidBrush(clrBackground))
						g.FillRectangle(br, new Rectangle(0, intBaseY, bmpOutput.Width, intHeightPerPropellant));
					g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].bmp, new Point(0, intBaseY));
					if (cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].bmp != null)
						g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].bmp, new Point(intOxidizerQuantity_Left, intBaseY));

					g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].bmp, new Point(intOxidizer - cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].bmp.Width - (int)(dblShiftFormulaFromCenter * (double)picVSB.Width), intBaseY - (cThisPropellantInfo.cPropellant != null ? intFormulaTop : 0)));

					g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].bmp, new Point(intOxidizer + (int)(dblShiftFormulaFromCenter * (double)picVSB.Width), intBaseY - (cThisPropellantInfo.cPropellant != null ? intFormulaTop : 0)));
					if (cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].bmp != null)
						g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].bmp, new Point(intFuelQuantity_Left - cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].bmp.Width, intBaseY));
					g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].bmp, new Point(intOxidizer * 2 - cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].bmp.Width, intBaseY));

					g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Ratio].bmp, new Point((int)((double)picVSB.Width * .87), intBaseY));
					g.DrawImage(cThisPropellantInfo.Field[(int)enuPropellantSelectionPanelStrings.Force].bmp, new Point((int)((double)picVSB.Width * .92), intBaseY));
				}
			}
			picVSB.setImage(ref bmpOutput);
		}

		public class classPropellantFieldDisplayInfo
		{
			public string Text;
			public Bitmap bmp;
		}

		public class classPropellantDisplayInfo
		{
			public classPropellantFieldDisplayInfo[] Field;
			public classPropellant cPropellant;
			public classChemicalInventory cChemicalInventory;
			public classChemicalInventoryItem cOxidizerAvailable;
			public classChemicalInventoryItem cFuelAvailable ;
			public classPropellantDisplayInfo(ref classPropellant Propellant,
											  ref classChemicalInventory ChemicalInventory)
			{
				cPropellant = Propellant;
				cChemicalInventory = ChemicalInventory;
				Field = new classPropellantFieldDisplayInfo[(int)enuPropellantSelectionPanelStrings._numPropellantSelectionPanelStrings];
				for (int intFieldCounter = 0; intFieldCounter < Field.Length; intFieldCounter++)
					Field[intFieldCounter] = new classPropellantFieldDisplayInfo();
				if (cPropellant.bolFlag)
				{ // title panel
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].Text = "oxidizer name";
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text = "supply";
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].Text = "Oxidizer";
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].Text = "Fuel";
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text = "supply";
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].Text = "fuel name";
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Ratio].Text = "ratio";
					Field[(int)enuPropellantSelectionPanelStrings.Ratio].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Ratio].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Force].Text = "force";
					Field[(int)enuPropellantSelectionPanelStrings.Force].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Force].Text, cgrFont);

					cPropellant = null;
				}
				else
				{
					cOxidizerAvailable = cChemicalInventory.GetChemicalItem(cPropellant.Oxidizer.Formula);
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].Text = cPropellant.Oxidizer.Name;
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Name].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].Text = cPropellant.Oxidizer.Formula;
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].bmp = GraphicText.classGraphicText.getImageChemicalFormula(Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Formula].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text = cChemicalInventory.Available(cPropellant.Oxidizer.Formula).ToString();
					Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Oxidizer_Quantity].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].Text = cPropellant.Fuel.Name;
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Fuel_Name].Text, cgrFont);

					cFuelAvailable = cChemicalInventory.GetChemicalItem(cPropellant.Fuel.Formula);
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].Text = cPropellant.Fuel.Formula;
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].bmp = GraphicText.classGraphicText.getImageChemicalFormula(Field[(int)enuPropellantSelectionPanelStrings.Fuel_Formula].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text = cChemicalInventory.Available(cPropellant.Fuel.Formula).ToString();
					Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Fuel_Quantity].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Ratio].Text = cPropellant.Ratio.ToString();
					Field[(int)enuPropellantSelectionPanelStrings.Ratio].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Ratio].Text, cgrFont);

					Field[(int)enuPropellantSelectionPanelStrings.Force].Text = cPropellant.Force.ToString();
					Field[(int)enuPropellantSelectionPanelStrings.Force].bmp = GraphicText.classGraphicText.getWordImage(Field[(int)enuPropellantSelectionPanelStrings.Force].Text, cgrFont);
				}
			}
		}
	
		public enum enuPropellantSelectionPanelStrings { Oxidizer_Name, Oxidizer_Quantity, Oxidizer_Formula, Fuel_Formula, Fuel_Quantity, Fuel_Name, Ratio, Force, _numPropellantSelectionPanelStrings };
		static GraphicText.classMyFont cgrFont = new GraphicText.classMyFont(new Font("courier", 12), Color.Black);
	
	}

	
	public class classChemicalReaction
	{
		public classChemicalHalfEquation input = new classChemicalHalfEquation();
		public classChemicalHalfEquation output = new classChemicalHalfEquation();
		public bool Valid { get { return input.Compare(ref output); } }
		public List<classElementInventory_Item> imbalance
		{
			get
			{
				List<classElementInventory_Item> lInputElements = input.lElementInventory != null
																		? input.lElementInventory.ToList()
																		: classElementInventory_Item.getEmptyInventory();
				List<classElementInventory_Item> lOutputElements = output.lElementInventory != null
																		? output.lElementInventory.ToList()
																		: classElementInventory_Item.getEmptyInventory();

				List<classElementInventory_Item> lImbalanceElements = new List<classElementInventory_Item>();

				for (int intCounter = 0; intCounter < lInputElements.Count; intCounter ++)
				{

					if (lInputElements[intCounter].intQuantity != lOutputElements[intCounter].intQuantity)
					{
						classElementInventory_Item cImbalancedReactant = new classElementInventory_Item();
						cImbalancedReactant.Element = lInputElements[intCounter].Element;
						cImbalancedReactant.intQuantity = lOutputElements[intCounter].intQuantity - lInputElements[intCounter].intQuantity;
						lImbalanceElements.Add(cImbalancedReactant);
					}
				}

				//for (int intCounter = 0; intCounter < lInputElements.Count; intCounter++)
				//    lImbalanceElements.Add(lInputElements[intCounter]);
				//for (int intOutputCounter = 0; intOutputCounter < lOutputElements.Count; intOutputCounter++)
				//    lImbalanceElements.Add(lOutputElements[intOutputCounter]);

				return lImbalanceElements;
			}
		}

	}

	public class classChemicalReactant
	{
		public classChemical Chemical;
		public int Quantity;
		public classChemicalReactant(ref classChemical cChemical, int intQuantity)
		{
			Chemical = cChemical;
			Quantity = intQuantity;
		}
	}

	public class classChemicalHalfEquation
	{
		public List<classChemicalReactant> reactants = new List<classChemicalReactant>();
		bool bolInventoryValid = true;

		public void add(ref classChemicalReactant cNewReactant)
		{
			foreach(classChemicalReactant cReactant in reactants)
				if (cNewReactant.Chemical == cReactant.Chemical)
				{
					cReactant.Quantity += cNewReactant.Quantity;
					return;
				}
			reactants.Add(cNewReactant);
			bolInventoryValid = false;
		}

		public bool Compare(ref classChemicalHalfEquation cHalfEquation)
		{
			if (cHalfEquation.ElementList.Count != ElementList.Count) return false;
			for (int intElementCounter = 0; intElementCounter < ElementList.Count; intElementCounter++)
			{
				if (cHalfEquation.ElementList[intElementCounter].Element != ElementList[intElementCounter].Element) return false;
				if (cHalfEquation.ElementList[intElementCounter].intQuantity != ElementList[intElementCounter].intQuantity) return false;
			}
			return true;
		}

		public List<classElementInventory_Item> lElementInventory = classElementInventory_Item.getEmptyInventory();

		List<classElementInventory_Item> lElementList = classElementInventory_Item.getEmptyInventory();
		public List<classElementInventory_Item> ElementList
		{
			get
			{
				if (!bolInventoryValid)
				{
					foreach (classChemicalReactant cReactant in reactants)
					{
						foreach (classMoleculeComponent cMoleculecomponent in cReactant.Chemical.lComponents)
						{
							lElementInventory[cMoleculecomponent.cElement.intIndex].intQuantity += cMoleculecomponent.intNumber * cReactant.Quantity;
						}
					}

					lElementList = new List<classElementInventory_Item>();
					foreach (classElementInventory_Item cElement in lElementInventory)
						if (cElement.intQuantity >0)
							lElementList.Add(cElement);
					bolInventoryValid = true;
				}
				return lElementList;
			}
		}
	}

	public class classElementInventory_Item
	{
		public classChemical_Element Element;
		public int intQuantity;

		public static List<classElementInventory_Item> getEmptyInventory()
		{
			List < classElementInventory_Item > lRetVal = new classElementInventory_Item[classChemical_Element.lElements.Count].ToList();
			for (int intItemCounter = 0; intItemCounter < lRetVal.Count; intItemCounter++)
			{
				lRetVal[intItemCounter] = new classElementInventory_Item();
				lRetVal[intItemCounter].Element = classChemical_Element.lElements[intItemCounter];
				lRetVal[intItemCounter].intQuantity = 0;
			}
			return lRetVal;
		}
	}

	

	public class formChemicalReactionInterface : Form
	{
		public classChemicalReaction cChemicalReaction = null;
		List<classElementInventory_Item> lImbalance = new List<classElementInventory_Item>();
		public CK_Controls.LabelButton lbtnChemicalInventory = new CK_Controls.LabelButton();
		classActionSchedule_event cActionEvent = null;
		
		double ChemistRating
		{
			get
			{
				if (cActionEvent != null)
					if (cActionEvent.cAstronauts.Length > 0)
						return cActionEvent.cAstronauts[0].cProficiencies[(int)enuAstronautProficiencies.chemist].Rating;
				return 0;
			}
		}

		public classActionSchedule_event ActionEvent
		{
			get { return cActionEvent; }
			set
			{
				cActionEvent = value;
				cleanTable();
				if (cActionEvent.cAstronauts != null
					&& cActionEvent.cAstronauts.Length > 0)
				{
					btnimgChemist.Text = cActionEvent.cAstronauts[0].Name;
					btnimgChemist.Visible = true;
				}
				else
				{ // this should not happen
					btnimgChemist.Text = "";
					btnimgChemist.Visible = false;
				}
				Show();
				display();
			}
		}

		List<textbox_Chemical> ltxtchem_Input = new List<textbox_Chemical>();
		List<textbox_Chemical> ltxtchem_Output  = new List<textbox_Chemical>();

		Graphics gBackground = null;
		buttonImage btnimgOk = new buttonImage();
		buttonImage btnimgCancel = new buttonImage();
		buttonImage btnimgChemist = new buttonImage();

		public class buttonImage 
		{
			public static PictureBox picBackground = null;
			public static Bitmap bmpBackground = null;
			
			bool bolVisible = true;
			public bool Visible
			{
				get { return bolVisible; }
				set { bolVisible = value; }
			}

			bool bolAutosize = false;
			public bool Autosize
			{
				get { return bolAutosize; }
				set
				{
					if (bolAutosize != value)
					{
						bolAutosize = value;
						if (bolAutosize) resize();
					}
				}
			}

			string strText = "button";
			public string Text
			{
				get { return strText; }
				set
				{
					strText = value;
					if (bolAutosize) resize();
				}
			}

			void resize()
			{
				if (g != null)
				{
					SizeF szf = g.MeasureString(strText, fnt);
					size = new Size((int)szf.Width + 8, (int)szf.Height + 4);
				}
			}

			Rectangle _rec = new Rectangle();
			public Rectangle rectangle
			{
				get { return _rec; }
				set { _rec = value; }
			}

			public Point location
			{
				get { return _rec.Location; }
				set { _rec.Location = value;}
			}

			public Size size
			{
				get { return _rec.Size; }
				set 
				{
					if (_rec.Size != value)
					{
						_rec.Size = value;
						if (bolAutosize) resize();
					}
				}
			}

			public int width
			{
				get { return _rec.Width; }
				set 
				{
					_rec.Width = value;
					if (bolAutosize) resize();
				}
			}

			public int height
			{
				get { return _rec.Height; }
				set
				{
					_rec.Height = value;
					if (bolAutosize) resize();
				}
			}

			public int Left
			{
				get { return _rec.Left; }
				set { _rec.X = value; }
			}

			public int Top
			{
				get { return _rec.Top; }
				set { _rec.Y = value; }
			}

			public int Right { get { return _rec.Right; } }
			public int Bottom { get { return _rec.Bottom; } }

			static Graphics _g;
			public static Graphics g
			{
				get { return _g; }
				set
				{
					_g = value;
					foreach (buttonImage btnimg in lbtnImages)
					{
						if (btnimg.Autosize)
							btnimg.resize();
					}
				}
			}
			public Font fnt = formMarsMission.fntMSSansSerif8;
			const int conDefaultOutlineWidth = 3;
			int intOutlineWidth = conDefaultOutlineWidth;
			public int OutLineWidth
			{
				get { return intOutlineWidth; }
				set
				{
					if (intOutlineWidth != value)
					{
						intOutlineWidth = value;
						setPenOutlineDull();
						setPenOutlineHighlight();
					}
				}
			}

			Color _clrOutlineHighlight = Color.Black;
			Pen pOutlineHighlight = new Pen(Color.Black, conDefaultOutlineWidth);
			public Color clrOutlineHighlight
			{
				get { return pOutlineHighlight.Color; }
				set
				{
					_clrOutlineHighlight = value;
					setPenOutlineHighlight();
				}
			}

			void setPenOutlineHighlight()
			{
				if (pOutlineHighlight != null) pOutlineHighlight.Dispose();
				pOutlineHighlight = new Pen(_clrOutlineHighlight, intOutlineWidth);
			}

			Color _clrOutlineDull = Color.Gray;
			Pen pOutlineDull = new Pen(Color.Gray, conDefaultOutlineWidth);
			public Color clrOutlineDull
			{
				get { return pOutlineDull.Color; }
				set
				{
					_clrOutlineDull = value;
					setPenOutlineDull();
				}
			}
			void setPenOutlineDull()
			{
				if (pOutlineDull != null) pOutlineDull.Dispose();
				pOutlineDull = new Pen(_clrOutlineDull, intOutlineWidth);
			}

			SolidBrush  brBackground = new SolidBrush(Color.Black);
			public Color clrBackground
			{
				get { return brBackground.Color; }
				set
				{
					brBackground.Dispose();
					brBackground = new SolidBrush(value);
				}
			}

			SolidBrush brForeground = new SolidBrush(Color.Black);
			public Color clrForeground
			{
				get { return brForeground.Color; }
				set
				{
					brForeground.Dispose();
					brForeground = new SolidBrush(value);
				}
			}
			
			public event EventHandler<EventArgs> Clicked;
			protected virtual void OnClicked(EventArgs e) { if (Clicked != null) { Clicked(this, e); } }

			static public event EventHandler<EventArgs> MouseOverChanged;
			static protected void OnMouseOverChanged(EventArgs e) { if (MouseOverChanged != null) { MouseOverChanged(null, e); } }

			static List<buttonImage> lbtnImages = new List<buttonImage>();
			public buttonImage()
			{
				lbtnImages.Add(this);
				size = new Size(50, 15);
			}

			public void dispose() { lbtnImages.Remove(this); }

			public static void DrawButtons()
			{
				foreach (buttonImage btnimg in lbtnImages)
					if (btnimg.Visible) btnimg.Draw();
			}

			public void Draw()
			{
				Pen p = _btnimgMouseOver == this
										? pOutlineHighlight
										: pOutlineDull;
				g.DrawRectangle(p, rectangle);
				g.DrawString(Text, fnt, brForeground, new PointF(Left + 2, Top + 2));
				
			}
			
			static buttonImage _btnimgMouseOver = null;
			public static buttonImage btnimgMouseOver
			{
				get { return _btnimgMouseOver; }
				set
				{
					if (_btnimgMouseOver != value)
					{
						buttonImage btnimgOld = _btnimgMouseOver;
						_btnimgMouseOver = value;
						if (_btnimgMouseOver != null) _btnimgMouseOver.Draw();
						if (btnimgOld != null) btnimgOld.Draw();
						picBackground.Image = bmpBackground;
						OnMouseOverChanged(new EventArgs());
					}
				}
			}
			public static void MouseMove(Point ptMousePosition)
			{
				foreach (buttonImage btnimg in lbtnImages)
				{
					if (btnimg.Visible)
					{
						if (ptMousePosition.X >= btnimg.Left
							&& ptMousePosition.X <= btnimg.Right
							&& ptMousePosition.Y >= btnimg.Top
							&& ptMousePosition.Y <= btnimg.Bottom)
						{
							btnimgMouseOver = btnimg;
							return;
						}
					}
				}
				btnimgMouseOver = null;
			}

			public static bool MouseClick(Point ptMousePosition)
			{
				if (btnimgMouseOver != null)
				{
					btnimgMouseOver.OnClicked(new EventArgs());
					return true;
				}
				return false;
			}
		}

		void lbtnChemicalInventory_AfterClick(object sender, EventArgs e)
		{
			if (lbtnChemicalInventory.Flag)
			{
				if (formChemicalInventory.frmChemicalInventory == null)
				{
					formChemicalInventory.buildFormChemicalInventory(ref  cActionEvent.cAstronauts[0].cCDO.cSIRoom.cSI.cChemicalInventory);
				}
				formChemicalInventory.frmChemicalInventory.FormBorderStyle = FormBorderStyle.Sizable;
				formMarsMission.frmMarsMission.grbBuildingData.grbResources.lbtnChemicalInventory.set();
				formMarsMission.frmMarsMission.grbShipData.grbResources.lbtnChemicalInventory.set();
			}
			else
			{
				formMarsMission.frmMarsMission.grbBuildingData.grbResources.lbtnChemicalInventory.reset();
				formMarsMission.frmMarsMission.grbShipData.grbResources.lbtnChemicalInventory.reset();
				formChemicalInventory.frmChemicalInventory.FormBorderStyle = FormBorderStyle.None;
				formChemicalInventory.placeForm();
			}
		}

		void lbtnChemicalInventory_MouseLeave(object sender, EventArgs e)
		{
			if (formChemicalInventory.frmChemicalInventory != null && !lbtnChemicalInventory.Flag)
				formChemicalInventory.frmChemicalInventory.Hide();
		}

		void lbtnChemicalInventory_MouseEnter(object sender, EventArgs e)
		{
			if (formChemicalInventory.frmChemicalInventory == null || formChemicalInventory.frmChemicalInventory.IsDisposed)
			{
				formChemicalInventory.buildFormChemicalInventory(ref cActionEvent.cAstronauts[0].cCDO.cSIRoom.cSI.cChemicalInventory);
				formChemicalInventory.frmChemicalInventory.ShowData();
			}
			else
			{
				formChemicalInventory.frmChemicalInventory.Inventory = cActionEvent.cAstronauts[0].cCDO.cSIRoom.cSI.cChemicalInventory;
				if (!formChemicalInventory.frmChemicalInventory.Visible && !lbtnChemicalInventory.Flag)
				{
					formChemicalInventory.placeForm();
				}
			}
			//formMarsMission.hsbMouseWheel.Focus();
		}

		public static formChemicalReactionInterface frmChemicalReactionInterface;
		static bool bolInitImages = false;
		public formChemicalReactionInterface()
		{
			if (buttonImage.picBackground != null) buttonImage.picBackground.Dispose();

			buttonImage.picBackground = new PictureBox();
			if (!bolInitImages)
			{
				bmpArrow = classGraphicText.getWordImage("→", cgrfntChemicalReactionFont_Black);
				bmpPlus = classGraphicText.getWordImage("+", cgrfntChemicalReactionFont_Black);
				bmpInvalidReaction = classGraphicText.getWordImage("Invalid Reaction", cgrfntChemicalReactionFont_Red);
				bolInitImages = true;

				ToolTip ttChemicalReactionInterface = new ToolTip();
				ttChemicalReactionInterface.IsBalloon = true;
				ttChemicalReactionInterface.SetToolTip(buttonImage.picBackground, "balance your input reactants with your output reactants");
			}

			Controls.Add(buttonImage.picBackground); buttonImage.picBackground.Dock = DockStyle.Fill;
			btnimgOk.Text = "Ok"; btnimgOk.Visible = false;
			btnimgCancel.Text = "Cancel";
			btnimgChemist.Text = "";
			btnimgCancel.Autosize
				= btnimgOk.Autosize
				= btnimgChemist.Autosize 
				= true;

			//ShowInTaskbar = false;
			Text = "Chemistry Laboratory";
			Controls.Add(lbtnChemicalInventory); lbtnChemicalInventory.Text = "Chemical Inventory";
			lbtnChemicalInventory.MouseEnter += new EventHandler(lbtnChemicalInventory_MouseEnter);
			lbtnChemicalInventory.MouseLeave += new EventHandler(lbtnChemicalInventory_MouseLeave);
			lbtnChemicalInventory.AfterClick += new EventHandler<EventArgs>(lbtnChemicalInventory_AfterClick);
			
			intItemHeight = 55;
			btnimgCancel.Clicked += new EventHandler<EventArgs>(btnimgCancel_Clicked);
			btnimgOk.Clicked += new EventHandler<EventArgs>(btnimgOk_Clicked);

			SizeChanged += new EventHandler(formChemicalReactionInterface_SizeChanged);
			VisibleChanged += new EventHandler(formChemicalReactionInterface_VisibleChanged);
			buttonImage.picBackground.MouseClick += new MouseEventHandler(picBackground_MouseClick);
			buttonImage.picBackground.MouseMove += new MouseEventHandler(picBackground_MouseMove);
			buttonImage.MouseOverChanged += new EventHandler<EventArgs>(buttonImage_MouseOverChanged);
		}

		void buttonImage_MouseOverChanged(object sender, EventArgs e)
		{
			if (buttonImage.btnimgMouseOver == btnimgChemist)
			{ // display astronaut information 
				classAstronaut cChemist = cActionEvent.cAstronauts[0];
				Size szRecChemst = new Size(250, 160);
				Rectangle recChemist = new Rectangle(btnimgChemist.Right, btnimgChemist.Top - szRecChemst.Height, szRecChemst.Width, szRecChemst.Height);
				gBackground.FillRectangle(classPensAndBrushes.brBeige, recChemist);
				using (Font fntChemistName = new System.Drawing.Font("Ms Sans-serif", 12, FontStyle.Underline))
					gBackground.DrawString("Chemist " + cChemist.Name, 
										   fntChemistName, 
										   classPensAndBrushes.brBlack, 
										   new PointF(recChemist.Left + 2, recChemist.Top + 2));

				int intTopProficiencies =recChemist.Top + 34;
				int intLeftProficiencies = recChemist.Right - 40, intHeightProficiencies = 15;
				gBackground.DrawString("Proficiencies",
									   formMarsMission.fntMSSansSerif8,
									   classPensAndBrushes.brBlack,
									   new PointF(recChemist.Left + 20, intTopProficiencies));

				for (enuAstronautProficiencies eChemistProficiencies = (enuAstronautProficiencies)0; eChemistProficiencies < enuAstronautProficiencies._numAstronautProficiencies; eChemistProficiencies++)
				{
					SizeF szfProficiency = gBackground.MeasureString(eChemistProficiencies.ToString(), formMarsMission.fntMSSansSerif8);
					int intTopThisProficiency = intTopProficiencies + (int)eChemistProficiencies * intHeightProficiencies;
					gBackground.DrawString(eChemistProficiencies.ToString(), 
										   formMarsMission.fntMSSansSerif8, 
										   eChemistProficiencies == enuAstronautProficiencies.chemist
													? classPensAndBrushes.brBlack
													: classPensAndBrushes.brDarkGray, 
										   new PointF(intLeftProficiencies - szfProficiency.Width - 15, 
													  intTopThisProficiency));
					gBackground.DrawString(cChemist.cProficiencies[(int)eChemistProficiencies].Rating.ToString(),
										   formMarsMission.fntMSSansSerif8,
										   eChemistProficiencies == enuAstronautProficiencies.chemist
													? classPensAndBrushes.brBlack
													: classPensAndBrushes.brDarkGray, 
										   new PointF(intLeftProficiencies, intTopThisProficiency));
				}

				string strLocationDescription = cChemist.Descriptor().Replace("\r\n", "");
				PointF ptfLocDes = new PointF(recChemist.Left + 5, 10+ intTopProficiencies + (int)enuAstronautProficiencies._numAstronautProficiencies * intHeightProficiencies);
				SizeF szfLocDes = new SizeF(recChemist.Right - ptfLocDes.X - 10, recChemist.Bottom - ptfLocDes.Y -10);
				gBackground.DrawString(strLocationDescription, formMarsMission.fntMSSansSerif8, classPensAndBrushes.brDarkBlue, new RectangleF(ptfLocDes, szfLocDes));
				buttonImage.picBackground.Image = buttonImage.bmpBackground;
			}
			else
			{
				display();
			}
		}

		void picBackground_MouseMove(object sender, MouseEventArgs e) { buttonImage.MouseMove(new Point(e.X, e.Y)); }
		void picBackground_MouseClick(object sender, MouseEventArgs e)
		{
			if (!buttonImage.MouseClick(new Point(e.X, e.Y)))
			{
				int intMidway = (int)((double)buttonImage.picBackground.Width / 2);
				if (e.X > intMidway) addTxtOutput();
				else addTxtInput();
			}
		}

		void btnimgOk_Clicked(object sender, EventArgs e)
		{
			testReaction();

			if (cChemicalReaction != null)
			{
				Visible = false;
				classAction cUseChemLabChemAction = new classAction();
				cUseChemLabChemAction.Name = "";
				cUseChemLabChemAction.Title = "Chemist";
				cUseChemLabChemAction.eAction = enuActions.Chemical_Reaction;
				cUseChemLabChemAction.cRequirements = new classActionRequirements();
				cUseChemLabChemAction.cRequirements.cLoopAction = cUseChemLabChemAction;
				cUseChemLabChemAction.cRequirements.cProficiency = cActionEvent.cAction.cRequirements.cProficiency;
				cUseChemLabChemAction.cRequirements.dblHoursOfLabour = 1.15 - ((double)cActionEvent.cAstronauts[0].cProficiencies[(int)enuAstronautProficiencies.chemist].Rating / 100.0);
				cUseChemLabChemAction.cRequirements.eLoc = enuLocation.anywhere;
				cUseChemLabChemAction.cRequirements.ePublishCriteria = enuActionsPublishCriteria.never;

				int intBaseAmount = (int)(500 * cActionEvent.cAstronauts[0].cProficiencies[(int)enuAstronautProficiencies.chemist].Rating / 100);
				cUseChemLabChemAction.cResourceTypes = new classActionResourceType[(int)enuActionResourceType._numActionResourceType];
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input] = new classActionResourceType();

				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources = new classActionResource[frmChemicalReactionInterface.cChemicalReaction.input.reactants.Count + 1];
				// need chemical reaction station
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[0] = new classActionResource();
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[0].strChemical = "";
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[0].eEnergy = enuEnergy._numEnergy;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[0].eResource = enuResources.Chemical_Reaction_Station;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[0].intQuantity = 1;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[0].eLocation = enuActionResourceLocation.nearby;

				for (int intInputReactantCounter = 1; intInputReactantCounter < cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources.Length; intInputReactantCounter++)
				{
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[intInputReactantCounter] = new classActionResource();
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[intInputReactantCounter].strChemical = frmChemicalReactionInterface.cChemicalReaction.input.reactants[intInputReactantCounter - 1].Chemical.Formula;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[intInputReactantCounter].eEnergy = enuEnergy._numEnergy;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[intInputReactantCounter].eResource = enuResources._numResources;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[intInputReactantCounter].intQuantity = intBaseAmount * frmChemicalReactionInterface.cChemicalReaction.input.reactants[intInputReactantCounter - 1].Quantity;
					cUseChemLabChemAction.Name += (frmChemicalReactionInterface.cChemicalReaction.input.reactants[intInputReactantCounter - 1].Quantity > 1
																																	? frmChemicalReactionInterface.cChemicalReaction.input.reactants[intInputReactantCounter - 1].Quantity.ToString()
																																	: "")
												+ cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources[intInputReactantCounter].strChemical
												+ (intInputReactantCounter < cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Input].cResources.Length - 1
																			? "+"
																			: "");
				}

				// add chemical reaction station
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt] = new classActionResourceType();
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources = new classActionResource[1];
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0] = new classActionResource();
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0].strChemical = "";
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0].eEnergy = enuEnergy._numEnergy;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0].eResource = enuResources.Chemical_Reaction_Station;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0].intQuantity = 1;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0].intIndexInputResourceIReplace = 0;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Interrupt].cResources[0].eLocation = enuActionResourceLocation.nearby;

				cUseChemLabChemAction.Name += " → ";

				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output] = new classActionResourceType();
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources = new classActionResource[frmChemicalReactionInterface.cChemicalReaction.output.reactants.Count + 1];
				// add chemical reaction station
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0] = new classActionResource();
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0].strChemical = "";
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0].eEnergy = enuEnergy._numEnergy;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0].eResource = enuResources.Chemical_Reaction_Station;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0].intQuantity = 1;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0].intIndexInputResourceIReplace = 0;
				cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[0].eLocation = enuActionResourceLocation.nearby;

				for (int intOutputReactantCounter = 1; intOutputReactantCounter < cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources.Length; intOutputReactantCounter++)
				{
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter] = new classActionResource();
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter].strChemical = frmChemicalReactionInterface.cChemicalReaction.output.reactants[intOutputReactantCounter - 1].Chemical.Formula;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter].eEnergy = enuEnergy._numEnergy;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter].eResource = enuResources._numResources;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter].intQuantity = intBaseAmount * frmChemicalReactionInterface.cChemicalReaction.output.reactants[intOutputReactantCounter - 1].Quantity;
					cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter].intIndexInputResourceIReplace = -1;
					cUseChemLabChemAction.Name += (frmChemicalReactionInterface.cChemicalReaction.output.reactants[intOutputReactantCounter - 1].Quantity > 1
																																			? frmChemicalReactionInterface.cChemicalReaction.output.reactants[intOutputReactantCounter - 1].Quantity.ToString()
																																			: "")
												+ cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources[intOutputReactantCounter].strChemical
												+ (intOutputReactantCounter < cUseChemLabChemAction.cResourceTypes[(int)enuActionResourceType.Output].cResources.Length - 1
																			? "+"
																			: "");
				}

				cUseChemLabChemAction.eAction = enuActions.Chemical_Reaction;

				classActionSchedule_event cUseChemLab_Event = new classActionSchedule_event(ref cUseChemLabChemAction, ref classGameState.cControlAstronaut);
				classActionSchedule.enQNewEvent(ref cUseChemLab_Event);

				foreach (classAstronaut cAstronaut in cActionEvent.cAstronauts)
					cAstronaut.cActionEvent = cUseChemLab_Event;
			}
			Hide();
		}

		void btnimgCancel_Clicked(object sender, EventArgs e)
		{
			cChemicalReaction = null;
			foreach (classAstronaut cAstronaut in ActionEvent.cAstronauts)
			{
				cAstronaut.cActionEvent = null;
			}
			Hide();
		}

		void formChemicalReactionInterface_VisibleChanged(object sender, EventArgs e)
		{
			if (Visible)
			{
				Width = (int)((double)Screen.PrimaryScreen.WorkingArea.Width * .9);
				Height = (int)((double)Screen.PrimaryScreen.WorkingArea.Height * .9);
				Top = (Screen.PrimaryScreen.WorkingArea.Height - Height) / 2;
				Left = (Screen.PrimaryScreen.WorkingArea.Width - Width) / 2;
				cChemicalReaction = null;
				addTxtInput();
				addTxtOutput();
			}
		}

		void formChemicalReactionInterface_SizeChanged(object sender, EventArgs e) 
		{
			buttonImage.bmpBackground = new Bitmap(buttonImage.picBackground.Width, buttonImage.picBackground.Height);
			if (gBackground != null) gBackground.Dispose();

			buttonImage.g 
				= gBackground
				= Graphics.FromImage(buttonImage.bmpBackground);			

			display(); 
		}

		GraphicText.classMyFont cgrfntChemicalReactionFont_Black = new classMyFont(formMarsMission.fntMSSansSerif8, Color.Black);
		GraphicText.classMyFont cgrfntChemicalReactionFont_Red = new classMyFont(formMarsMission.fntMSSansSerif8, Color.Red);

		static Bitmap bmpArrow = null, bmpPlus = null, bmpInvalidReaction = null;
		void display()
		{
			gBackground.FillRectangle(classPensAndBrushes.brWhite, new Rectangle(0, 0, buttonImage.bmpBackground.Width, buttonImage.bmpBackground.Height));
			gBackground.DrawRectangle(classPensAndBrushes.pLightGray3 , new Rectangle(0, 0, buttonImage.bmpBackground.Width-3, buttonImage.bmpBackground.Height-3));
			Point ptDrawReactant = new Point(5, 25);
			if (cChemicalReaction != null && cChemicalReaction.Valid)
			{
				// draw input reactants 
				for (int intInputReactantCounter = 0; intInputReactantCounter < cChemicalReaction.input.reactants.Count; intInputReactantCounter++)
				{
					if (cChemicalReaction.input.reactants[intInputReactantCounter].Quantity > 1)
					{
						Bitmap bmpNumeral = classGraphicText.getWordImage(cChemicalReaction.input.reactants[intInputReactantCounter].Quantity.ToString(), cgrfntChemicalReactionFont_Black);
						gBackground.DrawImage(bmpNumeral, new Point(ptDrawReactant.X, ptDrawReactant.Y - 2));
						ptDrawReactant.X += bmpNumeral.Width;
					}
					Bitmap bmpReactant = classGraphicText.getImageChemicalFormula(cChemicalReaction.input.reactants[intInputReactantCounter].Chemical.Formula, cgrfntChemicalReactionFont_Black);
					gBackground.DrawImage(bmpReactant, ptDrawReactant);

					ptDrawReactant.X += bmpReactant.Width + 10;

					if (intInputReactantCounter < cChemicalReaction.input.reactants.Count - 1)
					{
						gBackground.DrawImage(bmpPlus, new Point(ptDrawReactant.X, ptDrawReactant.Y - 2));
						ptDrawReactant.X += bmpPlus.Width + 10;
					}
				}

				// draw arrow
				ptDrawReactant.X += 15;
				gBackground.DrawImage(bmpArrow, new Point(ptDrawReactant.X, ptDrawReactant.Y - 2));
				ptDrawReactant.X += bmpArrow.Width + 25;

				// draw output reactants
				for (int intOutputReactantCounter = 0; intOutputReactantCounter < cChemicalReaction.output.reactants.Count; intOutputReactantCounter++)
				{
					if (cChemicalReaction.output.reactants[intOutputReactantCounter].Quantity > 1)
					{
						Bitmap bmpNumeral = classGraphicText.getWordImage(cChemicalReaction.output.reactants[intOutputReactantCounter].Quantity.ToString(), cgrfntChemicalReactionFont_Black);
						gBackground.DrawImage(bmpNumeral, new Point(ptDrawReactant.X, ptDrawReactant.Y -2));
						ptDrawReactant.X += bmpNumeral.Width;
					}

					Bitmap bmpReactant = classGraphicText.getImageChemicalFormula(cChemicalReaction.output.reactants[intOutputReactantCounter].Chemical.Formula, cgrfntChemicalReactionFont_Black);
					gBackground.DrawImage(bmpReactant, ptDrawReactant);
					ptDrawReactant.X += bmpReactant.Width + 10;

					if (intOutputReactantCounter < cChemicalReaction.output.reactants.Count - 1)
					{
						Bitmap bmpPlus = classGraphicText.getWordImage("+", cgrfntChemicalReactionFont_Black);
						gBackground.DrawImage(bmpPlus, new Point(ptDrawReactant.X, ptDrawReactant.Y - 2));
						ptDrawReactant.X += bmpPlus.Width+25;
					}
				}
			}
			else
			{
				gBackground.DrawImage(bmpInvalidReaction, ptDrawReactant);
			}

			int intTop = 50;
			int intLeft = 10;
			int intWidthChemBoxes = (int)((double)Width - 115) / 2;

			for (int intInputTxtCounter = 0; intInputTxtCounter < ltxtchem_Input.Count; intInputTxtCounter++)
			{
				ltxtchem_Input[intInputTxtCounter].Width = intWidthChemBoxes;
				ltxtchem_Input[intInputTxtCounter].Location = new Point(intLeft, intTop + (intItemHeight + 1) * intInputTxtCounter);
			}

			intLeft = buttonImage.picBackground.Width - intWidthChemBoxes - intLeft;
			for (int intOutputTxtCounter = 0; intOutputTxtCounter < ltxtchem_Output.Count; intOutputTxtCounter++)
			{
				ltxtchem_Output[intOutputTxtCounter].Width = intWidthChemBoxes;
				ltxtchem_Output[intOutputTxtCounter].Location = new Point(intLeft, intTop + (intItemHeight + 1) * intOutputTxtCounter);
			}

			// draw arrow separating interface reactant input/output
			gBackground.DrawImage(bmpArrow, new Point(intLeft - 40, intTop));

			if (ChemistRating > 85)
			{ // draw balance
				if (lImbalance.Count >0)
				{
					for (int intImbalanceCounter = 0; intImbalanceCounter < lImbalance.Count; intImbalanceCounter++)
					{
						Bitmap bmpElement = classGraphicText.getWordImage(lImbalance[intImbalanceCounter].Element.Symbol, cgrfntChemicalReactionFont_Red);
						Bitmap bmpNumber = classGraphicText.getWordImage((lImbalance[intImbalanceCounter].intQuantity > 0
																													? "+"
																													: "-")
																			+ Math.Abs( lImbalance[intImbalanceCounter].intQuantity).ToString(),
																			cgrfntChemicalReactionFont_Red);
						Point ptNumber = new Point(intLeft - 45, intTop + 40 + 20 * intImbalanceCounter);
						gBackground.DrawImage(bmpNumber, ptNumber);
						gBackground.DrawImage(bmpElement, new Point(ptNumber.X + bmpNumber.Width + 5, ptNumber.Y));
					}
				}
			}

			btnimgOk.Top 
				= btnimgCancel.Top 
				= btnimgChemist.Top 
				= Height - 85;
			btnimgOk.Left = Width - btnimgOk.width - 25;
			btnimgCancel.Left = btnimgOk.Left - btnimgCancel.width - 5;
			btnimgChemist.Left
				= lbtnChemicalInventory.Left
				= 5;
			lbtnChemicalInventory.Top = btnimgChemist.Bottom;
			buttonImage.picBackground.Controls.Add(lbtnChemicalInventory);
			buttonImage.DrawButtons();
			buttonImage.picBackground.Image = buttonImage.bmpBackground;
		}

		void setNewTextBox(ref textbox_Chemical txt)
		{
			if (ChemistRating > 80)
				txt.DisplayMode = textbox_Chemical.enuDisplayMode.Both;
			else if (ChemistRating > 60)
				txt.DisplayMode = textbox_Chemical.enuDisplayMode.Formula;
			else
				txt.DisplayMode = textbox_Chemical.enuDisplayMode.Name;

			if (ChemistRating > 90)
				txt.IntellisenseMode = textbox_Chemical.enuIntellisenseMode.Both;
			else if (ChemistRating > 70)
				txt.IntellisenseMode = textbox_Chemical.enuIntellisenseMode.Formula;
			else if (ChemistRating > 50)
				txt.IntellisenseMode = textbox_Chemical.enuIntellisenseMode.Name;
			else
				txt.IntellisenseMode = textbox_Chemical.enuIntellisenseMode.none;

			txt.Height = intItemHeight;
			txt.Font = new System.Drawing.Font("Courier", 12);
			txt.Width = (int)((double)Width * .45);

			txt.ValueEntered += new EventHandler<EventArgs>(txtChemNew_ValueEntered);
			txt.Chemical = null;
			txt.Visible = true;
			txt.ValueChanged += new EventHandler<EventArgs>(txt_ValueChanged);
			buttonImage.picBackground.Controls.Add(txt);
		}

		int intItemHeight = 24;
		void addTxtInput()
		{
			textbox_Chemical txtChemNew = new textbox_Chemical();
			setNewTextBox(ref txtChemNew);
			txtChemNew.Tag = (object)ltxtchem_Input;
			ltxtchem_Input.Add(txtChemNew);
			display();
			
			txtChemNew.Focus();
		}

		void addTxtOutput()
		{
			textbox_Chemical txtChemNew = new textbox_Chemical();
			setNewTextBox(ref txtChemNew);
			txtChemNew.Tag = (object)ltxtchem_Output;
			ltxtchem_Output.Add(txtChemNew);
			display();
			txtChemNew.Focus();
		}

		void txtChemNew_ValueEntered(object sender, EventArgs e)
		{
			textbox_Chemical txtChem = (textbox_Chemical)sender;

			if (txtChem.Chemical == null)
			{
				List<textbox_Chemical> ltxt = (List<textbox_Chemical>)txtChem.Tag;
				ltxt.Remove(txtChem);
				txtChem.Visible = false;
				txtChem.Dispose();
			}
			testReaction();
			display();
		}

		void txt_ValueChanged(object sender, EventArgs e)
		{
			testReaction();
			display();
		}

		void cleanTable()
		{
			foreach (textbox_Chemical txtChemicalInput in ltxtchem_Input)
			{
				txtChemicalInput.Visible = false;
				txtChemicalInput.Dispose();
			}
			ltxtchem_Input.Clear();

			foreach (textbox_Chemical txtChemicalOutput in ltxtchem_Output)
			{
				txtChemicalOutput.Visible = false;
				txtChemicalOutput.Dispose();
			}
			ltxtchem_Output.Clear();
		}

		void txthlInput_EnterValue(object sender, EventArgs e) { testReaction(); }
		void txthlOutput_EnterValue(object sender, EventArgs e) { testReaction(); }

		void testReaction()
		{
			cChemicalReaction = new classChemicalReaction();
			for (int intInputCounter = 0; intInputCounter < ltxtchem_Input.Count; intInputCounter++)
			{
				classChemical cChemical = ltxtchem_Input[intInputCounter].Chemical;
				if (cChemical == null) goto InvalidReaction;
				classChemicalReactant cInputReactant = new classChemicalReactant(ref cChemical, ltxtchem_Input[intInputCounter].Quantity);
				cChemicalReaction.input.add(ref cInputReactant);
			}

			for (int intOutputCounter = 0; intOutputCounter < ltxtchem_Output.Count; intOutputCounter++)
			{
				classChemical cChemical = ltxtchem_Output[intOutputCounter].Chemical;
				if (cChemical == null) goto InvalidReaction;
				classChemicalReactant cOutputReactant = new classChemicalReactant(ref cChemical, ltxtchem_Output[intOutputCounter].Quantity);
				cChemicalReaction.output.add(ref cOutputReactant);
			}

			btnimgOk.Visible = cChemicalReaction.Valid;

			if (cChemicalReaction.Valid)
			{
				// reduce equation to lowest-common-denominator
				int[] intPrimeNumbers = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 51, 53,57,59, 67 };
				for (int intCounter = 0; intCounter < intPrimeNumbers.Length; intCounter++)
				{
					bool bolUseThisNumber = true;
					foreach(classChemicalReactant cInputReactant in cChemicalReaction.input.reactants)
						if (cInputReactant.Quantity==1 || cInputReactant.Quantity % intPrimeNumbers[intCounter] != 0)
						{
							bolUseThisNumber = false;
							break;
						}
					
					if (bolUseThisNumber)
						foreach (classChemicalReactant cOutputReactant in cChemicalReaction.output.reactants)
							if (cOutputReactant.Quantity==1 || cOutputReactant.Quantity % intPrimeNumbers[intCounter] != 0)
							{
								bolUseThisNumber = false;
								break;
							}

					if (bolUseThisNumber)
					{
						foreach (classChemicalReactant cInputReactant in cChemicalReaction.input.reactants)
							cInputReactant.Quantity /= intPrimeNumbers[intCounter];
						foreach (classChemicalReactant cOutputReactant in cChemicalReaction.output.reactants)
							cOutputReactant.Quantity /= intPrimeNumbers[intCounter];
					}
				}
				lImbalance = cChemicalReaction.imbalance;
				display();
				return;
			}

		InvalidReaction:
			btnimgOk.Visible = cChemicalReaction.Valid;
			lImbalance = cChemicalReaction.imbalance;
			display();
			cChemicalReaction = null;
		}

	}

	public class textbox_Chemical : Panel
	{
		public event EventHandler<EventArgs> ValueChanged;
		protected virtual void OnValueChanged(EventArgs e) { if (ValueChanged != null) { ValueChanged(this, e); } }

		public enum enuIntellisenseMode { Name, Formula, Both, none, _numIntellisenseMode }
		public enum enuDisplayMode { Name, Formula, Both, _numDisplayMode };

		enuIntellisenseMode _eIntellisenseMode = enuIntellisenseMode.Both;
		public enuIntellisenseMode IntellisenseMode
		{
			get { return _eIntellisenseMode; }
			set
			{
				_eIntellisenseMode = value;
			}
		}

		enuDisplayMode _eDisplayMode = enuDisplayMode.Formula;
		public enuDisplayMode DisplayMode
		{
			get { return _eDisplayMode; }
			set
			{
				_eDisplayMode = value;
				textbox_Chemical_SizeChanged((object)this, new EventArgs());
			}
		}

		public event EventHandler<EventArgs> ValueEntered;
		protected virtual void OnValueEntered(EventArgs e) { if (ValueEntered != null) { ValueEntered(this, e); } }
		Bitmap _bmp = null;
		bool bolImageNeedsUpdate = true;
		public Bitmap Image
		{
			get
			{
				if (!bolImageNeedsUpdate) return _bmp;

				int intCharCounter = 0;
				while (intCharCounter < txtFormula.Text.Length && cLibString.isNumeral(txtFormula.Text[intCharCounter])) intCharCounter++;
				string strQuantity = txtFormula.Text.Substring(0, intCharCounter);
				string strFormula = txtFormula.Text;
				if (strQuantity.Length > 0)
				{
					strFormula = strFormula.Substring(strQuantity.Length);
					intQuantity = Convert.ToInt16(strQuantity);
				}
				else
				{
					intQuantity = 1;
				}
				Chemical = classChemical.GetChemical(strFormula, enuChemistry_TypeChemcalTreeSearch.Formula);
				if (Chemical != null)
				{
					if (Quantity > 1)
					{
						_bmp = new Bitmap(Chemical.image.Width + 12 * strQuantity.Length, Chemical.image.Height);
						using (Graphics g = Graphics.FromImage(_bmp))
						{
							g.DrawString(strQuantity, cgrFont.fnt, classPensAndBrushes.brBlack, new PointF(2, 2));
							SizeF szfNumeral = g.MeasureString(strQuantity, cgrFont.fnt);
							g.DrawImage(Chemical.image, new Point((int)szfNumeral.Width + 2, 0));
						}
					}
					else
						_bmp = Chemical.image;
					bolImageNeedsUpdate = false;
					return _bmp;
				}
				else return null;
			}
		}
		public TextBox txtFormula = new TextBox();
		public TextBox txtName = new TextBox();
		PictureBox picFormula = new PictureBox();

		Timer tmrHover = new Timer();
		formPopupIntellisense frmPopupIntellisense = null;

		int intQuantity;
		public int Quantity { get { return intQuantity; } }

		classChemical _cChemical = null;
		public classChemical Chemical
		{
			get { return _cChemical; }
			set
			{
				if (_cChemical != value)
				{
					txtFormula.BackColor = Color.White;
					txtName.BackColor = Color.LightGray;
					_cChemical = value;
					if (_cChemical != null)
					{
						OnValueChanged(new EventArgs());
						txtName.Text = _cChemical.Name;
						if (txtFormula.Text != _cChemical.Formula)
						{
							txtFormula.Text = (Quantity > 1
														? Quantity.ToString()
														: "")
												+ _cChemical.Formula;
							txtFormula.SelectionLength = 0;
						}
						if (txtName.Text != _cChemical.Name)
						{
							txtName.Text = _cChemical.Name;
							txtName.SelectionLength = 0;
						}
						picFormula.Visible = true;
						picFormula.BringToFront();
						picFormula.Image = Image;
					}
					else
					{
						txtName.Text = "invalid";
						txtFormula.Text = "";
					}
				}
			}
		}

		public string Text
		{
			get
			{
				switch (DisplayMode)
				{
					case enuDisplayMode.Formula:
						return txtFormula.Text;

					case enuDisplayMode.Name:
						return txtName.Text;

					default:
						return "";
				}
			}
		}

		public textbox_Chemical()
		{
			BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
			BackColor = Color.White;
			Controls.Add(picFormula); picFormula.SendToBack(); picFormula.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
			Controls.Add(txtFormula); txtFormula.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
			Controls.Add(txtName); txtName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
			textbox_Chemical txtMyReference = this;
			frmPopupIntellisense = new formPopupIntellisense(ref txtMyReference);
			txtFormula.Multiline
				= txtName.Multiline
				= true;
			txtFormula.KeyDown += new KeyEventHandler(textbox_Chemical_KeyDown);
			txtFormula.TextChanged += new EventHandler(textbox_Chemical_TextChanged);
			txtFormula.MouseWheel += new MouseEventHandler(textbox_Chemical_MouseWheel);
			txtName.KeyDown += new KeyEventHandler(textbox_Chemical_KeyDown);
			txtName.TextChanged += new EventHandler(textbox_Chemical_TextChanged);
			txtName.MouseWheel += new MouseEventHandler(textbox_Chemical_MouseWheel);
			Visible = false;
			GotFocus += new EventHandler(textbox_Chemical_GotFocus);
			tmrHover.Interval = 140;
			tmrHover.Tick += new EventHandler(tmrHover_Tick);

			SizeChanged += new EventHandler(textbox_Chemical_SizeChanged);
			VisibleChanged += new EventHandler(textbox_Chemical_VisibleChanged);
			picFormula.Click += new EventHandler(picFormula_Click);
			txtFormula.MouseLeave += new EventHandler(txtFormula_MouseLeave);
		}

		GraphicText.classMyFont cgrFont = new classMyFont(new Font("courier", 10), Color.Black);
		void txtFormula_MouseLeave(object sender, EventArgs e)
		{
			if (_eIntellisenseMode != enuIntellisenseMode.Name)
			{
				if (Chemical != null && !frmPopupIntellisense.Visible)
				{
					picFormula.Image = Image;
					picFormula.BringToFront();
					picFormula.Visible = true;
				}
			}
		}

		void picFormula_Click(object sender, EventArgs e)
		{
			txtFormula.BringToFront();
			txtFormula.Focus();
			picFormula.Visible = false;
		}

		void textbox_Chemical_GotFocus(object sender, EventArgs e) 
		{
			switch (_eDisplayMode)
			{
				case enuDisplayMode.Both:
				case enuDisplayMode.Formula:
					txtFormula.Focus();
					break;

				default:
				case enuDisplayMode.Name:
					txtName.Focus();
					break;
			}
		}

		void textbox_Chemical_VisibleChanged(object sender, EventArgs e)
		{
			if (Visible)
			{
				txtFormula.BackColor = Color.Yellow;
				//Height = 14;
				if (Chemical != null)
				{
					picFormula.Image = Image;
					picFormula.Visible = true;
					picFormula.BringToFront();
				}
				else
				{
					picFormula.Visible = false;
					txtFormula.Focus();
				}
			}
		}

		void textbox_Chemical_SizeChanged(object sender, EventArgs e)
		{
			txtFormula.Height
				= txtName.Height
				= picFormula.Height
				= DisplayMode == enuDisplayMode.Both
							? (int)((double)Height * .5)
							: Height;

			txtFormula.Top
				= picFormula.Top
				= DisplayMode == enuDisplayMode.Name
						? -txtFormula.Height
						: 0;

			txtName.Top = txtFormula.Bottom;

			txtFormula.Width
				= txtName.Width
				= picFormula.Width
				= Width;

			txtFormula.Left
				= txtName.Left
				= picFormula.Left
				= 0;

		}

		void textbox_Chemical_MouseWheel(object sender, MouseEventArgs e)
		{
			if (e.Delta > 0) frmPopupIntellisense.dec();
			else frmPopupIntellisense.inc();
		}

		int intTimeDelayCounter = 0;
		const int conTimeDelayMaxCount = 7;
		void tmrHover_Tick(object sender, EventArgs e)
		{
			if (Visible)
			{
				intTimeDelayCounter = (intTimeDelayCounter + 1) % conTimeDelayMaxCount;
				if (intTimeDelayCounter == 0)
				{
					if (frmPopupIntellisense.lcChemicals != null
						&& frmPopupIntellisense.lcChemicals.Count > 0
						&& (!picFormula.Visible || txtName.Focused))
					{

						frmPopupIntellisense.txt = txtFormula.Focused
														? txtFormula
														: txtName;
						frmPopupIntellisense.eDisplayMode = txtFormula.Focused
														? enuDisplayMode.Formula
														: enuDisplayMode.Name;
						frmPopupIntellisense.Show();
						frmPopupIntellisense.txt.Focus();
					}
					tmrHover.Enabled = false;
				}
			}
			else
				tmrHover.Enabled = false;
		}

		void textbox_Chemical_TextChanged(object sender, EventArgs e)
		{
			bolImageNeedsUpdate = true;
			intTimeDelayCounter = 0;
			setColor();
		}

		void textbox_Chemical_KeyDown(object sender, KeyEventArgs e)
		{
			TextBox txt = (TextBox)sender;
			frmPopupIntellisense.Visible = false;
			intTimeDelayCounter = 0;
			switch (IntellisenseMode)
			{
				case enuIntellisenseMode.Both:
					tmrHover.Start();
					break;

				case enuIntellisenseMode.Formula:
					if (txt == txtFormula) tmrHover.Start();
					break;

				case enuIntellisenseMode.Name:
					if (txt == txtName) tmrHover.Start();
					break;

				case enuIntellisenseMode.none:
					break;
			}


			switch (e.KeyCode)
			{
				case Keys.D0:
				case Keys.D1:
				case Keys.D2:
				case Keys.D3:
				case Keys.D4:
				case Keys.D5:
				case Keys.D6:
				case Keys.D7:
				case Keys.D8:
				case Keys.D9:
				case Keys.OemPeriod:
				case Keys.Decimal:
				case Keys.OemOpenBrackets:
				case Keys.OemCloseBrackets:
				case Keys.Shift:
				case Keys.Back:
				case Keys.Left:
				case Keys.Right:
				case Keys.Home:
				case Keys.End:
				case Keys.A:
				case Keys.B:
				case Keys.C:
				case Keys.D:
				case Keys.E:
				case Keys.F:
				case Keys.G:
				case Keys.H:
				case Keys.I:
				case Keys.J:
				case Keys.K:
				case Keys.L:
				case Keys.M:
				case Keys.N:
				case Keys.O:
				case Keys.P:
				case Keys.Q:
				case Keys.R:
				case Keys.S:
				case Keys.T:
				case Keys.U:
				case Keys.V:
				case Keys.W:
				case Keys.X:
				case Keys.Y:
				case Keys.Z:
					break;

				case Keys.Enter:
					e.SuppressKeyPress = true;
					if (txt == txtFormula)
					{
						if (txt.Text.Length > 0)
						{
							picFormula.Image = Image;
						}
						else
							Chemical = null;
					}
					else if (txt == txtName)
					{
						Chemical = classChemical.GetChemical(txtName.Text, enuChemistry_TypeChemcalTreeSearch.Name);
					}
					OnValueEntered(new EventArgs());
					break;

				default:
					frmPopupIntellisense.Hide();
					break;
			}
		}

		void setColor()
		{
			int intCharCounter = 0;
			while (intCharCounter < txtFormula.Text.Length && cLibString.isNumeral(txtFormula.Text[intCharCounter])) intCharCounter++;

			string strNumeral = txtFormula.Text.Substring(0, intCharCounter);
			string strFormula = txtFormula.Text.Substring(intCharCounter);

			if (txtFormula.Focused)
			{
				frmPopupIntellisense.lcChemicals = classChemical.getSubTree(strFormula, enuChemistry_TypeChemcalTreeSearch.Formula);
				if (frmPopupIntellisense.lcChemicals.Count > 0) txtFormula.BackColor =( _cChemical != null && _cChemical.Formula == txtFormula.Text) ? Color.White : Color.Yellow;
				else txtFormula.BackColor = Color.Red;
			}
			else if (txtName.Focused)
			{

				classChemical cCarbon = classChemical.GetChemical("carbon", enuChemistry_TypeChemcalTreeSearch.Name);
				frmPopupIntellisense.lcChemicals = classChemical.getSubTree(txtName.Text, enuChemistry_TypeChemcalTreeSearch.Name);
				if (frmPopupIntellisense.lcChemicals.Count > 0) txtName.BackColor = (_cChemical != null && _cChemical.Name.ToLower() == txtName.Text.ToLower()) ? Color.White : Color.Yellow;
				else txtName.BackColor = Color.Red;
			}
		}

		public class formPopupIntellisense : Form
		{
			public event EventHandler<EventArgs> ValueChanged;
			protected virtual void OnValueChanged(EventArgs e) { if (ValueChanged != null) { ValueChanged(this, e); } }
			public enuDisplayMode eDisplayMode = enuDisplayMode.Formula;
			classChemical cChemical = null;
			public classChemical Chemical
			{
				get { return cChemical; }
				set
				{
					cChemical = value;
					OnValueChanged(new EventArgs());
				}
			}

			public List<classChemical> lcChemicals = null;
			ListBox lstOptions = new ListBox();
			int intOptionsVisible = 11;
			int intOptionsAboveSelection = 0;

			int intSelection = 0;
			textbox_Chemical txtChemical = null;
			public TextBox txt = null;
			public formPopupIntellisense(ref textbox_Chemical textboxChemical)
			{
				txtChemical = textboxChemical;
				intOptionsAboveSelection = (int)Math.Floor((double)intOptionsVisible / 2.0);
				TopMost = true;
				lstOptions.Dock = DockStyle.Fill;
				ShowInTaskbar = false;
				FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
				Controls.Add(lstOptions);
				lstOptions.MouseWheel += new MouseEventHandler(lstOptions_MouseWheel);
				lstOptions.MouseClick += new MouseEventHandler(lstOptions_MouseClick);
				lstOptions.MouseLeave += new EventHandler(lstOptions_MouseLeave);
				lstOptions.KeyDown += new KeyEventHandler(lstOptions_KeyDown);

				VisibleChanged += new EventHandler(formPopupIntellisense_VisibleChanged);
				ValueChanged += new EventHandler<EventArgs>(formPopupIntellisense_ValueChanged);
			}

			void formPopupIntellisense_ValueChanged(object sender, EventArgs e)
			{
				if (eDisplayMode == enuDisplayMode.Formula)
					txt.Text = Chemical.Formula;
				else
					txt.Text = Chemical.Name;
				txt.SelectionLength = 0;
				txt.SelectionStart = txt.TextLength;
				txtChemical.Chemical = Chemical;
				
				Hide();
			}

			void lstOptions_KeyDown(object sender, KeyEventArgs e)
			{
				switch (e.KeyCode)
				{
					case Keys.Escape:
						Hide();
						break;

					case Keys.Enter:
						Chemical = lcChemicals[intSelection];
						Hide();
						break;
				}
			}

			void lstOptions_MouseLeave(object sender, EventArgs e) { Hide(); }

			void lstOptions_MouseClick(object sender, MouseEventArgs e)
			{
				intSelection = intSelection - intOptionsAboveSelection + lstOptions.SelectedIndex;
				if (lcChemicals != null && intSelection >= 0 && intSelection < lcChemicals.Count) Chemical = lcChemicals[intSelection];
				Hide();
			}

			void lstOptions_MouseWheel(object sender, MouseEventArgs e)
			{
				if (lcChemicals == null) return;
				if (e.Delta < 0) dec();
				else inc();
			}

			public void inc()
			{
				intSelection++;
				if (intSelection >= lcChemicals.Count - intOptionsAboveSelection - 1) intSelection = lcChemicals.Count - intOptionsAboveSelection - 1;
				showList();
			}
			public void dec()
			{
				intSelection--;
				if (intSelection < intOptionsAboveSelection) intSelection = intOptionsAboveSelection;
				showList();
			}

			void showList()
			{
				if (lcChemicals == null) return;

				string[] strOptions = new string[intOptionsVisible];
				int intIndexCounte = 0;

				for (int intOptionCounter = intSelection - intOptionsAboveSelection;
					intOptionCounter <= intSelection + intOptionsAboveSelection
						&& intIndexCounte < strOptions.Length;
					intOptionCounter++)
				{
					if (intOptionCounter >= 0 && intOptionCounter < lcChemicals.Count)
						strOptions[intIndexCounte++] = (txt == txtChemical.txtFormula)
																? lcChemicals[intOptionCounter].Formula
																: lcChemicals[intOptionCounter].Name;
				}
				lstOptions.DataSource = strOptions;
			}

			void formPopupIntellisense_VisibleChanged(object sender, EventArgs e)
			{
				if (Visible)
				{
					cChemical = null;

					Left = Control.MousePosition.X - 15;
					Top = Control.MousePosition.Y - 15;

					if (lcChemicals.Count > 11) intOptionsVisible = 11;
					else intOptionsVisible = lcChemicals.Count;
					intSelection
						= intOptionsAboveSelection
						= (int)Math.Floor((double)intOptionsVisible / 2.0);

					showList();
					TopMost = true;
					Width = 250;
					Height = intOptionsVisible * lstOptions.Font.Height + 4;

					if (Bottom > Screen.PrimaryScreen.WorkingArea.Height) Top = Screen.PrimaryScreen.WorkingArea.Height - Height;
					if (Right > Screen.PrimaryScreen.WorkingArea.Width) Left = Screen.PrimaryScreen.WorkingArea.Width - Width;
				}
				else
				{
				}
			}
		}
	}


}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO unemployable
Canada Canada
Christ Kennedy grew up in the suburbs of Montreal and is a bilingual Quebecois with a bachelor’s degree in computer engineering from McGill University. He is unemployable and currently living in Moncton, N.B. writing his next novel.

Comments and Discussions