using System;

using StringBuilder			= System.Text.StringBuilder;

using BitSet				= Spring.Expressions.Parser.antlr.collections.impl.BitSet;
using AST					= Spring.Expressions.Parser.antlr.collections.AST;

namespace Spring.Expressions.Parser.antlr
{
	/*ANTLR Translator Generator
	* Project led by Terence Parr at http://www.jGuru.com
	* Software rights: http://www.antlr.org/license.html
	*
	* $Id:$
	*/

	//
	// ANTLR C# Code Generator by Micheal Jordan
	//                            Kunle Odutola       : kunle UNDERSCORE odutola AT hotmail DOT com
	//                            Anthony Oguntimehin
	//
	// With many thanks to Eric V. Smith from the ANTLR list.
	//
	
	[Serializable]
	public class MismatchedTokenException : RecognitionException
	{
		// Token names array for formatting
		internal string[] tokenNames;
		// The token that was encountered
		public IToken token;
		// The offending AST node if tree walking
		public AST node;
		
		internal string tokenText = null; // taken from node or token object
		
		// Types of tokens
		public enum TokenTypeEnum
		{
			TokenType = 1,
			NotTokenType = 2,
			RangeType = 3,
			NotRangeType = 4,
			SetType = 5,
			NotSetType = 6
		}
		// One of the above
		public TokenTypeEnum mismatchType;
		
		// For TOKEN/NOT_TOKEN and RANGE/NOT_RANGE
		public int expecting;
		
		// For RANGE/NOT_RANGE (expecting is lower bound of range)
		public int upper;
		
		// For SET/NOT_SET
		public BitSet bset;
		
		/*Looking for AST wildcard, didn't find it */
		public MismatchedTokenException() : base("Mismatched Token: expecting any AST node", "<AST>", - 1, - 1)
		{
		}
		
		// Expected range / not range
		public MismatchedTokenException(string[] tokenNames_, AST node_, int lower, int upper_, bool matchNot) : 
					base("Mismatched Token", "<AST>", - 1, - 1)
		{
			tokenNames = tokenNames_;
			node = node_;
			if (node_ == null)
			{
				tokenText = "<empty tree>";
			}
			else
			{
				tokenText = node_.ToString();
			}
			mismatchType = matchNot ? TokenTypeEnum.NotRangeType : TokenTypeEnum.RangeType;
			expecting = lower;
			upper = upper_;
		}
		
		// Expected token / not token
		public MismatchedTokenException(string[] tokenNames_, AST node_, int expecting_, bool matchNot) :
					base("Mismatched Token", "<AST>", - 1, - 1)
		{
			tokenNames = tokenNames_;
			node = node_;
			if (node_ == null)
			{
				tokenText = "<empty tree>";
			}
			else
			{
				tokenText = node_.ToString();
			}
			mismatchType = matchNot ? TokenTypeEnum.NotTokenType : TokenTypeEnum.TokenType;
			expecting = expecting_;
		}
		
		// Expected BitSet / not BitSet
		public MismatchedTokenException(string[] tokenNames_, AST node_, BitSet set_, bool matchNot) :
					base("Mismatched Token", "<AST>", - 1, - 1)
		{
			tokenNames = tokenNames_;
			node = node_;
			if (node_ == null)
			{
				tokenText = "<empty tree>";
			}
			else
			{
				tokenText = node_.ToString();
			}
			mismatchType = matchNot ? TokenTypeEnum.NotSetType : TokenTypeEnum.SetType;
			bset = set_;
		}
		
		// Expected range / not range
		public MismatchedTokenException(string[] tokenNames_, IToken token_, int lower, int upper_, bool matchNot, string fileName_) : 
					base("Mismatched Token", fileName_, token_.getLine(), token_.getColumn())
		{
			tokenNames = tokenNames_;
			token = token_;
			tokenText = token_.getText();
			mismatchType = matchNot ? TokenTypeEnum.NotRangeType : TokenTypeEnum.RangeType;
			expecting = lower;
			upper = upper_;
		}
		
		// Expected token / not token
		public MismatchedTokenException(string[] tokenNames_, IToken token_, int expecting_, bool matchNot, string fileName_) :
					base("Mismatched Token", fileName_, token_.getLine(), token_.getColumn())
		{
			tokenNames = tokenNames_;
			token = token_;
			tokenText = token_.getText();
			mismatchType = matchNot ? TokenTypeEnum.NotTokenType : TokenTypeEnum.TokenType;
			expecting = expecting_;
		}
		
		// Expected BitSet / not BitSet
		public MismatchedTokenException(string[] tokenNames_, IToken token_, BitSet set_, bool matchNot, string fileName_) :
					base("Mismatched Token", fileName_, token_.getLine(), token_.getColumn())
		{
			tokenNames = tokenNames_;
			token = token_;
			tokenText = token_.getText();
			mismatchType = matchNot ? TokenTypeEnum.NotSetType : TokenTypeEnum.SetType;
			bset = set_;
		}
		
		/*
		* Returns a clean error message (no line number/column information)
		*/
		override public string Message
		{
			get 
			{
				StringBuilder sb = new StringBuilder();
			
				switch (mismatchType)
				{
					case TokenTypeEnum.TokenType: 
						sb.Append("expecting " + tokenName(expecting) + ", found '" + tokenText + "'");
						break;
				
					case TokenTypeEnum.NotTokenType: 
						sb.Append("expecting anything but " + tokenName(expecting) + "; got it anyway");
						break;
				
					case TokenTypeEnum.RangeType: 
						sb.Append("expecting token in range: " + tokenName(expecting) + ".." + tokenName(upper) + ", found '" + tokenText + "'");
						break;
				
					case TokenTypeEnum.NotRangeType: 
						sb.Append("expecting token NOT in range: " + tokenName(expecting) + ".." + tokenName(upper) + ", found '" + tokenText + "'");
						break;
				
					case TokenTypeEnum.SetType: case TokenTypeEnum.NotSetType: 
						sb.Append("expecting " + (mismatchType == TokenTypeEnum.NotSetType ? "NOT " : "") + "one of (");
						int[] elems = bset.toArray();
						for (int i = 0; i < elems.Length; i++)
						{
							sb.Append(" ");
							sb.Append(tokenName(elems[i]));
						}
						sb.Append("), found '" + tokenText + "'");
						break;
				
					default: 
						sb.Append(base.Message);
						break;				
				}			
				return sb.ToString();
			}
		}
		
		private string tokenName(int tokenType)
		{
			if (tokenType == Token.INVALID_TYPE)
			{
				return "<Set of tokens>";
			}
			else if (tokenType < 0 || tokenType >= tokenNames.Length)
			{
				return "<" + tokenType.ToString() + ">";
			}
			else
			{
				return tokenNames[tokenType];
			}
		}
	}
}