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

using System;
using System.IO;
using System.Collections;
using System.Diagnostics;

using BC.Core.ASTs;
using BC.Core.Passes;

namespace BC.C
{
	public class CRenderer: Renderer
	{
		protected string f;
	
		public CRenderer (string f)
		{
			this.f = f;
		}

		public override void Execute (ref Elements e, object o)
		{
			Indentation = 0;

			FlatInitializerList = 0;

			StreamWriter w = new StreamWriter(f);

			w.Write(RenderElements(e));

			w.Close();
		}

		protected const string NewLine = "\r\n";

		protected int Indentation;

		protected string Indent
		{
			get
			{
				return new string('\t', Indentation);
			}
		}

		protected int FlatInitializerList;

		public override string RenderFieldDesignator (FieldDesignator fd)
		{
			return string.Format(".{0}", fd.Field);
		}

		public override string RenderIndexDesignator (IndexDesignator id)
		{
			return string.Format("[{0}]", RenderAbstractExpression(id.Index));
		}

		public override string RenderInitializer (Initializer i)
		{
			string r = string.Empty;

			if (i.Designator != null)
				foreach (AbstractDesignator d in i.Designator)
					r += RenderAbstractDesignator(d);

			return string.Format("{0}{1}{2}", r, r == string.Empty ? r : " = ", RenderAbstractExpression(i.Expression));
		}

		public override string RenderInitializerList (InitializerList il)
		{
			string r;

			if (FlatInitializerList > 0)
				r = string.Format("{{{0}}}", Definitions.Join(il.Initializer, new Stringer(InitializerStringer), ", "));
			else
			{
				Indentation++;

				r = string.Format("{{{0}{1}", NewLine, Definitions.Join(il.Initializer, new Stringer(IndentedInitializerStringer), "," + NewLine, JoinDelimiter.Back));

				Indentation--;

				r += Indent + "}";
			}

			return r;
		}

		public override string RenderNamedExpression (NamedExpression e)
		{
			return e.Name;
		}

		public override string RenderIntegerConstant (IntegerConstant c)
		{
			string r = c.Value;

			if (!c.Signed)
				r += "U";

			if (c.Precision > Definitions.IT_signed_int.Precision)
				if (c.Precision == Definitions.IT_signed_long_int.Precision)
					r += "L";
				else if (c.Precision == Definitions.IT_signed_long_long_int.Precision)
					r += "LL";

			return r;
		}

		public override string RenderRealConstant (RealConstant c)
		{
			string r = c.Value.ToString("G17");

			if (c.Precision != Definitions.RT_double.Precision)
				if (c.Precision == Definitions.RT_float.Precision)
					r += "F";
				else if (c.Precision == Definitions.RT_long_double.Precision)
					r += "L";

			return r;
		}

		public override string RenderCharacterConstant (CharacterConstant c)
		{
			return string.Format("{0}'{1}'", c.Unicode ? "L" : string.Empty, c.Value);
		}

		public override string RenderStringConstant (StringConstant c)
		{
			return string.Format("{0}\"{1}\"", c.Unicode ? "L" : string.Empty, c.Value);
		}

		public override string RenderUnaryExpression (UnaryExpression e)
		{
			return string.Format(Definitions.PostOperation(e) ? "{1}{0}" : "{0}{1}", 
				Definitions.OperatorToString(e.Operator), 
				RenderAbstractExpression(e.Operand, e, Direction.Right));
		}

		public override string RenderBinaryExpression (BinaryExpression e)
		{
			return string.Format("{0} {1} {2}", 
				RenderAbstractExpression(e.LeftOperand, e, Direction.Left), 
				Definitions.OperatorToString(e.Operator), 
				RenderAbstractExpression(e.RightOperand, e, Direction.Right));
		}

		public override string RenderTrinaryExpression (TrinaryExpression e)
		{
			return string.Format("{0} ? {1} : {2}", 
				RenderAbstractExpression(e.Condition, e, Direction.Right), 
				RenderAbstractExpression(e.TrueResult, e),
				RenderAbstractExpression(e.FalseResult, e, Direction.Left));
		}

		public override string RenderCastExpression (CastExpression e)
		{
			return string.Format("({0}){1}", 
				RenderAbstractType(e.Type, null), RenderAbstractExpression(e.Operand, e));
		}

		public override string RenderSizeOfExpression (SizeOfExpression e)
		{
			return string.Format("sizeof({0})", 
				RenderAbstractExpression(e.Operand, e));
		}

		public override string RenderSizeOfTypeExpression (SizeOfTypeExpression e)
		{
			return string.Format("sizeof({0})", 
				RenderAbstractType(e.Operand, null));
		}

		public override string RenderFieldExpression (FieldExpression e)
		{
			return string.Format("{0}.{1}", 
				RenderAbstractExpression(e.Operand, e), e.Field);
		}

		public override string RenderArrayExpression (ArrayExpression e)
		{
			return string.Format("{0}[{1}]",
				RenderAbstractExpression(e.Operand, e), 
				Definitions.Join(e.Index, new Stringer(ExpressionStringer), "]["));
		}

		public override string RenderFunctionExpression (FunctionExpression e)
		{
			return string.Format("{0}({1})", 
				RenderAbstractExpression(e.Operand, e, Direction.Right), 
				Definitions.Join(e.Argument, new Stringer(ExpressionStringer), ", "));
		}

		public override string RenderCompositeExpression (CompositeExpression e)
		{
			FlatInitializerList++;
			
			string r = string.Format("({0}){1}", RenderAbstractType(e.Type, null), RenderInitializerList(e.Operand));

			FlatInitializerList--;

			return r;
		}

		protected override bool OperationNeedsGrouping (AbstractExpression e, AbstractNode p, Direction d)
		{
			return
				Definitions.OperationPrecedence(p) < Definitions.OperationPrecedence(e) ||
				Definitions.OperationPrecedence(p) == Definitions.OperationPrecedence(e) && (d == Direction.Right) ^ Definitions.BackwardOperation(p);
		}

		protected override string Group (string s)
		{
			return string.Format("({0})", s);
		}

		public override string RenderLabeledStatement (LabeledStatement s)
		{
			string l;

			Indentation--;

			if (s.Case)
				if (s.Expression != null)
					l = string.Format("{0}case {1}:{2}", Indent, RenderAbstractExpression(s.Expression), NewLine);
				else
					l = string.Format("{0}default:{1}", Indent, NewLine);
			else
				l = string.Format("{0}{1}:{2}", Indent, s.Label, NewLine);

			Indentation++;

			return l + IndentedStatementStringer(s.Statement);
		}

		public override string RenderExpressionStatement (ExpressionStatement s)
		{
			return string.Format("{0};", RenderAbstractExpression(s.Expression));
		}

		public override string RenderDeclarationStatement (DeclarationStatement s)
		{
			return string.Format("{0};", RenderDeclaration(s.Declaration));
		}

		public override string RenderCommentStatement (CommentStatement s)
		{
			return string.Format("/*{0}*/", s.Content);
		}

		public override string RenderCompoundStatement (CompoundStatement s)
		{
			Indentation--;

			string r = Indent + "{" + NewLine;

			Indentation++;

			r += Definitions.Join(s.Statement, new Stringer(IndentedStatementStringer), string.Empty);

			Indentation--;

			r += Indent + "}";

			Indentation++;

			return r;
		}

		public override string RenderSwitchStatement (SwitchStatement s)
		{
			string r = string.Format("switch ({0}){1}", RenderAbstractExpression(s.Expression), NewLine); 
			
			Indentation++;

			r += RenderAbstractStatement(s.Statement);

			Indentation--;

			return r;
		}

		public override string RenderIfStatement (IfStatement s)
		{
			string r = string.Format("if ({0}){1}", RenderAbstractExpression(s.Expression), NewLine);

			r += SubstatementStringer(s.Statement);

			if (s.ElseStatement != null)
				r += Indent + "else" + NewLine + SubstatementStringer(s.ElseStatement);

			return r;
		}

		public override string RenderForStatement (ForStatement s)
		{
			return String.Format("for ({0}; {1}; {2}){3}{4}", 
				RenderAbstractExpression(s.Init), 
				RenderAbstractExpression(s.Expression), 
				RenderAbstractExpression(s.Update), 
				NewLine,
				SubstatementStringer(s.Statement));
		}

		public override string RenderLoopStatement (LoopStatement s)
		{
			string c = string.Format("while ({0})", RenderAbstractExpression(s.Expression));

			if(s.PostCheck)
				return "do" + NewLine + IndentedStatementStringer(s.Statement) + Indent + c + ";" + NewLine;
			else
				return c + NewLine + IndentedStatementStringer(s.Statement) + NewLine;
		}

		public override string RenderGotoStatement (GotoStatement s)
		{
			return string.Format("goto {0};", s.Target);
		}

		public override string RenderBreakStatement (BreakStatement s)
		{
			return "break;";
		}

		public override string RenderContinueStatement (ContinueStatement s)
		{
			return "continue;";
		}

		public override string RenderReturnStatement (ReturnStatement s)
		{
			return "return" + (s.Expression == null ? "" : " " + RenderAbstractExpression(s.Expression)) + ";";
		}

		protected bool OnlyIdentifier (string n)
		{
			foreach (char c in n)
				if ("*&[]()".IndexOf(c) >= 0)
					return false;

			return true;
		}

		public override string RenderAbstractStatement (AbstractStatement s)
		{
			string r = base.RenderAbstractStatement(s);
			
			if (!(s is AbstractCombinationStatement))
				r += NewLine;

			return r;
		}

		public override string RenderEnumerationItem (EnumerationItem ei)
		{
			throw new Exception();
		}

		public override string RenderEnumerationType (EnumerationType et)
		{
			string w = RenderAbstractTypeQualifiers(et) + "enum " + et.Name + NewLine + Indent + "{";

			Indentation++;

			w += Definitions.Join(et.Item, new Stringer(IndentedEnumerationItemStringer), NewLine);

			Indentation--;

			return w + NewLine + Indent + "}" + PrependedCurrentTypeValue;
		}

		public override string RenderFunctionType (FunctionType ft)
		{
			return RenderAbstractTypeQualifiers(ft) + RenderAbstractType(ft.Base, string.Format("{0}({1})", CurrentTypeValue, Definitions.Join(ft.Argument, new Stringer(DeclarationStringer), ", ")));
		}

		public override string RenderArrayType (ArrayType at)
		{
			string n = CurrentTypeValue;
			string r = RenderAbstractTypeQualifiers(at);

			if (at.Static)
				r = "static " + r;

			if (at.Dimension != null)
				foreach (AbstractExpression e in at.Dimension)
				{
					n = string.Format("{0}[{1}{2}]", n, r, e != null ? RenderAbstractExpression(e) : "*");

					r = string.Empty;
				}
			else
				n = string.Format("{0}[{1}]", n, r);

			return RenderAbstractType(at.Base, n);
		}

		public override string RenderIndirectType (IndirectType it)
		{
			if (it.Reference)
				throw new Exception();

			return RenderAbstractType(it.Base, 
				string.Format(it.Base is ArrayType || it.Base is FunctionType ? "(*{0}{1})" : "*{0}{1}", RenderAbstractTypeQualifiers(it, JoinDelimiter.Front), PrependedCurrentTypeValue));
		}

		public override string RenderVarArgType (VarArgType t)
		{
			return "...";
		}

		public override string RenderCompositeType (CompositeType ct)
		{
			string r = RenderAbstractTypeQualifiers(ct);

			r += (ct.Union ? "union" : "struct") + " " + ct.Name + NewLine +
				Indent + "{" + NewLine;

			Indentation++;

			r += Definitions.Join(ct.Field, new Stringer(IndentedElementStringer), NewLine);

			Indentation--;

			return r + NewLine + Indent + "}" + PrependedCurrentTypeValue;
		}

		public override string RenderComplexType (ComplexType t)
		{
			throw new Exception();
		}

		public override string RenderImaginaryType (ImaginaryType t)
		{
			throw new Exception();
		}

		public override string RenderRealType (RealType t)
		{
			throw new Exception();
		}

		public override string RenderIntegerType (IntegerType t)
		{
			throw new Exception();
		}

		public override string RenderBoolType (BoolType t)
		{
			throw new Exception();
		}

		public override string RenderVoidType (VoidType t)
		{
			throw new Exception();
		}

		public override string RenderAbstractPrimitiveType (AbstractPrimitiveType apt)
		{
			return string.Format("{0}{1}{2}", RenderAbstractTypeQualifiers(apt), Definitions.TypeToString(apt), PrependedCurrentTypeValue);
		}

		public override string RenderNamedType (NamedType nt)
		{
			return string.Format("{0}{1}{2}", RenderAbstractTypeQualifiers(nt), nt.Name, PrependedCurrentTypeValue);
		}

		protected string RenderAbstractTypeQualifiers (AbstractType t)
		{
			return RenderAbstractTypeQualifiers(t, JoinDelimiter.Back);
		}

		protected string RenderAbstractTypeQualifiers (AbstractType t, JoinDelimiter d)
		{
			ArrayList q = new ArrayList();

			if (t.Constant)
				q.Add("const");
			if (t.Volatile)
				q.Add("volatile");
			if (t.Restricted)
				q.Add("restricted");

			return Definitions.Join(q, " ", JoinDelimiter.Back);
		}

		protected Stack TypeValueStack = new Stack();

		protected string CurrentTypeValue
		{
			get
			{
				return (string)TypeValueStack.Peek();
			}
		}

		protected string PrependedCurrentTypeValue
		{
			get
			{
				string n = CurrentTypeValue;

				return n != null ? " " + n : string.Empty;
			}
		}

		protected string RenderAbstractType (AbstractType t, string n)
		{
			TypeValueStack.Push(n);

			string r = RenderAbstractType(t);

			TypeValueStack.Pop();

			return r;
		}

		public override string RenderDeclaration (Declaration d)
		{
			// ISO_CHECK - this is not very correct... bitfield + initialization !??

			string r = RenderAbstractType(d.Type, d.Name) + (d.Value != null ? " = " + RenderAbstractInitializer(d.Value) : string.Empty);

			if (d.Type is AbstractPrimitiveType)
			{
				AbstractPrimitiveType pt = (AbstractPrimitiveType)d.Type;

				if (pt.Precision < 0)
					r += string.Format(" : {0}", RenderAbstractExpression(pt.PrecisionExpression));
			}

			return RenderElementSpecifiers(d) + r;
		}

		public override string RenderTypeDefinition (TypeDefinition d)
		{
			return "typedef " + RenderAbstractType(d.Type, d.Name);
		}

		public override string RenderMethodDefinition (MethodDefinition d)
		{
			string r = RenderAbstractType(d.Type, d.Name) + NewLine;

			Indentation++;

			r += RenderAbstractStatement(d.Body);

			Indentation--;

			return RenderElementSpecifiers(d) +  r.Substring(0, r.Length - NewLine.Length);
		}

		public override string RenderCommentElement (CommentElement e)
		{
			return string.Format("{0}/*{1}*/", Indent, e.Content);
		}

		public override string RenderAbstractElement (AbstractElement e)
		{
			return base.RenderAbstractElement(e) + (e is Declaration || e is TypeDefinition ? ";" : string.Empty);
		}

		public override string RenderElements (Elements e)
		{
			return Definitions.Join(e.Element, new Stringer(IndentedElementStringer), NewLine + NewLine);		
		}

		protected string RenderElementSpecifiers (AbstractElement e)
		{
			if (e is Declaration)
			{
				Declaration d = (Declaration)e;

				if (d.Linkage != Linkage.None)
				{
					Debug.Assert(d.Storage == Storage.Static); // ISO_ERROR

					if (d.Linkage == Linkage.External)
						return "extern ";
					else if (d.Linkage == Linkage.Internal)
						return "static ";
					else
					{
						Debug.Assert(false, "invalid linkage");

						return null;
					}
				}
				else if (d.Storage == Storage.Register)
					return "register ";
				else if (d.Storage == Storage.Automatic)
					return string.Empty; // ISO_CHECK... when is ok to return "auto ";
				else
				{
					Debug.Assert(false, "static storage + no linkage prohibited"); // ISO_ERROR

					return null;
				}
			}
			else if (e is TypeDefinition)
				return "typedef ";
			else if (e is MethodDefinition)
			{
				MethodDefinition d = (MethodDefinition)e;

				if (d.Linkage == Linkage.External)
					return "extern ";
				else if (d.Linkage == Linkage.Internal)
					return "static "; // marques: i fixed bug, originally wrote "intern"
				else
					return string.Empty; // ISO_CHECK for return "auto ";
			}
			else
			{
				Debug.Assert(false, "invalid element");

				return null;
			}
		}

		// Stringers

		protected string InitializerStringer (object o)
		{
			return RenderAbstractInitializer((AbstractInitializer)o);
		}

		protected string IndentedInitializerStringer (object o)
		{
			return Indent + InitializerStringer(o);
		}

		protected string ExpressionStringer (object o)
		{
			return RenderAbstractExpression((AbstractExpression)o);
		}

		protected string SubstatementStringer (AbstractStatement s)
		{
			Indentation++;

			string r = RenderAbstractStatement(s);

			if (!(s is CompoundStatement))
				r = Indent + r;

			Indentation--;

			return r;
		}

		protected string IndentedStatementStringer (object o)
		{
			string r;

			if (o is LabeledStatement)
				r = RenderAbstractStatement((AbstractStatement)o);
			else if (o is CompoundStatement)
			{
				Indentation++;

				r = RenderAbstractStatement((AbstractStatement)o);

				Indentation--;
			}
			else if (o is AbstractStatement)
				r = Indent + RenderAbstractStatement((AbstractStatement)o);
			else
			{
				Debug.Assert(false, "invalid element");

				return null;
			}

			return r;
		}

		protected string IndentedEnumerationItemStringer (object o)
		{
			EnumerationItem ei = (EnumerationItem)o;

			return Indent + ei.Name + (ei.Value != null ? " = " + RenderAbstractExpression(ei.Value) : string.Empty);
		}

		protected string DeclarationStringer (object o)
		{
			return RenderDeclaration((Declaration)o);
		}

		protected string IndentedElementStringer (object o)
		{
			return Indent + RenderAbstractElement((AbstractElement)o);
		}
	}
}
