// 
// OperatorDeclaration.cs
//
// Author:
//       Mike Krüger <mkrueger@novell.com>
// 
// Copyright (c) 2009 Novell, Inc (http://www.novell.com)
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.ComponentModel;
using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.Decompiler.CSharp.Syntax
{
	public enum OperatorType
	{
		// Unary operators
		LogicalNot,
		OnesComplement,
		Increment,
		Decrement,
		True,
		False,

		// Unary and Binary operators
		Addition,
		Subtraction,

		UnaryPlus,
		UnaryNegation,
		
		// Binary operators
		Multiply,
		Division,
		Modulus,
		BitwiseAnd,
		BitwiseOr,
		ExclusiveOr,
		LeftShift,
		RightShift,
		Equality,
		Inequality,
		GreaterThan,
		LessThan,
		GreaterThanOrEqual,
		LessThanOrEqual,

		// Implicit and Explicit
		Implicit,
		Explicit
	}
	
	public class OperatorDeclaration : EntityDeclaration
	{
		public static readonly TokenRole OperatorKeywordRole = new TokenRole ("operator");
		
		// Unary operators
		public static readonly TokenRole LogicalNotRole = new TokenRole ("!");
		public static readonly TokenRole OnesComplementRole = new TokenRole ("~");
		public static readonly TokenRole IncrementRole = new TokenRole ("++");
		public static readonly TokenRole DecrementRole = new TokenRole ("--");
		public static readonly TokenRole TrueRole = new TokenRole ("true");
		public static readonly TokenRole FalseRole = new TokenRole ("false");

		// Unary and Binary operators
		public static readonly TokenRole AdditionRole = new TokenRole ("+");
		public static readonly TokenRole SubtractionRole = new TokenRole ("-");

		// Binary operators
		public static readonly TokenRole MultiplyRole = new TokenRole ("*");
		public static readonly TokenRole DivisionRole = new TokenRole ("/");
		public static readonly TokenRole ModulusRole = new TokenRole ("%");
		public static readonly TokenRole BitwiseAndRole = new TokenRole ("&");
		public static readonly TokenRole BitwiseOrRole = new TokenRole ("|");
		public static readonly TokenRole ExclusiveOrRole = new TokenRole ("^");
		public static readonly TokenRole LeftShiftRole = new TokenRole ("<<");
		public static readonly TokenRole RightShiftRole = new TokenRole (">>");
		public static readonly TokenRole EqualityRole = new TokenRole ("==");
		public static readonly TokenRole InequalityRole = new TokenRole ("!=");
		public static readonly TokenRole GreaterThanRole = new TokenRole (">");
		public static readonly TokenRole LessThanRole = new TokenRole ("<");
		public static readonly TokenRole GreaterThanOrEqualRole = new TokenRole (">=");
		public static readonly TokenRole LessThanOrEqualRole = new TokenRole ("<=");
		
		public static readonly TokenRole ExplicitRole = new TokenRole ("explicit");
		public static readonly TokenRole ImplicitRole = new TokenRole ("implicit");

		static readonly string[][] names;

		static OperatorDeclaration()
		{
			names = new string[(int)OperatorType.Explicit + 1][];
			names[(int)OperatorType.LogicalNot] = new string[] { "!", "op_LogicalNot" };
			names[(int)OperatorType.OnesComplement] = new string[] { "~", "op_OnesComplement" };
			names[(int)OperatorType.Increment] = new string[] { "++", "op_Increment" };
			names[(int)OperatorType.Decrement] = new string[] { "--", "op_Decrement" };
			names[(int)OperatorType.True] = new string[] { "true", "op_True" };
			names[(int)OperatorType.False] = new string[] { "false", "op_False" };
			names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" };
			names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" };
			names[(int)OperatorType.UnaryPlus] = new string[] { "+", "op_UnaryPlus" };
			names[(int)OperatorType.UnaryNegation] = new string[] { "-", "op_UnaryNegation" };
			names[(int)OperatorType.Multiply] = new string[] { "*", "op_Multiply" };
			names[(int)OperatorType.Division] = new string[] { "/", "op_Division" };
			names[(int)OperatorType.Modulus] = new string[] { "%", "op_Modulus" };
			names[(int)OperatorType.BitwiseAnd] = new string[] { "&", "op_BitwiseAnd" };
			names[(int)OperatorType.BitwiseOr] = new string[] { "|", "op_BitwiseOr" };
			names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" };
			names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" };
			names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" };
			names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" };
			names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" };
			names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" };
			names[(int)OperatorType.LessThan] = new string[] { "<", "op_LessThan" };
			names[(int)OperatorType.GreaterThanOrEqual] = new string[] { ">=", "op_GreaterThanOrEqual" };
			names[(int)OperatorType.LessThanOrEqual] = new string[] { "<=", "op_LessThanOrEqual" };
			names[(int)OperatorType.Implicit] = new string[] { "implicit", "op_Implicit" };
			names[(int)OperatorType.Explicit] = new string[] { "explicit", "op_Explicit" };
		}

		public override SymbolKind SymbolKind {
			get { return SymbolKind.Operator; }
		}
		
		OperatorType operatorType;
		
		public OperatorType OperatorType {
			get { return operatorType; }
			set {
				ThrowIfFrozen();
				operatorType = value;
			}
		}
		
		public CSharpTokenNode OperatorToken {
			get { return GetChildByRole (OperatorKeywordRole); }
		}
		
		public CSharpTokenNode OperatorTypeToken {
			get { return GetChildByRole (GetRole (OperatorType)); }
		}
		
		public CSharpTokenNode LParToken {
			get { return GetChildByRole (Roles.LPar); }
		}
		
		public AstNodeCollection<ParameterDeclaration> Parameters {
			get { return GetChildrenByRole (Roles.Parameter); }
		}
		
		public CSharpTokenNode RParToken {
			get { return GetChildByRole (Roles.RPar); }
		}
		
		public BlockStatement Body {
			get { return GetChildByRole (Roles.Body); }
			set { SetChildByRole (Roles.Body, value); }
		}
		
		/// <summary>
		/// Gets the operator type from the method name, or null, if the method does not represent one of the known operator types.
		/// </summary>
		public static OperatorType? GetOperatorType(string methodName)
		{
			for (int i = 0; i < names.Length; ++i) {
				if (names[i][1] == methodName)
					return (OperatorType)i;
			}

			return null;
		}
		
		public static TokenRole GetRole (OperatorType type)
		{
			switch (type) {
			case OperatorType.LogicalNot:
				return LogicalNotRole;
			case OperatorType.OnesComplement:
				return OnesComplementRole;
			case OperatorType.Increment:
				return IncrementRole;
			case OperatorType.Decrement:
				return DecrementRole;
			case OperatorType.True:
				return TrueRole;
			case OperatorType.False:
				return FalseRole;
			
			case OperatorType.Addition:
			case OperatorType.UnaryPlus:
				return AdditionRole;
			case OperatorType.Subtraction:
			case OperatorType.UnaryNegation:
				return SubtractionRole;
			
			case OperatorType.Multiply:
				return MultiplyRole;
			case OperatorType.Division:
				return DivisionRole;
			case OperatorType.Modulus:
				return ModulusRole;
			case OperatorType.BitwiseAnd:
				return BitwiseAndRole;
			case OperatorType.BitwiseOr:
				return BitwiseOrRole;
			case OperatorType.ExclusiveOr:
				return ExclusiveOrRole;
			case OperatorType.LeftShift:
				return LeftShiftRole;
			case OperatorType.RightShift:
				return RightShiftRole;
			case OperatorType.Equality:
				return EqualityRole;
			case OperatorType.Inequality:
				return InequalityRole;
			case OperatorType.GreaterThan:
				return GreaterThanRole;
			case OperatorType.LessThan:
				return LessThanRole;
			case OperatorType.GreaterThanOrEqual:
				return GreaterThanOrEqualRole;
			case OperatorType.LessThanOrEqual:
				return LessThanOrEqualRole;
			
			case OperatorType.Implicit:
				return ImplicitRole;
			case OperatorType.Explicit:
				return ExplicitRole;
			
			default:
				throw new System.ArgumentOutOfRangeException ();
			}
		}
		
		/// <summary>
		/// Gets the method name for the operator type. ("op_Addition", "op_Implicit", etc.)
		/// </summary>
		public static string GetName (OperatorType? type)
		{
			if (type == null)
				return null;
			return names[(int)type][1];
		}
		
		/// <summary>
		/// Gets the token for the operator type ("+", "implicit", etc.)
		/// </summary>
		public static string GetToken (OperatorType type)
		{
			return names[(int)type][0];
		}
		
		public override void AcceptVisitor (IAstVisitor visitor)
		{
			visitor.VisitOperatorDeclaration (this);
		}
			
		public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
		{
			return visitor.VisitOperatorDeclaration (this);
		}
		
		public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
		{
			return visitor.VisitOperatorDeclaration (this, data);
		}
		
		public override string Name {
			get { return GetName (this.OperatorType); }
			set { throw new NotSupportedException(); }
		}
		
		[EditorBrowsable(EditorBrowsableState.Never)]
		public override Identifier NameToken {
			get { return Identifier.Null; }
			set { throw new NotSupportedException(); }
		}
		
		protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
		{
			OperatorDeclaration o = other as OperatorDeclaration;
			return o != null && this.MatchAttributesAndModifiers (o, match) && this.OperatorType == o.OperatorType
				&& this.ReturnType.DoMatch (o.ReturnType, match)
				&& this.Parameters.DoMatch (o.Parameters, match) && this.Body.DoMatch (o.Body, match);
		}
	}
}
