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


import cn.hutool.core.util.RuntimeUtil;
import com.jiayue.ipfcst.common.data.constant.enums.DataSourceEnum;
import com.jiayue.ipfcst.common.data.constant.enums.TunnelStatus;
import com.jiayue.ipfcst.common.data.entity.ProtocolDataType;
import com.jiayue.ipfcst.common.data.entity.ProtocolGatherDataPoint;
import com.jiayue.ipfcst.common.data.entity.gathertunnelinfo.Gather104TcpTunnelInfo;
import com.jiayue.ipfcst.dataexchange.exception.DataExchangeException;
import com.jiayue.ipfcst.dataexchange.logger.TunnelLoggerFactory;
import com.jiayue.ipfcst.dataexchange.tunnelworker.BaseProtocolTunnel;
import com.jiayue.ipfcst.dataexchange.tunnelworker.gathers.DataGatherInterface;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.SingleThreadPoolExecutorUtil;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.iec104tcp.SendDataFrameHelper;
import org.joda.time.DateTime;
import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 104协议数据获取
 *
 * @author 修唯xiuwei
 * @version 3.0
 */
public class Iec104TcpDataGather extends BaseProtocolTunnel<Gather104TcpTunnelInfo> implements DataGatherInterface {


  /**
   * 104的master 客户端
   */
  private Iec104HSMasterBuilder masterBuilder;

  /**
   * 因无数据传输而重连的次数
   */
  private int noDataReconTimes = 0;

  /**
   * 点表
   */
  private Map<Integer, ProtocolGatherDataPoint> iec104DataPoint = new HashMap<>();


  /**
   * iec104tcp数据采集
   * 构造方法
   *
   * @param gather104TcpTunnelInfo 104采集通道信息
   * @throws DataExchangeException 数据交换异常
   */
  public Iec104TcpDataGather(Gather104TcpTunnelInfo gather104TcpTunnelInfo) throws DataExchangeException {
    super(gather104TcpTunnelInfo);
    parseGatherDataPoint();
  }

  @Override
  public void parseGatherDataPoint() throws DataExchangeException {
    List<ProtocolGatherDataPoint> protocolGatherDataPoints = this.getTunnelInfo().getDataPoints();
    iec104DataPoint = new HashMap<>();
    if (protocolGatherDataPoints == null || protocolGatherDataPoints.size() == 0) {
      return;
    }
    protocolGatherDataPoints.removeIf((o) -> o.getProtocolPointNo() == null || o.getDataSource() != DataSourceEnum.GATHER);
    protocolGatherDataPoints.sort(Comparator.comparingInt(p -> (p.getProtocolPointType().equals(ProtocolDataType.A) ? p.getProtocolPointNo() : p.getProtocolPointNo() + 16385))
    );
    //默认排序后 前段为遥信  后段为遥测   且遥测第一个为16385的点位
    for (int i = 0; i < protocolGatherDataPoints.size(); i++) {
      //把顺序转化成点位  遥信是开头和顺序一样
      if (ProtocolDataType.A.equals(protocolGatherDataPoints.get(i).getProtocolPointType())) {
        iec104DataPoint.put(protocolGatherDataPoints.get(i).getProtocolPointNo(), protocolGatherDataPoints.get(i));
      } else {
        iec104DataPoint.put(protocolGatherDataPoints.get(i).getProtocolPointNo(), protocolGatherDataPoints.get(i));
      }
    }
    if (masterBuilder != null) {
      masterBuilder.getConfigInfoMap().put("104DataPoint", this.iec104DataPoint);
    }
  }


  @Override
  public Iec104TcpDataGather buildTunnel() throws DataExchangeException {
    masterBuilder = new MyMasterBuilder(tunnelInfo.getRemoteIp(), tunnelInfo.getRemotePort());
    masterBuilder.getConfigInfoMap().put("104DataPoint", this.iec104DataPoint);
    masterBuilder.setLog(log);
    if (getTunnelInfo().getRemoteSpareIp() != null) {
      masterBuilder.setSpareIp(getTunnelInfo().getRemoteSpareIp());
    }
    if (getTunnelInfo().getRemoteSparePort() != null) {
      masterBuilder.setSparePort(getTunnelInfo().getRemoteSparePort());
    }
    if (getTunnelInfo().getSelfIp() != null) {
      masterBuilder.setSelfIp(getTunnelInfo().getSelfIp());
    }
    if (getTunnelInfo().getSelfPort() != null) {
      masterBuilder.setSelfPort(getTunnelInfo().getSelfPort());
    }
    getProtocolTunnelContainer().addTunnel(this);
    this.setTunnelStatus(TunnelStatus.BUILT);
    log.info("成功创建Iec 104Tcp master通道对象：{}", this.tunnelInfo.getTunnelName());
    return this;
  }

  @Override
  public synchronized Iec104TcpDataGather startTunnel() throws DataExchangeException {
    protocolTunnelContainer.addUpdateDateTask(this);
    log.info("IEC104 TCP master {} 通道开始连接", getTunnelInfo().getTunnelName());
    try {
      SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> this.masterBuilder.create());
    } catch (Exception e) {
      new DataExchangeException(10009, "104master通道创建时发生异常");
    }
    return this;
  }


  @Override
  public synchronized BaseProtocolTunnel tunnelStop() throws DataExchangeException {
    try {
      if (this.masterBuilder != null) {
        this.masterBuilder.stop();
      }
      log.info("104 master {} 关闭", tunnelInfo.getTunnelName());
    } catch (Exception e) {
      throw new DataExchangeException(40012, "关闭104通道失败");
    } finally {
      return super.tunnelStop();
    }

  }

  @Override
  public synchronized void getDataFromProtocol() throws DataExchangeException {
    try {
      log.info("向104 通道:{} 中发送总召唤", getTunnelInfo().getTunnelName());
      DateTime lastTime = DateTime.now();
      if (!this.masterBuilder.getConfigInfoMap().containsKey("lastReceiveTime") || this.masterBuilder.getConfigInfoMap().get("lastReceiveTime") == null) {
        this.masterBuilder.getConfigInfoMap().put("lastReceiveTime", lastTime);
      } else {
        lastTime = (DateTime) this.masterBuilder.getConfigInfoMap().get("lastReceiveTime");
      }
      if (lastTime.plusMinutes(2).isBefore(DateTime.now())) {
        log.warn("通道已超过两分钟没有接收到信息，重连");
        if (this.noDataReconTimes > 4) {
          log.warn("通道已超过十分钟没有接收到信息，切换主备机");
          try {
            this.masterBuilder.switchover();
          }catch (Exception e){
            log.error("转换主备机失败",e);
          }
          this.noDataReconTimes = 0;
        }
        this.tunnelStop();
        this.log = TunnelLoggerFactory.getLogger(tunnelId, tunnelInfo.getTunnelName());
        this.masterBuilder.getConfigInfoMap().put("lastReceiveTime", DateTime.now());
        this.startTunnel();
        this.noDataReconTimes++;
      }
      if (this.masterBuilder.isConnected()) {
        SendDataFrameHelper.sendTotalSummonFrame(this.masterBuilder.getFuture().channel(), getTunnelInfo().getPublicAddress(), 6, log);

      }
    } catch (Exception e) {
      log.error("104通道发送数据时发生异常", e);
      throw new DataExchangeException(10008, "104通道发送数据时发生异常");
    }
  }


  /**
   * 内部类 用于设置连接 和断连 的后续动作
   */
  class MyMasterBuilder extends Iec104HSMasterBuilder {
    public MyMasterBuilder(String ip, Integer port) {
      super(ip, port);
    }

    @Override
    public void connected() {
      log.info("IEC104 Master {}连接成功", getTunnelInfo().getTunnelName());
      setTunnelStatus(TunnelStatus.CONNECTED);
    }

    @Override
    public void disconnected() {
      log.error("IEC104 Master {}断开连接", getTunnelInfo().getTunnelName());
      setTunnelStatus(TunnelStatus.LOSECONN);
    }


  }

}
