
export const Expression = function(_str, _flag = 0, _placeholderValueMap = {})
{
  this.pos = 0; // 当前解析表达式字符串的位置
  this.str = _str; // 表达式
  this.errno = Expression.NO_ERROR; // 当前错误码
  this.placeholderValueMap = _placeholderValueMap; // 占位符值字典
  this.flag = _flag | Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION; // 行为标记
  this.placeholders = [];
}

Expression.NO_ERROR    = 0;
Expression.ERROR       = 1;
Expression.NULL_STRING   = 2;
Expression.BY_ZERO              = 3;
Expression.MISSING_DIGIT        = 4;
Expression.MISSING_RIGHT_PARENT = 5;
Expression.MISSING_OPERATOR    = 6;
Expression.INVALID_CHARACTER   = 7;
Expression.MISSING_PLACEHOLDER = 8;
Expression.INVALID_DIGIT                 = 9;
Expression.INVALID_PLACEHOLDER           = 10;
Expression.MISSING_RIGHT_PLACEHOLDER_END = 11;
Expression.INVALID_PLACEHOLDER_FUNCTION  = 12;
Expression.MISSING_COMPARE_OPERATOR  = 13;
Expression.INVALID_COMPARE_OPERATOR           = 14;
Expression.INCOMPLETE   = 15;
Expression.ErrorStr                      = [
  "",
  "错误",
  "空字符串",
  "除0",
  "缺失数字",
  "缺失右括号",
  "缺失运算符",
  "无效字符",
  "缺失占位符",
  "无效数字",
  "无效占位符",
  "缺失占位符结尾标识",
  "无效占位符内置函数",
  "缺失比较运算符",
  "无效比较运算符",
  "表达式不完整",
];

Expression.PARSE_FLAG_NONE = 0;
Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_RETURN_ZERO = 1; // 解析到错误则当前部分返回0
Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION = 1 << 1; // 解析到错误则抛出异常
Expression.PARSE_FLAG_ALL_NUMBER_AS_PLACEHOLDER = 1 << 3; // 所有数字都作为占位符
Expression.PARSE_FLAG_IGNORE_ERROR_MISSING_PLACEHOLDER = 1 << 4; // 忽略占位符缺失
Expression.PARSE_FLAG_IGNORE_ERROR_BY_ZERO = 1 << 5; // 忽略除0

// 是否读取到字符串结尾
Expression.prototype.__EOF = function()
{
  return !this.str || this.pos >= this.str.length;
};

// 字符串转为数字
Expression.prototype.__StrTo = function(numStr)
{
  return Number(numStr)
};

// 开始解析, 重置状态
Expression.prototype.__Ready = function()
{
  this.pos = 0;
  this.errno = Expression.NO_ERROR;
  this.placeholders = [];
  let err = this.__CheckStr();
  return (err === Expression.NO_ERROR);
};

// 解析 值 括号 占位符 的优先级最高的表达式
Expression.prototype.__EvalV = function()
{
  if(this.__EOF())
  {
    return this.__FatalError(Expression.INCOMPLETE);
  }
  this.__SkipBlank();
  let nStr = this.str.substring(this.pos);
  let p = 0;
  let nag = false;
  let result = 0;
  if(nStr.charAt(p) === '-')
  {
    nag = true;
    this.pos++;
    p++;
  }
  p += this.__SkipBlank();
  if(nStr.charAt(p) === '(') // 括号表达式
  {
    p++;
    this.pos++;
    result = this.__EvalP();
    if(this.str.charAt(this.pos) !== ')')
    {
      return this.__FatalError(Expression.MISSING_RIGHT_PARENT);
    }
    this.pos++;
  }
  else if(nStr.charAt(p) === '{') // 占位符
  {
    p++;
    this.pos++;
    p += this.__SkipBlank();
    let d = '';
    let size = 0;
    let ch = nStr.charAt(p);
    while("`~@#%^&*()-+=,/?:;\"'|\\[{]}".indexOf(ch) === -1) // 中文支持
    {
      size++;
      d += ch;
      p++;
      this.pos++;
      if(p >= nStr.length)
      {
        return this.__FatalError(Expression.MISSING_RIGHT_PLACEHOLDER_END);
      }
      ch = nStr.charAt(p);
    }
    p += this.__SkipBlank();
    if(this.str.charAt(this.pos) !== '}')
    {
      return this.__FatalError(Expression.MISSING_RIGHT_PLACEHOLDER_END);
    }
    this.pos++;
    if(size > 0)
    {
      result = this.__ParsePlaceholder(d);
    }
    else
    {
      return this.__NormalError(Expression.MISSING_PLACEHOLDER);
    }
  }
  else // 纯数字
  {
    let d = '';
    let size = 0;
    let ch = nStr.charAt(p);
    while((ch >= '0' && ch <= '9') || ch === '.')
    {
      size++;
      d += ch;
      p++;
      this.pos++;
      if(p >= nStr.length)
        break;
      ch = nStr.charAt(p);
    }
    if(size > 0)
    {
      let m = d.indexOf(".");
      if(m !== -1)
      {
        let n = d.lastIndexOf(".");
        if(n !== -1 && m !== n)
        {
          return this.__FatalError(Expression.INVALID_DIGIT);
        }
        if(m === 0 || m === d.length - 1)
          d = d.substr(0, m) + d.substr(m + 1);
      }
      if(this.flag & Expression.PARSE_FLAG_ALL_NUMBER_AS_PLACEHOLDER)
        result = this.__ParsePlaceholder(d);
      else
        result = this.__StrTo(d);
    }
    else
    {
      return this.__FatalError(Expression.MISSING_DIGIT);
    }
  }
  if(nag)
    result = -result;
  return result;
};

// 解析* /的优先级相对较高的表达式
Expression.prototype.__EvalM = function()
{
  if(this.__EOF())
  {
    return this.__FatalError(Expression.INCOMPLETE);
  }
  let first = this.__EvalV();
  let result = first;
  this.__SkipBlank();
  let nStr = this.str.substring(this.pos);
  while(nStr.length > 0)
  {
    let ch = nStr.charAt(0);
    if(ch !== '*' && ch !== '/')
      return result;
    else
    {
      this.pos++;
      let second = this.__EvalV();
      if(ch === '*')
        result = result *second;
      else
      {
        if(second === 0)
        {
          return this.__NormalError(Expression.BY_ZERO);
        }
        result = result / second;
      }
    }
    nStr = this.str.substring(this.pos);
  }
  return result;
};

// 解析+ -的优先级最低的表达式
Expression.prototype.__EvalP = function()
{
  if(this.__EOF())
  {
    return this.__FatalError(Expression.INCOMPLETE);
  }
  this.__SkipBlank();
  let first = this.__EvalM();
  let result = first;
  let nStr = this.str.substring(this.pos);
  while(nStr.length > 0)
  {
    let ch = nStr.charAt(0);
    if(ch !== '+' && ch !== '-')
      return result;
    else
    {
      this.pos++;
      let second = this.__EvalM();
      if(ch === '+')
        result = result + second;
      else
        result = result - second;
    }
    nStr = this.str.substring(this.pos);
  }
  return result;
};

// 解析比较运算符
Expression.prototype.__EvalCmp = function()
{
  if(this.__EOF())
  {
    return this.__FatalError(Expression.MISSING_COMPARE_OPERATOR);
  }
  this.__SkipBlank();
  let result = '';
  let nStr = this.str.substring(this.pos);
  while(nStr.length > 0)
  {
    let ch = nStr.charAt(0);
    if(ch !== '=' && ch !== '!' && ch !== '>' && ch !== '<')
      break;
    else
    {
      if(result.length === 0)
      {
        result += ch;
        this.pos++;
      }
      else // === 1
      {
        if(result !== "=") // 如果第一个字符不是=, 第二个字符必定是=
        {
          if(ch !== '=')
            return this.__FatalError(Expression.INVALID_COMPARE_OPERATOR);
        }
        else // 如果第一个字符是=, 第二个字符可以是=
        {
          if(ch === '=')
            result += ch;
          else
          {
            break;
          }
        }
        result += ch;
        this.pos++;
      }
    }
    if(result.length >= 2)
      break;
    nStr = this.str.substring(this.pos);
  }

  if(result === "=")
    result = "==";
  return result;
};

// 开始计算
Expression.prototype.Calc = function()
{
  if(!this.__Ready())
    return this.__OnError(0);
  return this.__EvalP();
};

// 判断表达式断言
Expression.prototype.Predicate = function()
{
  if(!this.__Ready())
    return this.__OnError(false);
  let a = this.__EvalP();
  if(this.HasError())
    return this.__OnError(false);
  let op = this.__EvalCmp();
  if(this.HasError())
    return this.__OnError(false);
  let b = this.__EvalP();
  if(this.HasError())
    return this.__OnError(false);
  //console.log(a + op + b);
  return eval(a + op + b);
};

Expression.prototype.FLAG = function(flags)
{
  for(let i of arguments)
  {
    if((this.flag & i) === i)
      return true;
  }
  return false;
}

// 普通错误, 仅设置可允许忽略的错误时调用, 错误被忽略时不设置错误码
Expression.prototype.__NormalError = function(err)
{
  switch(err)
  {
    case Expression.BY_ZERO:
      if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_BY_ZERO))
        return 0;
      break;
    case Expression.MISSING_PLACEHOLDER:
      if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_MISSING_PLACEHOLDER))
        return 0;
      break;
    case Expression.INVALID_DIGIT:
      if(this.FLAG(Expression.PARSE_FLAG_IGNORE_ERROR_INVALID_DIGIT))
        return 0;
      break;
  }
  return this.__FatalError(err);
};

// 全局错误处理
Expression.prototype.__OnError = function(ifError)
{
  if(this.FLAG(Expression.PARSE_FLAG_ERROR_HANDLE_SCHEME_THROW_EXCEPTION)) // 抛异常
  {
    throw new Error(this.Error());
  }
  else // 返回0
  {
    return ifError;
  }
};

// 计算是否有错误
Expression.prototype.HasError = function()
{
  return this.errno !== Expression.NO_ERROR;
};

// 返回计算的错误, 并清除错误
Expression.prototype.GetError = function()
{
  let e = this.errno;
  this.errno = Expression.NO_ERROR;
  return e;
}

// 获取当前错误的字符串
Expression.prototype.Error = function()
{
  return Expression.ErrorStr[this.errno];
}

// 致命错误
Expression.prototype.__FatalError = function(err)
{
  this.errno = err;
  throw new Error(this.Error());
};

Expression.prototype.ParsedPlaceholders = function()
{
  return this.placeholders;
}

// 解析占位符
Expression.prototype.__ParsePlaceholder = function(name)
{
  this.placeholders.push(name);
  if(!this.placeholderValueMap)
  {
    return this.__NormalError(Expression.MISSING_PLACEHOLDER);
  }
  if(name.startsWith("$")) // 特殊环境变量
    return this.__ParseNumber(this.placeholderValueMap[name]);

  let ptr = this.placeholderValueMap;
  let d = null;
  let notFetch = false;
  let i = 0;
  do
  {
    let index = name.indexOf(".", i);
    let s = index === -1 ? name.substring(i) : name.substring(i, index);
    i += s.length + 1;
    s = s.trim();
    if(s.startsWith("$")) // 函数管线
    {
      if(!notFetch) // 此时当前值应该是数字, 不再继续向下取Object/Map
      {
        d = this.__ParseNumber(ptr); // 当前值转换为数字
        notFetch = true; // 此时当前值应该是数字, 不再继续向下取Object/Map
      }
      switch(s.substring(1).toLowerCase())
      {
        case "abs":
          d = Math.abs(d);
          break;
        case "neg":
          d = -d;
          break;
        case "lt0":
          if(d >= 0)
            return 0;
          break;
        case "gt0":
          if(d <= 0)
            return 0;
          break;
        default:
          return this.__FatalError(Expression.INVALID_PLACEHOLDER_FUNCTION);
      }
    }
    else
    {
      if(notFetch/*null != d*/) // TODO: 被管线函数处理过, 占位符表达式书写不应该出现该情况, 管线函数应都在表达式尾部, 并且不在表达式起始
        return d;
      ptr = ptr[s];
      if(null === ptr || undefined === ptr)
      {
        return this.__NormalError(Expression.MISSING_PLACEHOLDER);
      }
    }
  } while(i < name.length);
  if(null == d)
    return this.__ParseNumber(ptr);
  else
    return d;
};

// 检查表达式字符串
Expression.prototype.__CheckStr = function()
{
  if(!this.str)
  {
    return Expression.NULL_STRING;
  }
  return Expression.NO_ERROR;
};

// 跳过空白符
Expression.prototype.__SkipBlank = function()
{
  let i = 0;
  for(; (this.pos + i) < this.str.length; i++)
  {
    if(!/\s/.test(this.str.charAt(this.pos + i)))
      break;
  }
  this.pos += i;
  return i;
};

// 对象转数字
Expression.prototype.__ParseNumber = function(val)
{
  if(null === val)
    return this.__NormalError(Expression.INVALID_DIGIT);
  if(val instanceof Number)
    return val;
  else
  {
    try
    {
      return this.__StrTo(val.toString());
    }
    catch(e)
    {
      console.error(e);
      return this.__NormalError(Expression.INVALID_DIGIT);
    }
  }
};

