using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Unosquare.Swan.Abstractions {
  /// 
  /// Represents a generic expression parser.
  /// 
  public abstract class ExpressionParser {
    /// 
    /// Resolves the expression.
    /// 
    /// The type of expression result.
    /// The tokens.
    /// The representation of the expression parsed.
    public virtual T ResolveExpression(IEnumerable tokens) {
      UnaryExpression conversion = Expression.Convert(this.Parse(tokens), typeof(T));
      return Expression.Lambda>(conversion).Compile()();
    }
    /// 
    /// Parses the specified tokens.
    /// 
    /// The tokens.
    /// The final expression.
    public virtual Expression Parse(IEnumerable tokens) {
      List> expressionStack = new List>();
      foreach(Token token in tokens) {
        if(expressionStack.Any() == false) {
          expressionStack.Add(new Stack());
        }
        switch(token.Type) {
          case TokenType.Wall:
            expressionStack.Add(new Stack());
            break;
          case TokenType.Number:
            expressionStack.Last().Push(Expression.Constant(Convert.ToDecimal(token.Value)));
            break;
          case TokenType.Variable:
            this.ResolveVariable(token.Value, expressionStack.Last());
            break;
          case TokenType.String:
            expressionStack.Last().Push(Expression.Constant(token.Value));
            break;
          case TokenType.Operator:
            this.ResolveOperator(token.Value, expressionStack.Last());
            break;
          case TokenType.Function:
            this.ResolveFunction(token.Value, expressionStack.Last());
            if(expressionStack.Count > 1 && expressionStack.Last().Count == 1) {
              Expression lastValue = expressionStack.Last().Pop();
              _ = expressionStack.Remove(expressionStack.Last());
              expressionStack.Last().Push(lastValue);
            }
            break;
        }
      }
      return expressionStack.Last().Pop();
    }
    /// 
    /// Resolves the variable.
    /// 
    /// The value.
    /// The expression stack.
    public abstract void ResolveVariable(String value, Stack expressionStack);
    /// 
    /// Resolves the operator.
    /// 
    /// The value.
    /// The expression stack.
    public abstract void ResolveOperator(String value, Stack expressionStack);
    /// 
    /// Resolves the function.
    /// 
    /// The value.
    /// The expression stack.
    public abstract void ResolveFunction(String value, Stack expressionStack);
  }
}