package com.jiayue.ipfcst.dataexchange.container;

import com.alibaba.fastjson.JSONObject;
import com.jiayue.ipfcst.common.data.entity.ProtocolDataType;
import com.jiayue.ipfcst.common.data.entity.ProtocolGatherDataPoint;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.NumOrBool;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.iml.PointDataCalculator;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Bean;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;

/**
 * 协议采集到数据的容器
 *
 * @author 修唯xiuwei
 * @version 3.0
 */

@Slf4j
public class ProtocolDataContainer {


  @Getter
  private final DataFiltering dataFiltering = new DataFiltering();
  @Getter
  private final ModifyTriggering modifyTriggering = new ModifyTriggering();


  /**
   * 遥测数据
   */
  private Map<Integer, BigDecimal> yc = new ConcurrentHashMap<>();

  /**
   * 遥信数据
   */
  private Map<Integer, Boolean> yx = new ConcurrentHashMap<>();
  /**
   * 点位数据是否是有效值
   */
  private Map<Integer, Boolean> isValid = new ConcurrentHashMap<>();

  /**
   * 点位计算器
   */
  private Map<Integer, PointDataCalculator> calculators = new ConcurrentHashMap<>();

  /**
   * 点位计算器的线程任务
   */
  private Map<Integer, Callable> calculatorTasks = new ConcurrentHashMap<>();

  private ProtocolDataContainer() {
  }

  /**
   * 获取单例实例
   *
   * @return the instance
   */
  public static final ProtocolDataContainer getInstance() {
    return LazyHolder.INSTANCE;
  }

  /**
   * 向池子内置数据
   *
   * @param point point
   * @param value value
   */
  public void putNumber(Integer point, BigDecimal value) {
    try {
      try {
        value = dataFiltering.filter(point, value);
      }catch (Exception e){
        log.error("数据存入时数据过滤器处理逻辑出错");
      }
      this.yc.put(point, value);
      try {
        modifyTriggering.trigger(point, value);
      }catch (Exception e){
        log.error("数据发生变化时，触发器触发逻辑出错");
      }
    } catch (Exception e) {
      log.error("向数据池置数时发生异常", e);
    }
  }


  /**
   * 向池子内置数据
   *
   * @param point point
   * @param value value
   */
  public void putBoolean(Integer point, Boolean value) {
    this.yx.put(point, value);
  }

  /**
   * 设定点位是否可用
   *
   * @param point point
   * @param value value
   */
  public void setAvailable(Integer point, Boolean value) {
    this.isValid.put(point, value);
  }

  /**
   * 获取到缓存池内的数值 如果遥测池内没有但是存在于遥信池 将返回 0or1
   *
   * @param point 点位
   * @return Number number
   */
  public BigDecimal getNumber(Integer point) {
    if (isYcContains(point)) {
      return this.yc.get(point);
    } else if (isYxContains(point)) {
      return this.yx.get(point) ? BigDecimal.valueOf(1D) : BigDecimal.valueOf(0D);
    } else {
      return BigDecimal.valueOf(-99D);
    }
  }

  /**
   * 获取到返回池内的遥信数据  如果遥信池内没有 遥测池内有 那么大于0的数值 将会返回ture 否则为false
   *
   * @param point 点位
   * @return Boolean boolean
   */
  public Boolean getBoolean(Integer point) {
    if (isYxContains(point)) {
      return this.yx.get(point);
    } else if (isYcContains(point)) {
      return this.yc.get(point).compareTo(BigDecimal.ZERO) > 0;
    } else {
      return false;
    }
  }

  /**
   * 获取点位数据  直接返回
   *
   * @param point 点位key
   * @return 返回值  可能是Boolean 或者是 BigDecimal
   */
  public Object getData(Integer point) {
    if (isYcContains(point)) {
      return this.yc.get(point);
    }
    if (isYxContains(point)) {
      return this.yx.get(point);
    }
    return BigDecimal.valueOf(0);
  }

  /**
   * 查看某点位数据是否可用
   *
   * @param point point
   * @return the available
   */
  public Boolean getAvailable(Integer point) {
    return this.isValid.get(point);
  }

  /**
   * 遥信数据池是否含有该点位
   *
   * @param point point
   * @return the boolean
   */
  public Boolean isYxContains(Integer point) {
    return this.yx.containsKey(point);
  }

  /**
   * 遥测数据池是否含有该点位值
   *
   * @param point point
   * @return the boolean
   */
  public Boolean isYcContains(Integer point) {
    return this.yc.containsKey(point);
  }

  /**
   * 向计算器池子中  添加计算器
   *
   * @param point      点位id
   * @param calculator 点位计算器
   * @return 点位计算器
   */
  public PointDataCalculator addCalculator(Integer point, PointDataCalculator calculator) {
    removeCalculatorTask(point);
    this.calculators.put(point, calculator);
    this.calculatorTasks.put(point, RecurringTaskContainer.getInstance().addRecurringTask(5, "计算器计算任务", () -> {
      Object obj = null;
      try {
        obj = calculator.calculate();
        if (obj != null) {
          if (obj instanceof Boolean) {
            this.putBoolean(point, (Boolean) obj);
          } else {
            this.putNumber(point, new BigDecimal(obj.toString()));
          }
        } else {
          log.warn("计算公式{}结果值为空", calculator.getFormula());
        }
      } catch (Exception ex) {
        log.error("数据计算器池计算异常", ex);
      }
      return obj;
    }));
    return calculator;
  }


  /**
   * 移除点位计算线程任务
   *
   * @param point 点位id
   */
  public void removeCalculatorTask(Integer point) {
    if (this.calculatorTasks.containsKey(point)) {
      RecurringTaskContainer.getInstance().removeTask(this.calculatorTasks.get(point));
      this.calculatorTasks.remove(point);
    }
  }


  /**
   * 移除所有的点位计算线程任务
   */
  public void removeAllCalculatorTask() {
    this.calculatorTasks.forEach((k, v) -> RecurringTaskContainer.getInstance().removeTask(v));
    this.calculatorTasks.clear();
  }

  /**
   * 添加计算器
   * 向计算器池子中  添加计算器
   *
   * @param protocolGatherDataPoint 协议采集数据点
   * @return {@link PointDataCalculator}
   */
  public PointDataCalculator addCalculator(ProtocolGatherDataPoint protocolGatherDataPoint) {
    if (ProtocolDataType.A.equals(protocolGatherDataPoint.getProtocolPointType())) {
      return addCalculator(protocolGatherDataPoint.getId(), new PointDataCalculator<Boolean>(NumOrBool.BOOL, protocolGatherDataPoint.getGenerationRegulation(), protocolGatherDataPoint.getMagnification()));
    } else {
      return addCalculator(protocolGatherDataPoint.getId(), new PointDataCalculator<BigDecimal>(NumOrBool.NUM, protocolGatherDataPoint.getGenerationRegulation(), protocolGatherDataPoint.getMagnification()));
    }
  }


  @Override
  public String toString() {
    JSONObject object = new JSONObject();
    object.put("yx", this.yx);
    object.put("yc", this.yc);
    return object.toJSONString();
  }


  public Map realTimeData() {
    Map map = new HashMap();
    Iterator<Integer> iter = this.yx.keySet().iterator();
    while (iter.hasNext()) {
      Integer key = iter.next();
      Boolean value = this.yx.get(key);
      map.put(key, value);
    }
    Iterator<Integer> iterator = this.yc.keySet().iterator();
    while (iterator.hasNext()) {
      Integer key = iterator.next();
      DecimalFormat df = new DecimalFormat("0.00");
      String value = df.format(this.yc.get(key));
      map.put(key, value);
    }
    return map;
  }

  /**
   * 格式化打印信息 以表格的形式打印通道数据
   *
   * @return
   */
  public String toStringFormatted() {
    StringBuffer stringBuffer = new StringBuffer();
    String splitLine = "—————————————————————|—————————————————————|——————————————————————\n";
    stringBuffer.append(splitLine);
    stringBuffer.append("  key  |    value    |  key  |    value    |  key  |    value    |\n");
    stringBuffer.append(splitLine);
    int i = 0;
    Iterator<Integer> it = new TreeSet(this.yx.keySet()).iterator();
    Integer position;
    while (it.hasNext()) {
      position = it.next();
      stringBuffer.append(StringUtils.center(position + "", 7));
      stringBuffer.append("|");
      stringBuffer.append(StringUtils.center(this.yx.get(position) + "", 13));
      stringBuffer.append("|");
      i++;
      if (i % 3 == 0) {
        stringBuffer.append("\n");
        stringBuffer.append(splitLine);
      }
    }
    it = new TreeSet(this.yc.keySet()).iterator();
    while (it.hasNext()) {
      position = it.next();
      stringBuffer.append(StringUtils.center(position + "", 7));
      stringBuffer.append("|");
      stringBuffer.append(StringUtils.center(StringUtils.substring(this.yc.get(position) + "", 0, 11), 13));
      stringBuffer.append("|");
      i++;
      if (i % 3 == 0) {
        stringBuffer.append("\n");
        stringBuffer.append(splitLine);
      }
    }
    return stringBuffer.toString();
  }


  private static class LazyHolder {
    private static final ProtocolDataContainer INSTANCE = new ProtocolDataContainer();
  }


  public interface Filter {

    /**
     * 过滤规则
     *
     * @param value 值
     * @return {@link BigDecimal}
     */
    BigDecimal filteringRules(BigDecimal value);

  }

  /**
   * 数据过滤器
   *
   * @author xiuwei
   * @date 2021/04/08
   */
  public class DataFiltering {

    private Map<Integer, Filter> filters = new HashMap<>();

    public void addFilter(Integer point, Filter filter) {
      this.filters.put(point, filter);
    }


    public BigDecimal filter(Integer point, BigDecimal value) {
      if (this.filters.containsKey(point)) {
        return this.filters.get(point).filteringRules(value);
      } else {
        return value;
      }
    }


  }


  /**
   * 数据修改触发器
   *
   * @author 3377
   * @date 2022/10/12
   */
  public interface ModifyTrigger {

    /**
     * 触发执行事件
     *
     * @param value 值
     */
    void triggerHandler(BigDecimal value);

  }

  /**
   * 数据修改触发器
   *
   * @author xiuwei
   * @date 2021/04/08
   */
  public class ModifyTriggering {

    private final Map<Integer, ModifyTrigger> modifyTriggers = new HashMap<>();

    public void addTrigger(Integer point, ModifyTrigger trigger) {
      this.modifyTriggers.put(point, trigger);
    }


    public void trigger(Integer point, BigDecimal value) {
      if (this.modifyTriggers.containsKey(point)) {
        Executors.newSingleThreadExecutor().submit(() -> {
          try {
            modifyTriggers.get(point).triggerHandler(value);
          }catch (Exception e){
            log.error("数据修改触发器执行触发任务时发生异常",e);
          }
        });
      }
    }


  }

}
