/*
 * Bernoulli Compiler
 * Copyright (c) Cornell University
 * Department of Computer Science
 * 
 * Kamen Yotov (kamen@yotov.org)
 * 
 * $Source: C:/CVS/kyotov/kyotov/Research/BC/PassGenerator/Generate.cs,v $
 * $Revision: 1.3 $
 * $Date: 2003/02/23 10:07:26 $
 */

using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Diagnostics;

using Microsoft.CSharp;

using BC.Core.ASTs;

using XTensions.OOCodeDOM;

namespace GTransformation
{
	class GTransformation
	{
		static Hashtable h = new Hashtable();

		static ArrayList D (Type t)
		{
			ArrayList a;

			if (h.Contains(t))
				a = (ArrayList)h[t];
			else
				h[t] = a = new ArrayList();

			return a;
		}

		static void Print (Type t, int i)
		{
			Console.WriteLine("{0}{1}", new string('\t', i), t.Name);

			foreach (Type c in D(t))
				Print(c, i + 1);
		}

		static void Main (string[] a)
		{
			foreach (Type t in typeof(AbstractNode).Assembly.GetTypes())
			{
				Type p = t.BaseType;

				if (p != typeof(object))
					D(p).Add(t);
			}

			//Print(typeof(AbstractNode), 0);

			GeneratePasses();
		}

		static CodeExpression Context
		{
			get
			{
				return Make.FieldReference(Make.This, "Context");
			}
		}

		static CodeTypeMember[] GenerateWalkerTypeMemebers (Type t)
		{
			ArrayList r = new ArrayList();
			ArrayList l = new ArrayList();

			if (h.Contains(t))
			{
				if (t == typeof(AbstractNode))
				{
					l.Add(Make.Statement(Make.Invoke(Context, "Add", Make.Expressions(Make.VariableReference("_")))));

					r.Add(Make.Field(typeof(ArrayList), "Context", MemberAttributes.Family));

					r.Add(Make.Method("Execute", 
						MemberAttributes.Public | MemberAttributes.Override, 
						typeof(void), 
						Make.Parameters(
							Make.Parameter(typeof(Elements), "_", FieldDirection.Ref),
							Make.Parameter(typeof(object), "o", FieldDirection.In)
						), 
						Make.Statements(
							Make.Statement(Make.Invoke(Make.Expression(Make.TypeReference(typeof(Console))), "Write", Make.Expressions(
								Make.Constant("PASS: {0}... "),
								Make.FieldReference(
									Make.Invoke(Make.This, "GetType", Make.Expressions()),
									"Name")))),
							Make.AssignStatement(Context, Make.NewExpression(typeof(ArrayList), Make.Expressions())),
							Make.Statement(Make.Invoke(Make.This, "VisitElements", Make.Expressions(
								Make.VariableReference("_"), 
								Make.VariableReference("o")
							))),
							Make.Statement(Make.Invoke(Make.Expression(Make.TypeReference(typeof(Console))), "WriteLine", Make.Expressions(
								Make.Constant("Done!"))))
						)
					));
				}

				CodeStatement s = Make.Statement(Make.Invoke(
					Make.VariableReference("System.Diagnostics.Debug"), 
					"Assert", 
					Make.Expressions(
						Make.Constant(false),
						Make.Constant(string.Format("invalid {0}", t.Name))
					)
				));

				foreach (Type c in D(t))
				{
					r.AddRange(GenerateWalkerTypeMemebers(c));

					s = Make.If(
						Make.Snippet("_ is " + c.FullName),
						Make.Statements(
							Make.Statement(
								Make.Invoke(
									Make.This, 
									"Visit" + c.Name, 
									Make.Expressions(
										Make.Cast(c, Make.VariableReference("_")),
										Make.VariableReference("o")
									)
								)
							)
						),
						Make.Statements(s)
					);
				}

				l.Add(s);

				if (t == typeof(AbstractNode))
					l.Add(Make.Statement(Make.Invoke(Context, "RemoveAt", Make.Expressions(
						Make.FieldReference(Context, "Count") - Make.Constant(1)))));
			}
			else
				foreach (FieldInfo fi in t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
					if (fi.FieldType.IsSubclassOf(typeof(AbstractNode)))
						l.Add(Make.Statement(Make.Invoke(Make.This, "VisitAbstractNode", 
							Make.Expressions(
								Make.FieldReference(Make.VariableReference("_"), fi.Name),
								Make.VariableReference("o")
							)
						)));
					else if (fi.FieldType.IsArray && fi.FieldType.GetElementType().IsSubclassOf(typeof(AbstractNode)))
						l.Add(Make.If(Make.FieldReference(Make.VariableReference("_"), fi.Name) != Make.Constant(null),
							Make.Statements(
								Make.For(
									Make.Declaration(typeof(int), "i", Make.Constant(0)),
									Make.VariableReference("i") < Make.FieldReference(Make.FieldReference(Make.VariableReference("_"), fi.Name), "Length"),
									Make.AssignStatement(Make.VariableReference("i"), Make.VariableReference("i") + Make.Constant(1)),
									Make.Statements(Make.Statement(Make.Invoke(Make.This, "VisitAbstractNode", Make.Expressions(
										Make.FieldReference(Make.VariableReference("_"), fi.Name)[Make.VariableReference("i")],
										Make.VariableReference("o")
									))))
								)
							),
							Make.Statements()
						));

			CodeStatement[] ss = new CodeStatement[l.Count];

			l.CopyTo(ss);

			r.Add(Make.Method(
				"Visit" + t.Name, 
				MemberAttributes.Family, 
				typeof(void),
				Make.Parameters(
					Make.Parameter(t, "_", FieldDirection.In),
					Make.Parameter(typeof(object), "o", FieldDirection.In)
				),
				ss
			));
			
			CodeTypeMember[] m = new CodeTypeMember[r.Count];

			r.CopyTo(m);

			return m;
		}

		static CodeTypeMember[] GenerateRendererTypeMemebers (Type t)
		{
			ArrayList r = new ArrayList();

			if (h.Contains(t))
			{
				CodeStatement[] s = Make.Statements(
					Make.Statement(Make.Invoke(
						Make.VariableReference("System.Diagnostics.Debug"), 
						"Assert", 
						Make.Expressions(
							Make.Constant(false),
							Make.Constant(string.Format("invalid {0}", t.Name))
						)
					)),
					Make.Return(Make.Constant(null))
				);

				foreach (Type c in D(t))
				{
					r.AddRange(GenerateRendererTypeMemebers(c));

					s = Make.Statements(Make.If(
						Make.Snippet("_ is " + c.FullName),
						Make.Statements(
							Make.Return(
								Make.Invoke(
									Make.This, 
									"Render" + c.Name, 
									Make.Expressions(
										Make.Cast(c, Make.VariableReference("_"))
									)
								)
							)
						),
						s
					));
				}

				r.Add(Make.Method(
					"Render" + t.Name, 
					MemberAttributes.Public, 
					typeof(string),
					Make.Parameters(Make.Parameter(t, "_", FieldDirection.In)),
					Make.Statements(s)));
			}
			else
				r.Add(Make.Method(
					"Render" + t.Name, 
					MemberAttributes.Abstract | MemberAttributes.Public, 
					typeof(string),
					Make.Parameters(Make.Parameter(t, "_", FieldDirection.In)),
					Make.Statements()));

			CodeTypeMember[] m = new CodeTypeMember[r.Count];

			r.CopyTo(m);

			return m;
		}

		static void GeneratePasses ()
		{
			Console.WriteLine(Make.CompileUnit(
				Make.Namespace("BC.Core.Passes",
					Make.Declaration("Pass", Make.TypeReference(typeof(object)), TypeAttributes.Public | TypeAttributes.Abstract,
						Make.Method("Execute", 
							MemberAttributes.Public | MemberAttributes.Abstract, 
							typeof(void), 
							Make.Parameters(
								Make.Parameter(typeof(Elements), "_", FieldDirection.Ref),
								Make.Parameter(typeof(object), "o", FieldDirection.In)
							), 
							Make.Statements()
						),
						Make.Method("Execute", 
							MemberAttributes.Public | MemberAttributes.Final, 
							typeof(void), 
							Make.Parameters(
								Make.Parameter(typeof(Elements), "_", FieldDirection.Ref)
							), 
							Make.Statements(
								Make.Statement(Make.Invoke(Make.This, "Execute", Make.Expressions(
									Make.Snippet("ref _"),
									Make.Constant(null)
								)))
							)
						)
					),
					Make.Declaration("Walker", Make.TypeReference("Pass"), TypeAttributes.Public | TypeAttributes.Abstract,
						GenerateWalkerTypeMemebers(typeof(AbstractNode))
					),
					Make.Declaration("AbstractRenderer", Make.TypeReference("Pass"), TypeAttributes.Public | TypeAttributes.Abstract,
						GenerateRendererTypeMemebers(typeof(AbstractNode))
					)
				)
			).Source(Language.CSharp));
		}
	}
}
