package com.jiayue.ipfcst.dataexchange.tunnelworker.senders.iml;

import cn.hutool.core.util.RuntimeUtil;
import com.alibaba.fastjson.JSON;
import com.jiayue.ipfcst.common.core.util.SpringContextHolder;
import com.jiayue.ipfcst.common.data.constant.enums.TunnelStatus;
import com.jiayue.ipfcst.common.data.entity.ProtocolDataType;
import com.jiayue.ipfcst.common.data.entity.ProtocolSenderDataPoint;
import com.jiayue.ipfcst.common.data.entity.sendertunnelinfo.Sender104TcpTunnelInfo;
import com.jiayue.ipfcst.console.service.SysParameterService;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.BaseDataGainer;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.DataGainerBuilder;
import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.NumOrBool;
import com.jiayue.ipfcst.dataexchange.exception.DataExchangeException;
import com.jiayue.ipfcst.dataexchange.tunnelworker.BaseProtocolTunnel;
import com.jiayue.ipfcst.dataexchange.tunnelworker.senders.DataSenderInterface;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.SingleThreadPoolExecutorUtil;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.TunnelBuilder;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.iec104tcp.SendDataFrameHelper;
import io.netty.channel.Channel;
import org.apache.commons.lang.StringUtils;
import wei.yigulu.iec104.nettyconfig.Iec104SlaverBuilder;

import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.util.*;

/**
 * 104转发的通道实体
 * <p>
 * 转发点表默认连续  遥信遥测分区
 *
 * @author: xiuwei
 * @version:
 */
public class Iec104TcpDataSender extends BaseProtocolTunnel<Sender104TcpTunnelInfo> implements DataSenderInterface {


  /**
   * 遥测点位数据构建器集合
   */
  Map<Integer, BaseDataGainer<Number>> ycDataPoints = new HashMap<>();

  /**
   * 遥信点位数据构建器集合
   */
  Map<Integer, BaseDataGainer<Boolean>> yxDataPoints = new HashMap<>();

  SysParameterService sysParameterService = SpringContextHolder.getBean(SysParameterService.class);


  /**
   * 104slave的客户端
   */
  private Iec104SlaverBuilder slaverBuilder;

  private List<String> ips = new ArrayList<>();

  /**
   * 顶层的构造方法
   *
   * @param tunnelInfo 通道信息
   */
  public Iec104TcpDataSender(Sender104TcpTunnelInfo tunnelInfo) {
    super(tunnelInfo);
  }

  @Override
  public Iec104TcpDataSender startTunnel() throws DataExchangeException {
    protocolTunnelContainer.addSaveSendDataTask(this);
    protocolTunnelContainer.addUpdateDateTask(this);
    try {
      SingleThreadPoolExecutorUtil.submitBySingleThreadExecutor(() -> {
        try {
          this.slaverBuilder.create();
          log.error("104 slaver  通道监听结束");
          this.setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
        } catch (Exception e) {
          log.error("创建104Sender失败", e);
          this.setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
        }
      });
    } catch (Throwable e) {
      setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
      log.error("104slaver通道创建时发生异常", e);
      throw new DataExchangeException(10010, "104slaver通道创建时发生异常");
    }
    if (!TunnelStatus.LISTENPORTFAIL.equals(getTunnelStatus())) {
      setTunnelStatus(TunnelStatus.LISTENPORTSUCCESS);
    }
    return this;
  }

  @Override
  public Iec104TcpDataSender buildTunnel() throws DataExchangeException {
    parseGatherDataPoint();
    this.slaverBuilder = new MyIec104SlaverBuilder(this.tunnelInfo.getSelfPort());
    if (StringUtils.isNotEmpty(this.tunnelInfo.getSelfIp())) {
      this.slaverBuilder.setIp(this.tunnelInfo.getSelfIp());
    }
    this.slaverBuilder.setLog(log);
    if (this.tunnelInfo.getRemoteIp() != null && StringUtils.isNotBlank(this.tunnelInfo.getRemoteIp())) {
      this.ips = Arrays.asList(this.tunnelInfo.getRemoteIp().split(","));
    }
    this.slaverBuilder.getConfigInfoMap().put("yx", this.yxDataPoints);
    this.slaverBuilder.getConfigInfoMap().put("yc", this.ycDataPoints);
    if ("1".equals(sysParameterService.getSysParameter("104ConnFilter", "0"))) {
      this.slaverBuilder.getConnectFilterManager().appendFilter((c) -> {
        InetSocketAddress ipSocket = (InetSocketAddress) c.remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        if (this.ips.contains(clientIp)) {
          return 1;
        } else {
          log.warn("未知IP {},请求连接,已被拒绝。", clientIp);
          return -1;
        }
      });
    }
    this.setTunnelStatus(TunnelStatus.BUILT);
    protocolTunnelContainer.addTunnel(this);
    log.info("成功创建Iec 104Tcp master通道对象：{}", this.tunnelInfo.getTunnelName());
    return this;
  }

  @Override
  public void parseGatherDataPoint() throws DataExchangeException {
    log.info("解析通道下所需点位");
    List<ProtocolSenderDataPoint> protocolSenderDataPoints = this.getTunnelInfo().getDataPoints();
    yxDataPoints = new HashMap<>();
    ycDataPoints = new HashMap<>();
    if (protocolSenderDataPoints == null || protocolSenderDataPoints.size() == 0) {
      return;
    }
    protocolSenderDataPoints.removeIf((o) -> o.getProtocolPointNo() == null);
    Collections.sort(protocolSenderDataPoints, Comparator.comparingInt(ProtocolSenderDataPoint::getProtocolPointNo));
    for (ProtocolSenderDataPoint p : protocolSenderDataPoints) {
      if (ProtocolDataType.A.equals(p.getProtocolPointType())) {
        yxDataPoints.put(p.getProtocolPointNo(), DataGainerBuilder.build(p));
      } else {
        ycDataPoints.put(p.getProtocolPointNo() + 16384, DataGainerBuilder.build(p));
      }
    }
    log.info("遥测点位对应：" + JSON.toJSONString(ycDataPoints));
    log.info("遥信点位对应：" + JSON.toJSONString(yxDataPoints));
  }

  @Override
  public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
    if (this.slaverBuilder != null) {
      this.slaverBuilder.stop();
    }
    log.info("关闭IEC104通道：{}", this.tunnelInfo.getTunnelName());
    return super.tunnelStop();
  }

  @Override
  public void updateData2Protocol() throws DataExchangeException {
    String flag = sysParameterService.getSysParameter("SlaverAlwaysNormal", "0");
    if (this.slaverBuilder.getChannels().isEmpty()) {
      log.info("当前没有master联入");
      if ("0".equals(flag)) {
        if (!this.ips.isEmpty()) {
          this.setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDNOCONN);
        }
      }
      return;
    }
    log.info("向该通道的所有连接发送数据");
    String ip;
    List<String> bl = new ArrayList<>(ips);
    for (Channel channel : this.slaverBuilder.getChannels()) {
      ip = ((InetSocketAddress) channel.remoteAddress()).getHostString();
      bl.remove(ip);
      try {
        log.info("向{}发送数据", ip);
        SendDataFrameHelper.sendYxDataFrameDiscontinuity(channel, this.yxDataPoints, getTunnelInfo().getPublicAddress(), 3, log);
        SendDataFrameHelper.sendYcDataFrameDiscontinuity(channel, this.ycDataPoints, getTunnelInfo().getPublicAddress(), 3, log);
      } catch (Exception e) {
        log.error("104通道发送数据时发生异常", e);
        e.printStackTrace();
        throw new DataExchangeException(10008, "104通道发送数据时发生异常");
      }
    }
    if ("0".equals(flag)) {
      if (!bl.isEmpty()) {
        log.warn("{}未连接到子站", bl.get(0));
        this.setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDNOCONN);
      } else {
        this.setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDCONN);
      }
    }
  }

  @Override
  public void setValueByManual(ProtocolSenderDataPoint dataPoint, Double value) throws DataExchangeException {
    if (dataPoint.getProtocolPointType() == ProtocolDataType.A) {
      this.yxDataPoints.put(dataPoint.getProtocolPointNo(), new BaseDataGainer<Boolean>(NumOrBool.BOOL, 1D) {
        @Override
        public Boolean getData() throws DataExchangeException {
          return value > 0;
        }
      });
    } else {
      this.ycDataPoints.put(dataPoint.getProtocolPointNo() + 16384, new BaseDataGainer<Number>(NumOrBool.NUM, dataPoint.getMagnification()) {
        @Override
        public Number getData() throws DataExchangeException {
          return BigDecimal.valueOf(value).multiply(BigDecimal.valueOf(dataPoint.getMagnification()));
        }
      });
    }
  }


  @Override
  public String getDataBeingSent() {
    return TunnelBuilder.buildSenderDataStr(yxDataPoints, ycDataPoints);
  }

  class MyIec104SlaverBuilder extends Iec104SlaverBuilder {

    public MyIec104SlaverBuilder(int port) {
      super(port);
    }

    @Override
    public void connected(InetSocketAddress ipSocket) {
      setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDCONN);
    }

    @Override
    public void disconnected(InetSocketAddress ipSocket) {

      if (this.getChannels().size() == 0)
        //当没有连接联入监听端口时  设置该状态
        setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDNOCONN);
    }
  }
}
