package com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.iml;

import com.alibaba.fastjson.JSON;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Options;
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
import com.jiayue.ipfcst.dataexchange.container.ProtocolDataContainer;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.BaseDataGainer;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.NumOrBool;
import com.jiayue.ipfcst.dataexchange.exception.DataExchangeException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 点位数据计算器
 * 公式中使用   P_点位_P  来划分点位
 *
 * @author: xiuwei
 * @version:
 */
@Slf4j
public class PointDataCalculator<V> extends BaseDataGainer<V> {

  private static final Pattern expr = Pattern.compile("(?<=P_).*?(?=_P)");

  static {
    AviatorEvaluator.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
  }

  /**
   * 计算表达式
   */
  Expression compiledExp;
  /**
   * 计算所需参数
   */
  private Map<String, Integer> calculatingParameters;
  /**
   * 计算所需公式
   */
  @Getter
  private String formula;
  private ProtocolDataContainer protocolDataContainer = ProtocolDataContainer.getInstance();

  public PointDataCalculator(NumOrBool expectedType, String formula, Double magnification) {
    super(expectedType, magnification);
    this.formula = formula;
  }


  /**
   * 计算
   * 对公式进行计算
   *
   * @return V 计算完成的数据
   * @throws DataExchangeException 数据交换异常
   */
  public V calculate() throws DataExchangeException {
    Map<String, Object> values = new HashMap<>();
    for (Map.Entry<String, Integer> e : getCalculatingParameters().entrySet()) {
      values.put(e.getKey(), protocolDataContainer.getData(e.getValue()));
    }
    V v = (V) new Integer(0);
    try {
      v = (V) getOrInitCompiledExp().execute(values);
    } catch (ArithmeticException e) {
      log.error("除数为0,计算公式为：{},请检查数据池内是否有该点位是否有数据", this.formula);
    } catch (Exception e) {
      log.error("数据计算{}时发生异常",this.formula,e);
      log.info(JSON.toJSONString(values));
      throw new DataExchangeException(10021, "计算器计算数据时发生异常,公式为：" + this.formula);
    }
    if(v==null){
      log.info(JSON.toJSONString(values));
    }
    return v;
  }

  /**
   * 对公式进行初始化  如果公式解析失败将会抛出异常
   *
   * @return Expression  解析完成的表达式
   * @throws DataExchangeException 表达式解析失败时的异常
   */
  private Expression getOrInitCompiledExp() throws DataExchangeException {
    if (this.compiledExp == null) {
      try {
        this.compiledExp = AviatorEvaluator.compile(formula);
      } catch (ExpressionSyntaxErrorException e) {
        log.error("公式解析失败，公式不可用", e);
        throw new DataExchangeException(50001, "公式解析失败，公式不可用");
      }
    }
    return this.compiledExp;
  }


  /**
   * 对计算所需的参数进行整理
   *
   * @return P_（点位）_P  替换成真实的数据   字段--数值
   */
  public Map<String, Integer> getCalculatingParameters() {
    if (this.calculatingParameters == null) {
      this.calculatingParameters = new HashMap<>();
      Matcher matcher = expr.matcher(this.formula);
      while (matcher.find()) {
        String s = matcher.group();
        this.calculatingParameters.put("P_" + s + "_P", Integer.parseInt(s));
      }
    }
    return this.calculatingParameters;
  }


  @Override
  public V getData() throws DataExchangeException {
    Object v = calculate();
    if(v instanceof  Long){
      v= BigDecimal.valueOf((Long) v);
    }else if(v instanceof  Double){
      v= BigDecimal.valueOf((Double) v);
    } else if(v instanceof  Integer){
      v= BigDecimal.valueOf((Integer) v);
    }else if(v instanceof  Float){
      v= BigDecimal.valueOf((Float) v);
    } else if(v instanceof  BigDecimal){
      v= (BigDecimal) v;
    }
    return (V) v;
  }
}
