context: using System;
  using System.CodeDom;
  using System.Collections;
  
  namespace Stetic
  {
  	public class GeneratorContext
  	{
  		CodeNamespace cns;
  		int n;
  		string idPrefix;
  		Hashtable vars = new Hashtable ();
  		ArrayList generatedWrappers = new ArrayList ();
  		WidgetMap map;
  		CodeStatementCollection statements;
  		GenerationOptions options;
  		ArrayList warnings = new ArrayList ();
  		CodeExpression rootObject;
  		
  		public GeneratorContext (CodeNamespace cns, string idPrefix, CodeStatementCollection statements, GenerationOptions options)
  		{
  			this.cns = cns;
  			this.idPrefix = idPrefix;
  			this.statements = statements;
  			this.options = options;
  			map = new WidgetMap (vars);
  		}
  		
  		public CodeNamespace GlobalCodeNamespace {
  			get { return cns; }
  		}
  		
  		public CodeStatementCollection Statements {
  			get { return statements; }
  		}
  		
  		public GenerationOptions Options {
  			get { return options; }
  		}
  		
  		public string[] Warnings {
  			get { return (string[]) warnings.ToArray (typeof(string)); }
  		}
  		
  		public void ReportWarning (string s)
  		{
  			warnings.Add (s);
  		}
  		
  		public string NewId ()
  		{
  			return idPrefix + (++n);
  		}
  		
  		public CodeExpression GenerateNewInstanceCode (Wrapper.Widget widget)
  		{
  			CodeExpression exp = widget.GenerateObjectCreation (this);
  			CodeExpression var = GenerateInstanceExpression (widget, exp);
  			GenerateBuildCode (widget, var);
  			return var;
  		}
  		
  		public virtual CodeExpression GenerateInstanceExpression (ObjectWrapper wrapper, CodeExpression newObject)
  		{
  			string varName = NewId ();
  			CodeVariableDeclarationStatement varDec = new CodeVariableDeclarationStatement (wrapper.WrappedTypeName.ToGlobalTypeRef (), varName);
  			varDec.InitExpression = newObject;
  			statements.Add (varDec);
  			return new CodeVariableReferenceExpression (varName);
  		}
  		
  		public virtual void GenerateCreationCode (ObjectWrapper wrapper, CodeExpression varExp)
  		{
  			rootObject = varExp;
  			wrapper.GenerateInitCode (this, varExp);
  			GenerateBuildCode (wrapper, varExp);
  		}
  		
  		public virtual void GenerateBuildCode (ObjectWrapper wrapper, CodeExpression var)
  		{
  			vars [wrapper] = var;
  			wrapper.GenerateBuildCode (this, var);
  			generatedWrappers.Add (wrapper);
  		}
  		
  		public virtual void GenerateCreationCode (Wrapper.ActionGroup agroup, CodeExpression var)
  		{
  			rootObject = var;
  			vars [agroup] = var;
  			agroup.GenerateBuildCode (this, var);
  		}
  		
  		public CodeExpression GenerateValue (object value, Type type)
  		{
  			return GenerateValue (value, type, false);
  		}
  		
  		public CodeExpression GenerateValue (object value, Type type, bool translatable)
  		{
  			if (value == null)
  				return new CodePrimitiveExpression (value);
  				
  			if (value.GetType ().IsEnum) {
  				if (!type.IsEnum) {
  					object ival = Convert.ChangeType (value, type);
  					return new CodePrimitiveExpression (ival);
  				} else {
  					long ival = (long) Convert.ChangeType (value, typeof(long));
  					return new CodeCastExpression (
  						value.GetType ().ToGlobalTypeRef (), 
  						new CodePrimitiveExpression (ival)
  					);
  				}
  			}
  			
  			if (value is Gtk.Adjustment) {
  				Gtk.Adjustment adj = value as Gtk.Adjustment;
  				return new CodeObjectCreateExpression (
  					typeof(Gtk.Adjustment).ToGlobalTypeRef (),
  					new CodePrimitiveExpression (adj.Value),
  					new CodePrimitiveExpression (adj.Lower),
  					new CodePrimitiveExpression (adj.Upper),
  					new CodePrimitiveExpression (adj.StepIncrement),
  					new CodePrimitiveExpression (adj.PageIncrement),
  					new CodePrimitiveExpression (adj.PageSize));
  			}
  			if (value is ushort || value is uint) {
  				return new CodeCastExpression (
  					value.GetType ().ToGlobalTypeRef (),
  					new CodePrimitiveExpression (Convert.ChangeType (value, typeof(long))));
  			}
  			if (value is ulong) {
  				return new CodeMethodInvokeExpression (
  					new CodeTypeReferenceExpression (value.GetType ()),
  					"Parse",
  					new CodePrimitiveExpression (value.ToString ()));
  			}
  			
  			if (value is ImageInfo && typeof(Gdk.Pixbuf).IsAssignableFrom (type))
  				return ((ImageInfo)value).ToCodeExpression (this);
  			
  			if (value is Wrapper.ActionGroup) {
  				return new CodeMethodInvokeExpression (
  					new CodeMethodReferenceExpression (
  						new CodeTypeReferenceExpression (new CodeTypeReference (GlobalCodeNamespace.Name + ".ActionGroups", CodeTypeReferenceOptions.GlobalReference)),
  						"GetActionGroup"
  					),
  					new CodePrimitiveExpression (((Wrapper.ActionGroup)value).Name)
  				);
  			}
  			
  			if (value is Array) {
  				ArrayList list = new ArrayList ();
  				foreach (object val in (Array)value)
  					list.Add (GenerateValue (val, val != null ? val.GetType() : null, translatable));
  				return new CodeArrayCreateExpression (value.GetType().GetElementType(), (CodeExpression[]) list.ToArray(typeof(CodeExpression)));
  			}
  			
  			if (value is DateTime) {
  				return new CodeObjectCreateExpression (
  					typeof(DateTime).ToGlobalTypeRef (),
  					new CodePrimitiveExpression (((DateTime)value).Ticks)
  				);
  			}
  			
  			if (value is TimeSpan) {
  				return new CodeObjectCreateExpression (
  					typeof(TimeSpan).ToGlobalTypeRef (),
  					new CodePrimitiveExpression (((TimeSpan)value).Ticks)
  				);
  			}
  			
  			string str = value as string;
  			if (translatable && str != null && str.Length > 0 && options.UseGettext) {
  				return new CodeMethodInvokeExpression (
  					new CodeTypeReferenceExpression (new CodeTypeReference (options.GettextClass, CodeTypeReferenceOptions.GlobalReference)),
  					"GetString",
  					new CodePrimitiveExpression (str)
  				);
  			}
  			
  			return new CodePrimitiveExpression (value);
  		}
  		
  		public WidgetMap WidgetMap {
  			get { return map; }
  		}
  
  		public System.CodeDom.CodeExpression RootObject {
  			get {
  				return rootObject;
  			}
  			set {
  				rootObject = value;
  			}
  		}
  		
  		public void EndGeneration ()
  		{
  			foreach (ObjectWrapper w in generatedWrappers) {
  				CodeExpression var = (CodeExpression) vars [w];
  				w.GeneratePostBuildCode (this, var);
  			}
  		}
  		
  		public void Reset ()
  		{
  			vars.Clear ();
  			generatedWrappers.Clear ();
  			map = new WidgetMap (vars);
  			n = 0;
  		}
  		
  		public CodeExpression GenerateLoadPixbuf (string name, Gtk.IconSize size)
  		{
  			bool found = false;
  			foreach (CodeTypeDeclaration t in cns.Types) {
  				if (t.Name == "IconLoader") {
  					found = true;
  					break;
  				}
  			}
  			
  			if (!found)
  			{
  				CodeTypeDeclaration cls = new CodeTypeDeclaration ("IconLoader");
  				cls.Attributes = MemberAttributes.Private;
  				cls.TypeAttributes = System.Reflection.TypeAttributes.NestedAssembly;
  				cns.Types.Add (cls);
  				
  				CodeMemberMethod met = new CodeMemberMethod ();
  				cls.Members.Add (met);
  				met.Attributes = MemberAttributes.Public | MemberAttributes.Static;
  				met.Name = "LoadIcon";
  				met.Parameters.Add (new CodeParameterDeclarationExpression (typeof(Gtk.Widget), "widget"));
  				met.Parameters.Add (new CodeParameterDeclarationExpression (typeof(string), "name"));
  				met.Parameters.Add (new CodeParameterDeclarationExpression (typeof(Gtk.IconSize), "size"));
  				met.ReturnType = new CodeTypeReference (typeof(Gdk.Pixbuf));
  				
  				CodeExpression widgetExp = new CodeVariableReferenceExpression ("widget");
  				CodeExpression nameExp = new CodeVariableReferenceExpression ("name");
  				CodeExpression sizeExp = new CodeVariableReferenceExpression ("size");
  				CodeExpression szExp = new CodeVariableReferenceExpression ("sz");
  				CodeExpression mgExp = new CodeBinaryOperatorExpression (szExp,  CodeBinaryOperatorType.Divide, new CodePrimitiveExpression (4));
  				CodeExpression pmapExp = new CodeVariableReferenceExpression ("pmap");
  				CodeExpression gcExp = new CodeVariableReferenceExpression ("gc");
  				CodeExpression szM1Exp = new CodeBinaryOperatorExpression (szExp, CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression (1));
  				CodeExpression zeroExp = new CodePrimitiveExpression (0);
  				CodeExpression resExp = new CodeVariableReferenceExpression ("res");
  				
  				met.Statements.Add (
  					new CodeVariableDeclarationStatement (typeof(Gdk.Pixbuf), "res",
  						new CodeMethodInvokeExpression (
  							widgetExp,
  							"RenderIcon",
  							nameExp,
  							sizeExp,
  							new CodePrimitiveExpression (null)
  						)
  					)
  				);
  				
  				CodeConditionStatement nullcheck = new CodeConditionStatement ();
  				met.Statements.Add (nullcheck);
  				nullcheck.Condition = new CodeBinaryOperatorExpression (
  					resExp,
  					CodeBinaryOperatorType.IdentityInequality,
  					new CodePrimitiveExpression (null)
  				);
  				nullcheck.TrueStatements.Add (new CodeMethodReturnStatement (resExp));
  				
  				// int sz, h;
  				// Gtk.Icon.SizeLookup (size, out sz, out h);
  
  				nullcheck.FalseStatements.Add (new CodeVariableDeclarationStatement (typeof(int), "sz"));
  				nullcheck.FalseStatements.Add (new CodeVariableDeclarationStatement (typeof(int), "sy"));
  				nullcheck.FalseStatements.Add (new CodeMethodInvokeExpression (
  				    new CodeTypeReferenceExpression (typeof(Gtk.Icon).ToGlobalTypeRef ()),
  				    "SizeLookup",
  				    sizeExp,
  				    new CodeDirectionExpression (FieldDirection.Out, szExp),
  				    new CodeDirectionExpression (FieldDirection.Out, new CodeVariableReferenceExpression ("sy"))
  				));
  
  				CodeTryCatchFinallyStatement trycatch = new CodeTryCatchFinallyStatement ();
  				nullcheck.FalseStatements.Add (trycatch);
  				trycatch.TryStatements.Add (
  					new CodeMethodReturnStatement (
  						new CodeMethodInvokeExpression (
  							new CodePropertyReferenceExpression (
  								new CodeTypeReferenceExpression (new CodeTypeReference (typeof(Gtk.IconTheme))),
  								"Default"
  							),
  							"LoadIcon",
  							nameExp,
  							szExp,
  							zeroExp
  						)
  					)
  				);
  				
  				CodeCatchClause ccatch = new CodeCatchClause ();
  				trycatch.CatchClauses.Add (ccatch);				
  				
  				CodeConditionStatement cond = new CodeConditionStatement ();
  				ccatch.Statements.Add (cond);
  				
  				cond.Condition = new CodeBinaryOperatorExpression (
  					nameExp,
  					CodeBinaryOperatorType.IdentityInequality,
  					new CodePrimitiveExpression ("gtk-missing-image")
  				);
  				
  				cond.TrueStatements.Add (
  					new CodeMethodReturnStatement (
  						new CodeMethodInvokeExpression (
  							new CodeTypeReferenceExpression (cns.Name + "." + cls.Name),
  							"LoadIcon",
  							widgetExp,
  							new CodePrimitiveExpression ("gtk-missing-image"),
  							sizeExp
  						)
  					)
  				);
  				
  				CodeStatementCollection stms = cond.FalseStatements;
  				
  				stms.Add (
  					new CodeVariableDeclarationStatement (typeof(Gdk.Pixmap), "pmap", 
  						new CodeObjectCreateExpression (
  							typeof(Gdk.Pixmap),
  							new CodePropertyReferenceExpression (
  								new CodePropertyReferenceExpression (
  									new CodeTypeReferenceExpression (typeof(Gdk.Screen)),
  									"Default"
  								),
  								"RootWindow"
  							),
  							szExp,
  							szExp
  						)
  					)
  				);
  				stms.Add (
  					new CodeVariableDeclarationStatement (typeof(Gdk.GC), "gc", 
  						new CodeObjectCreateExpression (typeof(Gdk.GC), pmapExp)
  					)
  				);
  				stms.Add (
  					new CodeAssignStatement (
  						new CodePropertyReferenceExpression (
  							gcExp,
  							"RgbFgColor"
  						),
  						new CodeObjectCreateExpression (
  							typeof(Gdk.Color),
  							new CodePrimitiveExpression (255),
  							new CodePrimitiveExpression (255),
  							new CodePrimitiveExpression (255)
  						)
  					)
  				);
  				stms.Add (
  					new CodeMethodInvokeExpression (
  						pmapExp,
  						"DrawRectangle",
  						gcExp,
  						new CodePrimitiveExpression (true),
  						zeroExp,
  						zeroExp,
  						szExp,
  						szExp
  					)
  				);
  				stms.Add (
  					new CodeAssignStatement (
  						new CodePropertyReferenceExpression (
  							gcExp,
  							"RgbFgColor"
  						),
  						new CodeObjectCreateExpression (
  							typeof(Gdk.Color),
  							zeroExp, zeroExp, zeroExp
  						)
  					)
  				);
  				stms.Add (
  					new CodeMethodInvokeExpression (
  						pmapExp,
  						"DrawRectangle",
  						gcExp,
  						new CodePrimitiveExpression (false),
  						zeroExp,
  						zeroExp,
  						szM1Exp,
  						szM1Exp
  					)
  				);
  				stms.Add (
  					new CodeMethodInvokeExpression (
  						gcExp,
  						"SetLineAttributes",
  						new CodePrimitiveExpression (3),
  						new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof(Gdk.LineStyle)), "Solid"),
  						new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof(Gdk.CapStyle)), "Round"),
  						new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof(Gdk.JoinStyle)), "Round")
  					)
  				);
  				stms.Add (
  					new CodeAssignStatement (
  						new CodePropertyReferenceExpression (
  							gcExp,
  							"RgbFgColor"
  						),
  						new CodeObjectCreateExpression (
  							typeof(Gdk.Color),
  							new CodePrimitiveExpression (255),
  							zeroExp,
  							zeroExp
  						)
  					)
  				);
  				stms.Add (
  					new CodeMethodInvokeExpression (
  						pmapExp,
  						"DrawLine",
  						gcExp,
  						mgExp,
  						mgExp,
  						new CodeBinaryOperatorExpression (szM1Exp, CodeBinaryOperatorType.Subtract, mgExp),
  						new CodeBinaryOperatorExpression (szM1Exp, CodeBinaryOperatorType.Subtract, mgExp)
  					)
  				);
  				stms.Add (
  					new CodeMethodInvokeExpression (
  						pmapExp,
  						"DrawLine",
  						gcExp,
  						new CodeBinaryOperatorExpression (szM1Exp, CodeBinaryOperatorType.Subtract, mgExp),
  						mgExp,
  						mgExp,
  						new CodeBinaryOperatorExpression (szM1Exp, CodeBinaryOperatorType.Subtract, mgExp)
  					)
  				);
  				stms.Add (
  					new CodeMethodReturnStatement (
  						new CodeMethodInvokeExpression (
  							new CodeTypeReferenceExpression (typeof(Gdk.Pixbuf)),
  							"FromDrawable",
  							pmapExp,
  							new CodePropertyReferenceExpression (pmapExp, "Colormap"),
  							zeroExp, zeroExp, zeroExp, zeroExp, szExp, szExp
  						)
  					)
  				);
  			}
  			
  			int sz, h;
  			Gtk.Icon.SizeLookup (size, out sz, out h);
  			
  			return new CodeMethodInvokeExpression (
  				new CodeTypeReferenceExpression (new CodeTypeReference (cns.Name + ".IconLoader", CodeTypeReferenceOptions.GlobalReference)),
  				"LoadIcon",
  				rootObject,
  				new CodePrimitiveExpression (name),
  			    new CodeFieldReferenceExpression (
  					new CodeTypeReferenceExpression (new CodeTypeReference (typeof(Gtk.IconSize), CodeTypeReferenceOptions.GlobalReference)),
  					size.ToString ()
  				)
  			);
  		}
  	}
  
  	public class WidgetMap
  	{
  		Hashtable vars;
  		
  		internal WidgetMap (Hashtable vars)
  		{
  			this.vars = vars;
  		}
  		
  		public CodeExpression GetWidgetExp (ObjectWrapper wrapper)
  		{
  			return (CodeExpression) vars [wrapper];
  		}
  		
  		public CodeExpression GetWidgetExp (object wrapped)
  		{
  			ObjectWrapper w = ObjectWrapper.Lookup (wrapped);
  			if (w != null)
  				return GetWidgetExp (w);
  			else
  				return null;
  		}
  	}
  	
  	[Serializable]
  	public class GenerationOptions
  	{
  		bool useGettext;
  		bool partialClasses;
  		bool generateEmptyBuildMethod;
  		bool generateSingleFile = true;
  		bool failForUnknownWidgets = false;
  		string path;
  		string globalNamespace = "Stetic";
  		string gettextClass;
  		string resourceLoader;
  
  		public bool UseGettext {
  			get { return useGettext; }
  			set { useGettext = value; }
  		}
  		
  		public string GettextClass {
  			get {
  				if (string.IsNullOrEmpty (gettextClass))
  					return "Mono.Unix.Catalog";
  				else
  					return gettextClass;
  			}
  			set { gettextClass = value; }
  		}
  		
  		public string ImageResourceLoaderClass {
  			get {
  				if (string.IsNullOrEmpty (resourceLoader))
  					return "Gdk.Pixbuf";
  				else
  					return resourceLoader;
  			}
  			set { resourceLoader = value; }
  		}
  
  		public bool UsePartialClasses {
  			get { return partialClasses; }
  			set { partialClasses = value; }
  		}
  		
  		public string Path {
  			get { return path; }
  			set { path = value; }
  		}
  		
  		public bool GenerateEmptyBuildMethod {
  
gt: 			get { return generateEmptyBuildMethod; }