using System;

namespace Omega.AST
{
	public class Node
	{
		public static Node operator - (Node n)
		{
			Coefficients c = n.AsCoefficients;
			Coefficients r = new Coefficients();

			r.Constant = -c.Constant;

			foreach (Variable v in c.Variables)
				r[v] = -c[v];

			return r;
		}

		public static Node operator * (Node l, Node r)
		{
			Coefficient c1 = l.AsCoefficient;
			Coefficient c2 = r.AsCoefficient;

			if (!c1.IsConstant && !c2.IsConstant && !c1.Variable.Equals(c2.Variable))
				throw new InvalidOperationException();

			Variable v = null;

			if (!c1.IsConstant)
				v = c1.Variable;
			else if (!c2.IsConstant)
				v = c2.Variable;

			return new Coefficient(c1.Constant * c2.Constant, v);
		}

		public static Node operator + (Node l, Node r)
		{
			Coefficients c1 = l.AsCoefficients;
			Coefficients c2 = r.AsCoefficients;
			Coefficients c = new Coefficients();

			c.Constant = c1.Constant + c2.Constant;

			foreach (Variable v in c1.Variables)
				c[v] = (int)c1[v] + (int)c2[v];

			foreach (Variable v in c2.Variables)
				if (c1[v] == 0)
					c[v] = c2[v];

			return c;
		}

		public static Node operator - (Node l, Node r)
		{
			return l + (-r);
		}

		public static Node operator == (Node l, Node r)
		{
			return new Relation(l.AsCoefficients, r.AsCoefficients, false);
		}

		public static Node operator != (Node l, Node r)
		{
			return !(l == r);
		}

		public static Node operator >= (Node l, Node r)
		{
			return new Relation(l.AsCoefficients, r.AsCoefficients, true);
		}

		public static Node operator <= (Node l, Node r)
		{
			return r >= l;
		}

		public static Node operator > (Node l, Node r)
		{
			return !(r >= l);
		}

		public static Node operator < (Node l, Node r)
		{
			return !(l >= r);
		}

		public static Node operator ! (Node n)
		{
			return new Not((Expression)n);
		}

		public static Node operator | (Node l, Node r)
		{
			return new Or((Expression)l, (Expression)r);
		}

		public static Node operator & (Node l, Node r)
		{
			return new And((Expression)l, (Expression)r);
		}

		public override bool Equals (object o)
		{
			return base.Equals(o);
		}

		public override int GetHashCode ()
		{
			return base.GetHashCode();
		}

		public Coefficient AsCoefficient
		{
			get
			{
				if (this is Coefficient)
					return (Coefficient)this;
				else
				{
					if (this is Variable)
						return new Coefficient((Variable)this);
					else
						throw new InvalidCastException();
				}
			}
		}

		public Coefficients AsCoefficients
		{
			get
			{
				if (this is Coefficients)
					return (Coefficients)this;
				else
					return new Coefficients(this.AsCoefficient);
			}
		}

		public static implicit operator Node (int C)
		{
			return new Coefficient(C);
		}

		/*
		public static Node operator * (Node l, int r)
		{
			return l * new Coefficient(r);
		}

		public static Node operator * (int l, Node r)
		{
			return new Coefficient(l) * r;
		}

		public static Node operator + (Node l, int r)
		{
			return l + new Coefficient(r);
		}

		public static Node operator + (int l, Node r)
		{
			return new Coefficient(l) + r;
		}

		public static Node operator - (Node l, int r)
		{
			return l - new Coefficient(r);
		}

		public static Node operator - (int l, Node r)
		{
			return new Coefficient(l) - r;
		}

		public static Node operator == (Node l, int r)
		{
			return l == new Coefficient(r);
		}

		public static Node operator == (int l, Node r)
		{
			return new Coefficient(l) == r;
		}

		public static Node operator != (Node l, int r)
		{
			return l != new Coefficient(r);
		}

		public static Node operator != (int l, Node r)
		{
			return new Coefficient(l) != r;
		}

		public static Node operator >= (Node l, int r)
		{
			return l >= new Coefficient(r);
		}

		public static Node operator >= (int l, Node r)
		{
			return new Coefficient(l) >= r;
		}

		public static Node operator <= (Node l, int r)
		{
			return l <= new Coefficient(r);
		}

		public static Node operator <= (int l, Node r)
		{
			return new Coefficient(l) <= r;
		}

		public static Node operator > (Node l, int r)
		{
			return l > new Coefficient(r);
		}

		public static Node operator > (int l, Node r)
		{
			return new Coefficient(l) > r;
		}

		public static Node operator < (Node l, int r)
		{
			return l < new Coefficient(r);
		}

		public static Node operator < (int l, Node r)
		{
			return new Coefficient(l) < r;
		}
		*/
	}
}
