using System;
using System.Collections;
using System.Collections.Generic;



namespace FormulaEditor
{

	/// <summary>
	/// Operation of elementary functions
	/// </summary>
	public class ElementaryFunctionOperation : IObjectOperation, IOperationAcceptor, IDerivationOperation,
		IFormulaCreatorOperation
	{
		
		/// <summary>
		/// The initialization of elementary functions derivations
		/// </summary>
		private static readonly string[] formStrings = {"c4[]x1[]",
														   "-3s4[]x1[]",
														   "F2[15[]][x1[]]",
														   "Q2[15[]-3x1[25[]]][]",
														   "-3Q2[15[]-3x1[25[]]][]",
														   "e4[]x1[]",
														   "F2[15[]][c4[25[]]x1[]]",
														   "F2[-315[]][s4[25[]]x1[]]",
														   "F2[15[]][15[]+3x1[25[]]]",
														   "F2[-315[]][15[]+3x1[25[]]]",
														   "F2[-3s4[]x1[]][c4[25[]]x1[]]",
														   "F2[c4[]x1[]][s4[25[]]x1[]]",
														   "F2[05[].355[]][Q2[x1[]][]]",
														   "-3x1[]"};
		
		/// <summary>
		/// Derivations of elementary functions
		/// </summary>
		private static ObjectFormulaTree[] functionDerivations;

		static private readonly int[] MAX_SIZES = new int[20];



		/// <summary>
		/// Type of operation
		/// </summary>
		private const Double a = 0;

		/// <summary>
		/// Operation symbol
		/// </summary>
		private char symbol;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="symbol"></param>
		public ElementaryFunctionOperation(char symbol)
		{
			this.symbol = symbol;
		}

		/// <summary>
		/// Initialization of derivation calculation functions
		/// </summary>
		public static void InitDeri()
		{
			functionDerivations = new ObjectFormulaTree[formStrings.Length];
			for (int i = 0; i < formStrings.Length; i++)
			{
				MathFormula f = MathFormula.FromString(MAX_SIZES, formStrings[i]);
				functionDerivations[i] = new ObjectFormulaTree(f.FullTransform);
			}
		}

		/// <summary>
		/// Creates formula
		/// </summary>
		/// <param name="tree">Operation tree</param>
		/// <param name="level">Formula level</param>
		/// <param name="sizes">Sizes of symbols</param>
		/// <returns>The formula</returns>
		public MathFormula CreateFormula(ObjectFormulaTree tree, byte level, int[] sizes)
		{
			MathFormula form = new MathFormula(level, sizes);
			MathFormula f = new MathFormula(level, sizes);
			MathSymbol sym = null;
			if (symbol == '-')
			{
				sym = new BinarySymbol('-');
			}
			else
			{
				sym = new SimpleSymbol(symbol, false, (byte)FormulaConstants.Unary);
			}
			sym.Append(form);
			sym = new BracketsSymbol();
			sym.Append(form);
			form.Last.Children[0] = FormulaCreator.CreateFormula(tree[0], level, sizes);
			form.Last.Children[1] = new MathFormula((byte)(level + 1), sizes);
			return form;
		}

		/// <summary>
		/// Operation priority
		/// </summary>
		public int OperationPriority
		{
			get
			{
				return (int)ElementaryOperationPriorities.Function;
			}
		}


		/// <summary>
		/// Calculates derivation
		/// </summary>
		/// <param name="tree">The function for derivation calculation</param>
		/// <param name="s">Derivation string</param>
		/// <returns>The derivation</returns>
		public ObjectFormulaTree Derivation(ObjectFormulaTree tree, string s)
		{
			int i = 0;
			if (symbol == '-')
			{
				i = functionDerivations.Length - 1;
			}
			else
			{
				i = MathSymbol.FUNCTIONS.IndexOf(symbol);
			}
			return derivation(tree, i, s);
		}

		/// <summary>
		/// Arity of this operation
		/// </summary>
		public int Arity
		{
			get
			{
				return 1;
			}
		}

		/// <summary>
		/// Calculates result of this operation
		/// </summary>
		public object this[object[] x]
		{
			get
			{
				double y = (double)x[0];
				return unaryValue(symbol, y);
			}
		}

		/// <summary>
		/// Return type
		/// </summary>
		public object ReturnType
		{
			get
			{
				return a;
			}
		}

		/// <summary>
		/// Accepts operation
		/// </summary>
		/// <param name="type">Argument type</param>
		/// <returns>The operation</returns>
		public IObjectOperation Accept(object type)
		{
			if (!(type is Double))
			{
				return null;
			}
			return this;
		}

		/// <summary>
		/// The "is powered" sign
		/// </summary>
		public bool IsPowered
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Derivation of square root
		/// </summary>
		/// <param name="tree">Prototype tree</param>
		/// <param name="s">Derivation variable</param>
		/// <returns>The derivation</returns>
		public static ObjectFormulaTree SquareRootDerivation(ObjectFormulaTree tree, string s)
		{
			return derivation(tree, formStrings.Length - 2, s);
		}


		/// <summary>
		/// Calculates derivation of i - th function
		/// </summary>
		/// <param name="tree">Object tree</param>
		/// <param name="i">Function number</param>
		/// <param name="s">Derivation variable</param>
		/// <returns>The derivation</returns>
		static private ObjectFormulaTree derivation(ObjectFormulaTree tree, int i, string s)
		{
			Double a = 0;
			ObjectFormulaTree fd = functionDerivations[i];
			ObjectFormulaTree f0 = insertVariable(fd, tree[0]);
			ObjectFormulaTree f1 = DerivationPerformer.Derivation(tree[0], s);
			IObjectOperation operation = new ElementaryBinaryOperation('*', a);
			List<ObjectFormulaTree> children = new List<ObjectFormulaTree>();
			children.Add(f0);
			children.Add(f1);
			return new ObjectFormulaTree(operation, children);

		}

		/// <summary>
		/// Calculates value of ementary function
		/// </summary>
		/// <param name="s">the symbol of function</param>
		/// <param name="x">the argument</param>
		/// <returns>The value</returns>
		private double unaryValue(char s, double x)
		{
			switch (s)
			{
				case 's':
					return Math.Sin(x);
				case 'c':
					return Math.Cos(x);
				case 'l':
					return Math.Log(x);
				case 'e':
					return Math.Exp(x);
				case 't':
					return Math.Tan(x);
				case 'q':
					return Math.Cos(x) / Math.Sin(x);
				case 'a':
					return Math.Atan(x);
				case 'b':
					return Math.PI / 2 - Math.Atan(x);
				case 'j':
					return 1 / Math.Cos(x);
				case 'k':
					return 1 / Math.Sin(x);
				case 'f':
					return Math.Asin(x);
				case 'g':
					return Math.Acos(x);
				case '?':
					return x;
				case '-':
					return -x;
			}
			return 0;
		}

		/// <summary>
		/// Opreation symbol
		/// </summary>
		internal char Symbol
		{
			get
			{
				return symbol;
			}
		}

		/// <summary>
		/// Inserts formula instead variable
		/// </summary>
		/// <param name="tree">Prototype</param>
		/// <param name="insert">Inerted formula</param>
		/// <returns>Result of the operation</returns>
		private static ObjectFormulaTree insertVariable(ObjectFormulaTree tree, ObjectFormulaTree insert)
		{
			if (tree.Operation is ElementaryRealVariable)
			{
				return insert.Clone() as ObjectFormulaTree;
			}
			List<ObjectFormulaTree> children = new List<ObjectFormulaTree>();
			for (int i = 0; i < tree.Count; i++)
			{
				ObjectFormulaTree t = tree[i].Clone() as ObjectFormulaTree;
				children.Add(insertVariable(t, insert));
			}
			return new ObjectFormulaTree(tree.Operation, children);
		}

	}

	/// <summary>
	/// Priorities of operations
	/// </summary>
	public enum ElementaryOperationPriorities
	{
		Optional, LogicalOR, LogicalAND, Comparation, IntegerOR, IntegerAND, IntegerBitwiseOR
		, IntegerShift, PlusMinus, Multiply, Function, Power, Variable, Brackets
	}


}


