// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision: 4482 $</version>
// </file>

using ICSharpCode.NRefactory.Visitors;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ICSharpCode.NRefactory.Ast;

namespace ICSharpCode.NRefactory.Parser.VB
{
	internal sealed partial class Parser : AbstractParser
	{
		Lexer lexer;
		
		public Parser(ILexer lexer) : base(lexer)
		{
			this.lexer = (Lexer)lexer;
		}
		
		private StringBuilder qualidentBuilder = new StringBuilder();

		Token t
		{
			[System.Diagnostics.DebuggerStepThrough]
			get {
				return lexer.Token;
			}
		}
		Token la
		{
			[System.Diagnostics.DebuggerStepThrough]
			get {
				return lexer.LookAhead;
			}
		}

		Token Peek (int n)
		{
			lexer.StartPeek();
			Token x = la;
			while (n > 0) {
				x = lexer.Peek();
				n--;
			}
			return x;
		}

		public void Error(string s)
		{
			if (errDist >= MinErrDist) {
				this.Errors.Error(la.line, la.col, s);
			}
			errDist = 0;
		}

		public override void Parse()
		{
			ParseRoot();
			compilationUnit.AcceptVisitor(new SetParentVisitor(), null);
		}
		
		public override TypeReference ParseTypeReference ()
		{
			// TODO
			return null;
		}
		
		public override Expression ParseExpression()
		{
			lexer.NextToken();
			Location startLocation = la.Location;
			Expression expr;
			Expr(out expr);
			while (la.kind == Tokens.EOL) lexer.NextToken();
			if (expr != null) {
				expr.StartLocation = startLocation;
				expr.EndLocation = t.EndLocation;
				expr.AcceptVisitor(new SetParentVisitor(), null);
			}
			Expect(Tokens.EOF);
			return expr;
		}
		
		public override BlockStatement ParseBlock()
		{
			lexer.NextToken();
			compilationUnit = new CompilationUnit();
			
			Location startLocation = la.Location;
			Statement st;
			Block(out st);
			if (st != null) {
				st.StartLocation = startLocation;
				if (t != null)
					st.EndLocation = t.EndLocation;
				else
					st.EndLocation = la.Location;
				st.AcceptVisitor(new SetParentVisitor(), null);
			}
			Expect(Tokens.EOF);
			return st as BlockStatement;
		}
		
		public override List<INode> ParseTypeMembers()
		{
			lexer.NextToken();
			compilationUnit = new CompilationUnit();
			
			TypeDeclaration newType = new TypeDeclaration(Modifiers.None, null);
			compilationUnit.BlockStart(newType);
			ClassBody(newType);
			compilationUnit.BlockEnd();
			Expect(Tokens.EOF);
			newType.AcceptVisitor(new SetParentVisitor(), null);
			return newType.Children;
		}

		bool LeaveBlock()
		{
			int peek = Peek(1).kind;
			return Tokens.BlockSucc[la.kind] && (la.kind != Tokens.End || peek == Tokens.EOL || peek == Tokens.Colon);
		}

		/* True, if "." is followed by an ident */
		bool DotAndIdentOrKw () {
			int peek = Peek(1).kind;
			return la.kind == Tokens.Dot && (peek == Tokens.Identifier || peek >= Tokens.AddHandler);
		}

		static bool IsIdentifierToken(Token tk)
		{
			return Tokens.IdentifierTokens[tk.kind] || tk.kind == Tokens.Identifier;
		}
		
		bool IsIdentifiedExpressionRange()
		{
			return la.kind == Tokens.As || la.kind == Tokens.Assign;
		}
		
		bool IsQueryExpression()
		{
			return (la.kind == Tokens.From || la.kind == Tokens.Aggregate) && IsIdentifierToken(Peek(1));
		}
		
		bool IsEndStmtAhead()
		{
			int peek = Peek(1).kind;
			return la.kind == Tokens.End && (peek == Tokens.EOL || peek == Tokens.Colon);
		}

		bool IsNotClosingParenthesis() {
			return la.kind != Tokens.CloseParenthesis;
		}

		/*
			True, if ident is followed by "=" or by ":" and "="
		 */
		bool IsNamedAssign() {
			if(Peek(1).kind == Tokens.Colon && Peek(2).kind == Tokens.Assign) return true;
			return false;
		}

		bool IsObjectCreation() {
			return la.kind == Tokens.As && Peek(1).kind == Tokens.New;
		}

		/*
			True, if "<" is followed by the ident "assembly" or "module"
		 */
		bool IsGlobalAttrTarget () {
			Token pt = Peek(1);
			return la.kind == Tokens.LessThan && ( string.Equals(pt.val, "assembly", StringComparison.InvariantCultureIgnoreCase) || string.Equals(pt.val, "module", StringComparison.InvariantCultureIgnoreCase));
		}

		/*
			True if the next token is a "(" and is followed by "," or ")"
		 */
		bool IsDims()
		{
			int peek = Peek(1).kind;
			return la.kind == Tokens.OpenParenthesis
				&& (peek == Tokens.Comma || peek == Tokens.CloseParenthesis);
		}
		
		/*
			True if the next token is an identifier
		 */
		bool IsLoopVariableDeclaration()
		{
			if (!IsIdentifierToken(la))
				return false;
			lexer.StartPeek();
			Token x = lexer.Peek();
			if (x.kind == Tokens.OpenParenthesis) {
				do {
					x = lexer.Peek();
				} while (x.kind == Tokens.Comma);
				if (x.kind != Tokens.CloseParenthesis)
					return false;
				x = lexer.Peek();
			}
			return x.kind == Tokens.As || x.kind == Tokens.Assign;
		}

		bool IsSize()
		{
			return la.kind == Tokens.OpenParenthesis;
		}

		/*
			True, if the comma is not a trailing one,
			like the last one in: a, b, c,
		 */
		bool NotFinalComma() {
			int peek = Peek(1).kind;
			return la.kind == Tokens.Comma &&
				peek != Tokens.CloseCurlyBrace;
		}

		/*
			True, if the next token is "Else" and this one
			if followed by "If"
		 */
		bool IsElseIf()
		{
			int peek = Peek(1).kind;
			return la.kind == Tokens.Else && peek == Tokens.If;
		}

		/*
	True if the next token is goto and this one is
	followed by minus ("-") (this is allowd in in
	error clauses)
		 */
		bool IsNegativeLabelName()
		{
			int peek = Peek(1).kind;
			return la.kind == Tokens.GoTo && peek == Tokens.Minus;
		}

		/*
	True if the next statement is a "Resume next" statement
		 */
		bool IsResumeNext()
		{
			int peek = Peek(1).kind;
			return la.kind == Tokens.Resume && peek == Tokens.Next;
		}

		/*
	True, if ident/literal integer is followed by ":"
		 */
		bool IsLabel()
		{
			return (la.kind == Tokens.Identifier || la.kind == Tokens.LiteralInteger)
				&& Peek(1).kind == Tokens.Colon;
		}

		bool IsNotStatementSeparator()
		{
			return la.kind == Tokens.Colon && Peek(1).kind == Tokens.EOL;
		}

		static bool IsMustOverride(ModifierList m)
		{
			return m.Contains(Modifiers.Abstract);
		}

		/* Writes the type name represented through the expression into the string builder. */
		/* Returns true when the expression was converted successfully, returns false when */
		/* There was an unknown expression (e.g. TypeReferenceExpression) in it */
		bool WriteFullTypeName(StringBuilder b, Expression expr)
		{
			MemberReferenceExpression fre = expr as MemberReferenceExpression;
			if (fre != null) {
				bool result = WriteFullTypeName(b, fre.TargetObject);
				if (b.Length > 0) b.Append('.');
				b.Append(fre.MemberName);
				return result;
			} else if (expr is IdentifierExpression) {
				b.Append(((IdentifierExpression)expr).Identifier);
				return true;
			} else {
				return false;
			}
		}

		/*
		True, if lookahead is a local attribute target specifier,
		i.e. one of "event", "return", "field", "method",
		"module", "param", "property", or "type"
		 */
		bool IsLocalAttrTarget() {
			// TODO
			return false;
		}
		
		void EnsureIsZero(Expression expr)
		{
			if (!(expr is PrimitiveExpression) || (expr as PrimitiveExpression).StringValue != "0")
				Error("lower bound of array must be zero");
		}
		
		/// <summary>
		/// Adds a child item to a collection stored in the parent node.
		/// Also set's the item's parent to <paramref name="parent"/>.
		/// Does nothing if item is null.
		/// </summary>
		static void SafeAdd<T>(INode parent, List<T> list, T item) where T : class, INode
		{
			Debug.Assert(parent != null);
			Debug.Assert((parent is INullable) ? !(parent as INullable).IsNull : true);
			if (item != null) {
				list.Add(item);
				item.Parent = parent;
			}
		}
	}
}
