using System;
using System.Text;
using System.Collections;
using System.Drawing;
using Tutor.Structure;


namespace Tutor.Imaging
{
	/// <summary>
	/// Summary description for ProduceImage.
	/// </summary>

	public class CacheDetails
	{
		public Graphics G;
		public Font Symbol;
		public Font SymbolSmall;

		public Font BlackBoard;


		public Font F;
		public Font FSmall;
		public SolidBrush C;
		public int TranslateX;
		public int TranslateY;
		public Pen P;
		public int OperatorBuffer;
		public bool InSmall;
		public int ParenWidth;
		public int ParenHeight;
		public double PowerOverlapRatio;
	}

	public interface EquationImage
	{
		void Draw(CacheDetails CD);
		int Width(CacheDetails CD);
		int Height(CacheDetails CD);
		int AlignPosition(CacheDetails CD);
	};
	public class DrawText : EquationImage
	{
		public string text;
		bool Small;
		int W = -1;
		int H = -1;
		int A = -1;

		public static string [] GreekLetters = new string [] { "Alpha", "Beta", "Gamma", "Delta",
																 "Epsilon",
																 "Zeta",
																 "Eta",
																 "Theta",
																 "Iota",
																 "Kappa",
																 "Lambda",
																 "Mu",
																 "Nu",
																 "Xi",
																 "Omicron",
																 "Pi",
																 "Rho","??",
																 "Sigma",
																 "Tau",
																 "Upsilon",
																 "Phi",
																 "Chi",
																 "Psi",
																 "Omega" };

		public DrawText(string txt, bool small)
		{
			text = txt;
			Small = small;
			for(int k = 0; k < GreekLetters.Length; k++)
			{
				if(txt.ToUpper().Equals(GreekLetters[k].ToUpper()))
				{
					if( Char.IsUpper(txt[0]) )
					{
						text = System.Text.UnicodeEncoding.Unicode.GetString( new byte[] { (byte) (0x91 + k),  0x03 }, 0, 2);
						return;
					}
					else
					{
						text = System.Text.UnicodeEncoding.Unicode.GetString( new byte[] { (byte) (0xB1 + k),  0x03 }, 0, 2);
						return;
					}
				}
			}
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			if(Small)
				CD.G.DrawString(text, CD.FSmall, CD.C, CD.TranslateX, CD.TranslateY);
			else
				CD.G.DrawString(text, CD.F, CD.C, CD.TranslateX, CD.TranslateY);
		}

		public int Width(CacheDetails CD)
		{
			if(W == -1)
			{
				if(Small)
				{	W = (int) CD.G.MeasureString(text, CD.FSmall).Width;	}
				else
				{	W = (int) CD.G.MeasureString(text, CD.F).Width;		}
			}
			return W;
		}

		public int Height(CacheDetails CD)
		{
			if(H == -1)
			{
				if(Small)
				{	H = (int) CD.G.MeasureString(text, CD.FSmall).Height;	}
				else
				{	H = (int) CD.G.MeasureString(text, CD.F).Height;		}		
			}
			return H;
		}

		public int AlignPosition(CacheDetails CD)
		{
			if(A == -1)
			{
				if(Small)
				{	A = (int) CD.G.MeasureString(text, CD.FSmall).Height/2;	}
				else
				{	A = (int) CD.G.MeasureString(text, CD.F).Height/2;		}		
			}
			return A;
		}
		#endregion
	}

	public class DrawCustomFont  : EquationImage
	{
		public string text;
		Font Fnt;
		int W = -1;
		int H = -1;
		int A = -1;

		public DrawCustomFont(string txt, Font f)
		{
			text = txt;
			Fnt = f;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
				CD.G.DrawString(text, Fnt, CD.C, CD.TranslateX, CD.TranslateY + 3);
		}

		public int Width(CacheDetails CD)
		{
			if(W == -1)
			{
				W = (int) CD.G.MeasureString(text, Fnt).Width;
			}
			return W;
		}

		public int Height(CacheDetails CD)
		{
			if(H == -1)
			{
				H = (int) CD.G.MeasureString(text, Fnt).Height;
			}
			return H;
		}

		public int AlignPosition(CacheDetails CD)
		{
			if(A == -1)
			{
				A = (int) CD.G.MeasureString(text, Fnt).Height/2;
				
			}
			return A;
		}
		#endregion
	}

	public class DrawSymbol  : EquationImage
	{
		public string text;
		bool Small;
		int W = -1;
		int H = -1;
		int A = -1;

		public DrawSymbol(string txt, bool small)
		{
			text = txt;
			Small = small;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			if(Small)
				CD.G.DrawString(text, CD.SymbolSmall, CD.C, CD.TranslateX, CD.TranslateY - 1);
			else
				CD.G.DrawString(text, CD.Symbol, CD.C, CD.TranslateX, CD.TranslateY - 1);
		}

		public int Width(CacheDetails CD)
		{
			if(W == -1)
			{
				if(Small)
				{	W = (int) CD.G.MeasureString(text, CD.SymbolSmall).Width;	}
				else
				{	W = (int) CD.G.MeasureString(text, CD.Symbol).Width;		}
			}
			return W;
		}

		public int Height(CacheDetails CD)
		{
			if(H == -1)
			{
				if(Small)
				{	H = (int) CD.G.MeasureString(text, CD.SymbolSmall).Height;	}
				else
				{	H = (int) CD.G.MeasureString(text, CD.Symbol).Height;		}		
			}
			return H;
		}

		public int AlignPosition(CacheDetails CD)
		{
			if(A == -1)
			{
				if(Small)
				{	A = (int) CD.G.MeasureString(text, CD.SymbolSmall).Height/2;	}
				else
				{	A = (int) CD.G.MeasureString(text, CD.Symbol).Height/2;		}		
			}
			return A;
		}
		#endregion
	}

	public class Translate : EquationImage
	{
		public int dX;
		public int dY;
		EquationImage Child;

		private int W = -1;
		private int H = -1;
		private int A = -1;

		public Translate(int dx, int dy, EquationImage child)
		{
			dX = dx;
			dY = dy;
			Child = child;
		}

		#region EquationImage Members
		public void Draw(CacheDetails CD)
		{
			CD.TranslateX += dX;
			CD.TranslateY += dY;
			Child.Draw(CD);
			CD.TranslateX -= dX;
			CD.TranslateY -= dY;
		}

		public int Width(CacheDetails CD)
		{
			if(W == -1)
				W = Child.Width(CD);
			return W;
		}

		public int Height(CacheDetails CD)
		{
			if(H == -1)
				H = Child.Height(CD);
			return H;
		}

		public int AlignPosition(CacheDetails CD)
		{
			if(A == -1)
				A = Child.AlignPosition(CD);
			return A;
		}
		#endregion
	}

	public class Group : EquationImage
	{
		public ArrayList Children;
		public int width;
		public int height;
		public int align;

		public Group()
		{
			Children = new ArrayList();
		}

		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			foreach(EquationImage EI in Children)
				EI.Draw(CD);
		}

		public int Width(CacheDetails CD)
		{
			return width;
		}

		public int Height(CacheDetails CD)
		{
			return height;
		}
		public int AlignPosition(CacheDetails CD)
		{
			return align;
		}
		#endregion
	}

	public class Bar : EquationImage
	{
		public int Length;

		public Bar(int L)
		{
			Length = L;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			CD.G.DrawLine(CD.P, CD.TranslateX, CD.TranslateY, CD.TranslateX + Length, CD.TranslateY);
		}

		public int Width(CacheDetails CD)
		{
			return Length;
		}

		public int Height(CacheDetails CD)
		{
			return 1;
		}

		public int AlignPosition(CacheDetails CD)
		{
			return 0;
		}
		#endregion
	}

	public class Parenthesis : EquationImage
	{
		EquationImage Child;
		
		public Parenthesis(EquationImage c)
		{
			Child = c;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			int bW = Child.Width(CD);
			int bH = Child.Height(CD);
			//			CD.G.DrawLine(CD.P, CD.TranslateX + 1, CD.TranslateY, CD.TranslateX, CD.TranslateY+bH);
			//			CD.G.DrawLine(CD.P, CD.TranslateX + bW, CD.TranslateY, CD.TranslateX, CD.TranslateY+bH);
			int x = CD.TranslateX;
			int y = CD.TranslateY;
			int dx = CD.ParenWidth;
			int dy = CD.ParenHeight;

			CD.G.DrawArc(CD.P,x,y,dx*2,dy*2,-90,-90);
			CD.G.DrawLine(CD.P, x, y + dy, x, y + bH + dy);
			CD.G.DrawArc(CD.P,x,y+bH,dx*2,dy*2,-180,-90);

			CD.G.DrawArc(CD.P,x + bW ,y,dx*2,dy*2,0,-90);
			CD.G.DrawLine(CD.P, x + bW + 2*dx, y + dy, x + bW + 2*dx, y + bH + dy);
			CD.G.DrawArc(CD.P,x + bW ,y+bH,dx*2,dy*2,0,90);

			CD.TranslateX += dx;
			CD.TranslateY += dy;
			Child.Draw(CD);
			CD.TranslateX -= dx;
			CD.TranslateY -= dy;
		}

		public int Width(CacheDetails CD)
		{
			return Child.Width(CD) + 2 * CD.ParenWidth + CD.OperatorBuffer;
		}

		public int Height(CacheDetails CD)
		{
			return Child.Height(CD) + 2 * CD.ParenHeight + CD.OperatorBuffer;
		}

		public int AlignPosition(CacheDetails CD)
		{
			return Child.AlignPosition(CD) + CD.ParenHeight;
		}

		#endregion
	}

	public class AbsoluteValue : EquationImage
	{
		EquationImage Child;
		
		public AbsoluteValue(EquationImage c)
		{
			Child = c;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			int bW = Child.Width(CD);
			int bH = Child.Height(CD);
			int x = CD.TranslateX;
			int y = CD.TranslateY;
			int dx = CD.ParenWidth/2;
			int dy = CD.ParenHeight/2;

			CD.G.DrawLine(CD.P, x, y + dy, x, y + bH + dy);
			CD.G.DrawLine(CD.P, x + bW + dx*2 + CD.OperatorBuffer, y + dx, x + bW + dx*2 + CD.OperatorBuffer, y + bH + dy);
			CD.TranslateX += CD.ParenWidth/2 + CD.OperatorBuffer;
			CD.TranslateY += CD.ParenHeight/2 + CD.OperatorBuffer;
			Child.Draw(CD);
			CD.TranslateX -= CD.ParenWidth/2 + CD.OperatorBuffer;
			CD.TranslateY -= CD.ParenHeight/2 + CD.OperatorBuffer;
		}

		public int Width(CacheDetails CD)
		{
			return Child.Width(CD) +  CD.ParenWidth + 2 * CD.OperatorBuffer;
		}

		public int Height(CacheDetails CD)
		{
			return Child.Height(CD) + CD.ParenHeight + 2 * CD.OperatorBuffer;
		}

		public int AlignPosition(CacheDetails CD)
		{
			return Child.AlignPosition(CD) + CD.ParenHeight/2;
		}

		#endregion
	}
	public class Shrink : EquationImage
	{
		EquationImage Child;
		public Shrink(EquationImage c)
		{
			Child = c;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			bool orig = CD.InSmall;
			CD.InSmall = true;
			Child.Draw(CD);
			CD.InSmall = orig;
		}

		public int Width(CacheDetails CD)
		{
			int r;
			bool orig = CD.InSmall;
			CD.InSmall = true;
			r = Child.Width(CD);
			CD.InSmall = orig;
			return r;
		}

		public int Height(CacheDetails CD)
		{
			int r;
			bool orig = CD.InSmall;
			CD.InSmall = true;
			r = Child.Height(CD);
			CD.InSmall = orig;
			return r;
		}

		public int AlignPosition(CacheDetails CD)
		{
			int r;
			bool orig = CD.InSmall;
			CD.InSmall = true;
			r = Child.AlignPosition(CD);
			CD.InSmall = orig;
			return r;
		}

		#endregion
	}

	public class Line : EquationImage
	{
		public int StartX, StartY;
		public int EndX, EndY;
		public Line(int sx, int sy, int ex, int ey)
		{
			StartX = sx;
			StartY = sy;
			EndX = ex;
			EndY = ey;
		}
		#region EquationImage Members

		public void Draw(CacheDetails CD)
		{
			CD.G.DrawLine(CD.P, CD.TranslateX + StartX, CD.TranslateY + StartY, CD.TranslateX + EndX, CD.TranslateY + EndY);
		}

		public int Width(CacheDetails CD)
		{
			return EndX - StartX;
		}

		public int Height(CacheDetails CD)
		{
			return EndY - StartY;
		}

		public int AlignPosition(CacheDetails CD)
		{
			return 0;
		}
		#endregion


	}



	
	public class ProduceImage
	{
		public static EquationImage ProduceBinary(CacheDetails CD, ArrayList Ele, string Sym)
		{
			Group G = new Group();
			Translate [] T = new Translate[2];
			int MaxDiff = 0;
			int MaxAlign = 0;
			int Idx = 0;
			foreach(Equation S in Ele)
			{
				EquationImage Result = Produce(S, CD);
				T[Idx] = new Translate(0,0, Result);
				int H = Result.Height(CD);
				int A = Result.AlignPosition(CD);
				if(A > MaxAlign) MaxAlign = A;
				if((H - A) > MaxDiff) MaxDiff = (H - A);
				Idx ++;
			}
			//DrawText Operator = new DrawText(equ.GetTypStr(equ.Typ), CD.InSmall);

			DrawSymbol Operator = new DrawSymbol(Sym, CD.InSmall);

			int RunningX = 0;
			for(int k = 0; k < T.Length; k++)
			{
				T[k].dX = RunningX;
				T[k].dY = MaxAlign - T[k].AlignPosition(CD);
				RunningX += T[k].Width(CD);
				G.Children.Add(T[k]);
				if(k != T.Length - 1)
				{
					G.Children.Add(new Translate(RunningX + CD.OperatorBuffer, MaxAlign - Operator.Height(CD)/2 + CD.OperatorBuffer, Operator));
					RunningX += Operator.Width(CD) + CD.OperatorBuffer * 2;
				}
			}
			G.height = MaxAlign + MaxDiff;
			G.align = MaxAlign;
			G.width = RunningX;
			return G;
		}

		public static EquationImage Produce(Equation E, CacheDetails CD)
		{
			#region Constants & Variables
			if(E is EquConstant)
			{
				return new DrawText( "" + ((EquConstant)E).Value, CD.InSmall);
			}
			if(E is EquVariable)
			{
				EquVariable EV = (EquVariable) E;
				if(EV.Variable.Equals("DotDotDot"))
				{
					return new DrawText( "...", CD.InSmall);
				}
			}
			if(E is EquVariable)
			{
				EquVariable EV = (EquVariable) E;

				string Sym = "?";
				switch(EV.Variable)
				{
					case ">": Sym = "" + (char) 0x3E;
						break;
					case ">=" : Sym = "" + (char) 0xB3;
						break;
					case "<": Sym = "" + (char) 0x3C;
						break;
					case "<=": Sym = "" + (char) 0xA3;
						break;
					case "!=": Sym = "" + (char) 0xB9;
						break;
				}
				if(!Sym.Equals("?"))
					return new DrawSymbol(Sym, CD.InSmall);

				int kBar = EV.Variable.IndexOf("_");
				if(kBar > 0)
				{
					Group G = new Group();
					string Vl = EV.Variable.Substring(0,kBar);
					string SubScript = EV.Variable.Substring(kBar+1);
					DrawText VDT = new DrawText(Vl,CD.InSmall);
					DrawText SDT = new DrawText(SubScript, true);

					int HR = VDT.Height(CD);
					int HS = SDT.Height(CD);

					G.width = VDT.Width(CD) + SDT.Width(CD);
					G.align = VDT.AlignPosition(CD);

					int Offset = (int) (VDT.AlignPosition(CD) );

					G.height = Math.Max(VDT.Height(CD), SDT.Height(CD) + Offset);


					G.Children.Add(VDT);
					G.Children.Add(new Translate(VDT.Width(CD) - CD.OperatorBuffer, Offset, SDT));
					return G;
				}
				return new DrawText( "" + EV.Variable, CD.InSmall);
			}
			#endregion
			#region Division
			if(E is EquDivide)
			{
				EquationImage Top    = Produce( ((EquDivide) E).Numerator , CD);
				EquationImage Bottom = Produce( ((EquDivide) E).Denominator , CD);
				int W = Math.Max(Top.Width(CD),Bottom.Width(CD)) + CD.OperatorBuffer * 3;

				Translate tT = new Translate( (W - Top.Width(CD)) / 2    , 0, Top);
				Translate tB = new Translate( (W - Bottom.Width(CD)) / 2  , Top.Height(CD) + CD.OperatorBuffer + 1 , Bottom);

				Group G = new Group();
				G.width = W;
				G.height = Top.Height(CD) + Bottom.Height(CD) + CD.OperatorBuffer + 1;
				G.Children.Add(tT);
				G.Children.Add(tB);
				G.Children.Add(new Translate(0, Top.Height(CD) + CD.OperatorBuffer * 2, new Bar(W)));
				G.align = Top.Height(CD) + CD.OperatorBuffer;
				return G;
			}
			#endregion
			#region SpecialFunctions

			if(E is EquFunctionApplication)
			{
				EquFunctionApplication EFA= (EquFunctionApplication) E;
				if(EFA.FunctionName.Equals("SubScript"))
				{
					EquationImage VDT     = Produce( (Equation) EFA.Parameters[0]  , CD);
					bool orig = CD.InSmall;
					CD.InSmall = true;
					EquationImage SDT     = Produce( (Equation) EFA.Parameters[1]  , CD);
					CD.InSmall = orig;

					int HR = VDT.Height(CD);
					int HS = SDT.Height(CD);
					Group G = new Group();
					G.width = VDT.Width(CD) + SDT.Width(CD) + CD.OperatorBuffer * 2;
					G.align = VDT.AlignPosition(CD);

					int Offset = VDT.Height(CD) - SDT.AlignPosition(CD);

					G.height = Math.Max(VDT.Height(CD), SDT.Height(CD) + Offset);


					G.Children.Add(VDT);
					G.Children.Add(new Translate(VDT.Width(CD) + CD.OperatorBuffer, Offset, SDT));
					return G;
				}
				if(EFA.FunctionName.Equals("BB"))
				{
					if( EFA.Parameters[0] is EquVariable)
					{
						EquVariable EV = (EquVariable) EFA.Parameters[0];
						return new DrawCustomFont(EV.Variable, CD.BlackBoard);
					}
				}
				if(EFA.FunctionName.Equals("Paren"))
				{
					return new Parenthesis( Produce( (Equation) EFA.Parameters[0]  , CD) );
				}
				if(EFA.FunctionName.Equals("In"))
				{
					ArrayList Ele = new ArrayList();
					Ele.Add( EFA.Parameters[0] );
					Ele.Add( EFA.Parameters[1] );
					return ProduceBinary(CD, Ele, "" + (char) 0xCE);
				}
			}
			#endregion
			#region Functions
			if(E is EquFunctionApplication)
			{
				EquFunctionApplication EFA = (EquFunctionApplication) E;
				Group G = new Group();
				Translate [] T = new Translate[EFA.Parameters.Count];
				int MaxDiff = 0;
				int MaxAlign = 0;
				int Idx = 0;
				foreach(Equation S in EFA.Parameters)
				{
					EquationImage Result = Produce(S, CD);
					T[Idx] = new Translate(0,0, Result);
					int H = Result.Height(CD);
					int A = Result.AlignPosition(CD);
					if(A > MaxAlign) MaxAlign = A;
					if((H - A) > MaxDiff) MaxDiff = (H - A);
					Idx ++;
				}
				DrawText Operator = new DrawText(",", CD.InSmall);
				int RunningX = 0;
				for(int k = 0; k < T.Length; k++)
				{
					T[k].dX = RunningX;
					T[k].dY = MaxAlign - T[k].AlignPosition(CD);
					RunningX += T[k].Width(CD);
					G.Children.Add(T[k]);
					if(k != T.Length - 1)
					{
						G.Children.Add(new Translate(RunningX + CD.OperatorBuffer, MaxAlign - Operator.Height(CD)/2 + CD.OperatorBuffer, Operator));
						RunningX += Operator.Width(CD) + CD.OperatorBuffer * 2;
					}
				}
				G.height = MaxAlign + MaxDiff;
				G.align = MaxAlign;
				G.width = RunningX;

				EquationImage Param = new Parenthesis(G);
				EquationImage FuncName = Produce(new EquVariable(EFA.FunctionName),CD);
					
				Group G2 = new Group();
				G2.height = Math.Max(Param.Height(CD), FuncName.Height(CD));
				G2.align = Math.Max(Param.AlignPosition(CD), FuncName.AlignPosition(CD));
				G2.width = Param.Width(CD) + FuncName.Width(CD) + CD.OperatorBuffer*3;
				G2.Children.Add(new Translate(0, G2.align - FuncName.AlignPosition(CD), FuncName));
				G2.Children.Add(new Translate(FuncName.Width(CD) + CD.OperatorBuffer*2, G2.align - Param.AlignPosition(CD), Param));
					
					
				return G2;

			}
			#endregion
			#region Addtion & Multiplication

			if(E is EquAddition)
			{
				EquAddition Add = (EquAddition) E;
				Group G = new Group();
				Translate [] T = new Translate[Add._Terms.Count];
				bool [] Negate = new bool[Add._Terms.Count];
				int MaxDiff = 0;
				int MaxAlign = 0;
				int Idx = 0;
				foreach(Equation SP in Add._Terms)
				{
					Equation S = SP;
					bool neg = false;
					if( Idx > 0)
					{
						if(S is EquNegation)
						{
							S = ((EquNegation) S).Term;
							neg = true;
						}
					}
					EquationImage Result = Produce(S, CD);
					if(!S.simple()) Result = new Parenthesis(Result);
					Negate[Idx] = neg;
					T[Idx] = new Translate(0,0, Result);
					int H = Result.Height(CD);
					int A = Result.AlignPosition(CD);
					if(A > MaxAlign) MaxAlign = A;
					if((H - A) > MaxDiff) MaxDiff = (H - A);
					Idx ++;
				}
				DrawText Operator = new DrawText("+", CD.InSmall);
				DrawText OperatorNeg = new DrawText("-", CD.InSmall);
				int RunningX = 0;
				for(int k = 0; k < T.Length; k++)
				{
					T[k].dX = RunningX;
					T[k].dY = MaxAlign - T[k].AlignPosition(CD);
					RunningX += T[k].Width(CD);
					G.Children.Add(T[k]);
					if(k != T.Length - 1)
					{
						if(Negate[k+1])
						{
							G.Children.Add(new Translate(RunningX + CD.OperatorBuffer, MaxAlign - OperatorNeg.Height(CD)/2 + CD.OperatorBuffer, OperatorNeg));
							RunningX += OperatorNeg.Width(CD) + CD.OperatorBuffer * 2;
						}
						else
						{
							G.Children.Add(new Translate(RunningX + CD.OperatorBuffer, MaxAlign - Operator.Height(CD)/2 + CD.OperatorBuffer, Operator));
							RunningX += Operator.Width(CD) + CD.OperatorBuffer * 2;
						}
					}
				}
				G.height = MaxAlign + MaxDiff;
				G.align = MaxAlign;
				G.width = RunningX;
				return G;
			}
			if(E is EquMultiplication)
			{
				EquMultiplication Mult = (EquMultiplication) E;
				Group G = new Group();
				Translate [] T = new Translate[Mult._Factors.Count];
				int MaxDiff = 0;
				int MaxAlign = 0;
				int Idx = 0;
				foreach(Equation S in Mult._Factors)
				{
					EquationImage Result = Produce(S, CD);
					if(!S.simple() || S is EquNegation) Result = new Parenthesis(Result);
					T[Idx] = new Translate(0,0, Result);
					int H = Result.Height(CD);
					int A = Result.AlignPosition(CD);
					if(A > MaxAlign) MaxAlign = A;
					if((H - A) > MaxDiff) MaxDiff = (H - A);
					Idx ++;
				}
				DrawText Operator = new DrawText("·", CD.InSmall);
				int RunningX = 0;
				for(int k = 0; k < T.Length; k++)
				{
					T[k].dX = RunningX;
					T[k].dY = MaxAlign - T[k].AlignPosition(CD);
					RunningX += T[k].Width(CD);
					G.Children.Add(T[k]);
					if(k != T.Length - 1)
					{
						G.Children.Add(new Translate(RunningX + CD.OperatorBuffer, MaxAlign - Operator.Height(CD)/2 + CD.OperatorBuffer, Operator));
						RunningX += Operator.Width(CD) + CD.OperatorBuffer * 2;
					}
				}
				G.height = MaxAlign + MaxDiff;
				G.align = MaxAlign;
				G.width = RunningX;
				return G;
			}
			#endregion
			#region Powers
			if(E is EquPower)
			{
				EquationImage Base     = Produce( ((EquPower) E).Base , CD);
				if( (! ((EquPower) E).Base.simple()) || (((EquPower) E).Base is EquPower)  || (((EquPower) E).Base is EquMultiplication) )
					Base = new Parenthesis(Base);
				bool orig = CD.InSmall;
				CD.InSmall = true;
				EquationImage Prior = new Shrink(Produce( ((EquPower) E).Power , CD));
				if( ! ((EquPower) E).Power.simple() && ! (((EquPower) E).Power is EquAddition) ) Prior = new Parenthesis(Prior);
				EquationImage Exponent = Prior;
				CD.InSmall = orig;

				int expH = Exponent.Height(CD);
				int expW = Exponent.Width(CD);
				int PixelOverlap = (int)( CD.PowerOverlapRatio * Exponent.Height(CD));
				Translate tBase = new Translate( 0 , expH - PixelOverlap - CD.OperatorBuffer, Base);
				Translate tExp = new Translate( Base.Width(CD) , 0 , Exponent);
				Group G = new Group();
				G.width = Base.Width(CD) + expW;
				G.height = expH + Base.Height(CD) - PixelOverlap - CD.OperatorBuffer;
				G.align = G.height - Base.AlignPosition(CD);
				G.Children.Add(tBase);
				G.Children.Add(tExp);
				return G;
			}
			#endregion
			#region Logs

			if(E is EquLog)
			{
				EquationImage Term     = Produce( ((EquLog) E).Term , CD);
				if( ! ((EquLog) E).Term.simple() )
					Term = new Parenthesis(Term);
				
				bool orig = CD.InSmall;
				CD.InSmall = true;
				EquationImage Prior = new Shrink(Produce( ((EquLog) E).Base  , CD));
				if( ! ((EquLog) E).Base.simple() ) Prior = new Parenthesis(Prior);
				EquationImage Base = Prior;
				CD.InSmall = orig;

				EquationImage LogText = new DrawText("log",false);

				int PixelOverlap = (int)( CD.PowerOverlapRatio * LogText.Height(CD));

				Group G = new Group();
				int Height = Math.Max(LogText.Height(CD), LogText.Height(CD) - PixelOverlap + Base.Height(CD));
				Height = Math.Max(Height, Term.Height(CD));

				G.height = Height;
				G.width = LogText.Width(CD) + CD.OperatorBuffer * 2 + Base.Width(CD) + Term.Width(CD);
				G.align = Math.Max(LogText.AlignPosition(CD), Term.AlignPosition(CD));

				G.Children.Add(new Translate(0, G.align - LogText.AlignPosition(CD), LogText));
				G.Children.Add(new Translate(LogText.Width(CD) - CD.OperatorBuffer, G.align - LogText.AlignPosition(CD) + LogText.Height(CD) - PixelOverlap, Base));
				G.Children.Add(new Translate(LogText.Width(CD) + CD.OperatorBuffer * 2 + Base.Width(CD), G.align - Term.AlignPosition(CD), Term));

				return G;
			}


			#endregion
			#region AbsoluteValue
			if(E is EquAbsoluteValue)
			{
				EquationImage inner = Produce( ((EquAbsoluteValue)E).Term, CD);
				return new AbsoluteValue(inner);
			}
			#endregion
			#region Negation
			if(E is EquNegation)
			{
				EquationImage inner = Produce( ((EquNegation)E).Term, CD);
				if( ! ((EquNegation)E).Term.simple() || ((EquNegation)E).Term is EquMultiplication ) inner = new Parenthesis(inner);
				Group G = new Group();
				DrawText Negate = new DrawText("-", CD.InSmall);
				int A = inner.AlignPosition(CD);

				G.align = A;

				G.Children.Add(new Translate(Negate.Width(CD) + CD.OperatorBuffer,0, inner));
				G.Children.Add(new Translate(0, A - Negate.Height(CD)/2, Negate));
				G.width = Negate.Width(CD) + CD.OperatorBuffer + inner.Width(CD);
				G.height = inner.Height(CD);
				G.align = A;
				return G;

			}
			#endregion
			#region Equality
			if(E is EquEquality)
			{
				EquEquality equ = (EquEquality) E;

				Group G = new Group();
				Translate [] T = new Translate[2];
				ArrayList Ele = new ArrayList();
				Ele.Add(equ.Left);
				Ele.Add(equ.Right);
				int MaxDiff = 0;
				int MaxAlign = 0;
				int Idx = 0;
				foreach(Equation S in Ele)
				{
					EquationImage Result = Produce(S, CD);
					T[Idx] = new Translate(0,0, Result);
					int H = Result.Height(CD);
					int A = Result.AlignPosition(CD);
					if(A > MaxAlign) MaxAlign = A;
					if((H - A) > MaxDiff) MaxDiff = (H - A);
					Idx ++;
				}
				DrawText Operator = new DrawText("=", CD.InSmall);
				int RunningX = 0;
				for(int k = 0; k < T.Length; k++)
				{
					T[k].dX = RunningX;
					T[k].dY = MaxAlign - T[k].AlignPosition(CD);
					RunningX += T[k].Width(CD);
					G.Children.Add(T[k]);
					if(k != T.Length - 1)
					{
						G.Children.Add(new Translate(RunningX + CD.OperatorBuffer, MaxAlign - Operator.Height(CD)/2 + CD.OperatorBuffer, Operator));
						RunningX += Operator.Width(CD) + CD.OperatorBuffer * 2;
					}
				}
				G.height = MaxAlign + MaxDiff;
				G.align = MaxAlign;
				G.width = RunningX;
				return G;
			}
			#endregion
			#region InEquality
			if(E is EquInEquality )
			{
				EquInEquality equ = (EquInEquality) E;

				ArrayList Ele = new ArrayList();
				Ele.Add(equ.Left);
				Ele.Add(equ.Right);

				string Sym = "?";
				switch(equ.Typ)
				{
					case EquInEqualityType.GreaterThan: Sym = "" + (char) 0x3E;
						break;
					case EquInEqualityType.GreaterThanEqual: Sym = "" + (char) 0xB3;
						break;
					case EquInEqualityType.LessThan: Sym = "" + (char) 0x3C;
						break;
					case EquInEqualityType.LessThanEqual: Sym = "" + (char) 0xA3;
						break;
					case EquInEqualityType.NotEqual: Sym = "" + (char) 0xB9;
						break;
				}
				return ProduceBinary(CD, Ele, Sym);
			}
			#endregion
			#region Roots

			// arbitrary roots

			if(E is EquRoot)
			{
				// assuming the root is smaller than the term

				EquationImage Term = Produce( ((EquRoot) E).Term , CD);
				bool orig = CD.InSmall;
				CD.InSmall = true;
				EquationImage Root = new Shrink(Produce( ((EquRoot) E).Root , CD));
				CD.InSmall = orig;

				int rootDist = 1;
				int rootExp  = 10;
				
				string TheRootText = System.Text.UnicodeEncoding.Unicode.GetString( new byte[] { (byte) (0x1A),  0x22 }, 0, 2);

				int rW = Root.Width(CD);
				int rH = Root.Height(CD);
				bool ShowRoot = true;
				if(((EquRoot) E).Root is EquConstant)
				{
					if( ((EquConstant) ((EquRoot) E).Root).Value == 2)
					{
						ShowRoot = false;
						rW /= 2;
					}
				}
				int tW = Term.Width(CD);
				int tH = Term.Height(CD);
				if(rH < tH - rootExp)
				{
					int TotalHeight = 2 * CD.OperatorBuffer + tH;
					int TotalWidth = rW + rootDist + tW;

					Group G = new Group();

					if(ShowRoot)
						G.Children.Add(new Translate(0, TotalHeight - rootExp - rH,Root) );

					int rStart = TotalHeight - rootExp + CD.OperatorBuffer;

//					G.Children.Add(new Line(rW/2, rStart + 2 * CD.OperatorBuffer, rW, rStart + 2 * CD.OperatorBuffer));
//					G.Children.Add(new Line(rW, rStart + 2 * CD.OperatorBuffer, rW + rootDist, TotalHeight));
//					G.Children.Add(new Line(rW + rootDist, 0, rW + rootDist, TotalHeight));
//					G.Children.Add(new Line(rW + rootDist, 0,rW + rootDist + tW + 2 * CD.OperatorBuffer, 0));

					
					G.Children.Add(new Line(rW - rootExp / 2 - 3, rStart + 2 * CD.OperatorBuffer + 3, rW - rootExp / 2, rStart + 2 * CD.OperatorBuffer));
					G.Children.Add(new Line(rW - rootExp / 2, rStart + 2 * CD.OperatorBuffer, rW + rootDist, TotalHeight));
					G.Children.Add(new Line(rW + rootDist, 0, rW + rootDist, TotalHeight));
					G.Children.Add(new Line(rW + rootDist, 0, rW + rootDist + tW + 2 * CD.OperatorBuffer, 0));
					
					G.Children.Add(new Translate(rW + rootDist + 2*CD.OperatorBuffer, CD.OperatorBuffer, Term));

					G.width = rW + rootDist + tW + CD.OperatorBuffer * 4;
					G.height = TotalHeight + 2 * CD.OperatorBuffer;
					G.align = Term.AlignPosition(CD) + CD.OperatorBuffer;
					return G;
				}
				else
				{
					int TotalHeight = 2 * CD.OperatorBuffer + rH + rootExp;
					int TotalWidth = rW + rootDist + tW;
					int TermHeight = CD.OperatorBuffer + tH;
					Group G = new Group();

					if(ShowRoot)
						G.Children.Add(new Translate(0, TotalHeight - rootExp - rH,Root) );

					int rStart = TotalHeight - rootExp + CD.OperatorBuffer;
					//G.Children.Add(new Line(rW - 2, rStart + 2 * CD.OperatorBuffer + 2, rW, rStart + 2 * CD.OperatorBuffer));

					G.Children.Add(new Line(rW - rootExp / 2 - 3, rStart + 2 * CD.OperatorBuffer + 3, rW - rootExp / 2, rStart + 2 * CD.OperatorBuffer));
					G.Children.Add(new Line(rW - rootExp / 2, rStart + 2 * CD.OperatorBuffer, rW + rootDist, TotalHeight));
					G.Children.Add(new Line(rW + rootDist, TotalHeight - TermHeight, rW + rootDist, TotalHeight));
					G.Children.Add(new Line(rW + rootDist, TotalHeight - TermHeight, rW + rootDist + tW + 2 * CD.OperatorBuffer, TotalHeight - TermHeight));



					G.Children.Add(new Translate(rW + rootDist + 2*CD.OperatorBuffer, CD.OperatorBuffer + TotalHeight - TermHeight, Term));

					G.width = rW + rootDist + tW + CD.OperatorBuffer * 4;
					G.height = TotalHeight + 2 * CD.OperatorBuffer;
					G.align = TotalHeight - rootExp;
					return G;
				}
			}


			#endregion
			if(E is MatrixAlgebra.Matrix)
			{
				MatrixAlgebra.Matrix M = E as MatrixAlgebra.Matrix;
				EquationImage [][] Mei = new EquationImage[M.matrix.Length][];
				for(int r = 0; r < Mei.Length; r++) Mei[r] = new EquationImage[M.matrix[r].Length];
				int maxW = 0;
				int maxH = 0;
				int maxA = 0;
				for(int r = 0; r < M.matrix.Length; r++)
				{
					for(int c = 0; c < M.matrix[r].Length; c++)
					{
						Mei[r][c] = Produce( M.matrix[r][c] , CD);
						int w = Mei[r][c].Width(CD);
						int h = Mei[r][c].Height(CD);
						int a = Mei[r][c].AlignPosition(CD);
						if(w > maxW) maxW = w;
						if(h > maxH) maxH = h;
						if(a > maxA) maxA = a;
					}
				}
				Group G = new Group();
				G.height = (maxH + 8) * M.matrix.Length - 4;
				G.width = (maxW + 8) * M.matrix[0].Length - 4;
				G.align = G.height / 2;

				for(int r = 0; r < M.matrix.Length; r++)
				{
					for(int c = 0; c < M.matrix[r].Length; c++)
					{
						G.Children.Add( new Translate(c * (maxW + 8), r * (maxH + 8) + (maxA - Mei[r][c].AlignPosition(CD)), Mei[r][c]));
					}
				}
				return new Parenthesis(G);
		
			}
			return null;
		}
		public static Bitmap Produce(Equation E, bool AddParen)
		{
			Bitmap B = new Bitmap(2,2);

			CacheDetails CD = new CacheDetails();
			CD.G =  Graphics.FromImage(B);
			CD.F = new Font("Times New Roman", 12);
			CD.C = new SolidBrush(Color.Black);
			CD.TranslateX = 3;
			CD.TranslateY = 3;
			CD.OperatorBuffer = 1;
			CD.FSmall = new Font("Times New Roman", 8);
			CD.InSmall = false;
			CD.P = new Pen(new SolidBrush(Color.Black), 1);
			CD.PowerOverlapRatio = 0.35;
			CD.ParenHeight = 4;
			CD.ParenWidth = 4;
			CD.Symbol = new Font("Symbol",12);
			CD.SymbolSmall = new Font("Symbol",8);
			CD.BlackBoard = new Font("Mathematica7Mono",14);

			EquationImage EI;
			if(AddParen) EI = new Parenthesis(Produce(E, CD));
			else EI = Produce(E, CD);

			int W = 3 + EI.Width(CD) + 3;
			int H = 3 + EI.Height(CD) + 3;

			B.Dispose();
			B = new Bitmap(W,H);
			CD.G.Dispose();
			CD.G = Graphics.FromImage(B);
			CD.G.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

			CD.G.Clear(Color.White);
			if(EI != null)
				EI.Draw(CD);

			CD.P.Dispose();
			CD.FSmall.Dispose();
			CD.F.Dispose();
			CD.C.Dispose();
			CD.G.Dispose();

			return B;
		}
	}
}
