package com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.iec104tcp;

import com.jiayue.ipfcst.dataexchange.dataprocesser.datagainerhandler.BaseDataGainer;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import wei.yigulu.iec104.apdumodel.Apdu;
import wei.yigulu.iec104.apdumodel.Asdu;
import wei.yigulu.iec104.asdudataframe.BooleanType;
import wei.yigulu.iec104.asdudataframe.PulseTotalSummonType;
import wei.yigulu.iec104.asdudataframe.ShortFloatType;
import wei.yigulu.iec104.asdudataframe.TotalSummonType;
import wei.yigulu.iec104.asdudataframe.typemodel.InformationBodyAddress;
import wei.yigulu.iec104.util.SendAndReceiveNumUtil;

import java.util.*;

/**
 * 发送数据帧的工具类
 *
 * @author: xiuwei
 * @version:
 */
public class SendDataFrameHelper {

  /**
   * 发送遥信数据帧
   *
   * @param channel 通达对象
   * @param dates   要发送的数据
   * @param address 公共地址位
   * @param cause   发送原因
   * @throws Exception 异常信息
   */
  public static void sendYxDataFrame(Channel channel, Map<Integer, BaseDataGainer<Boolean>> dates, Integer address, Integer cause, Logger log) throws Exception {
    BooleanType booleanType;
    Asdu asdu;
    Set<Integer> keys;
    Integer max;
    Integer min;
    StringBuffer logStr = new StringBuffer("推送遥信数据：");
    Boolean value;
    if (dates.size() > 0) {
      keys = dates.keySet();
      max = Collections.max(keys);
      min = Collections.min(keys);
      if ((max - min) == (keys.size() - 1)) {
        logStr.append("连续寻址数据，地址-" + min + " | ");
        for (List<Integer> li : splitAndSort(keys, 400)) {
          booleanType = new BooleanType();
          booleanType.addAddress(new InformationBodyAddress(li.get(0)));
          for (Integer i : li) {
            value = dates.get(i).getData();
            logStr.append(value + " ; ");
            booleanType.addData(value);
          }
          asdu = booleanType.generateBack();
          asdu.setNot(cause);
          asdu.setCommonAddress(address);
          Apdu apdu = new Apdu().setAsdu(asdu);
          SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
          Thread.sleep(20);
        }
      } else {
        logStr.append("单一寻址数据");
        for (Map<Integer, BaseDataGainer<Boolean>> m : split(dates, 100)) {
          booleanType = new BooleanType();
          for (Map.Entry<Integer, BaseDataGainer<Boolean>> em : m.entrySet()) {
            value = em.getValue().getData();
            logStr.append("{" + em.getKey() + ":" + value + "} ; ");
            booleanType.addDataAndAdd(new InformationBodyAddress(em.getKey()), value);
          }
          asdu = booleanType.generateBack();
          asdu.setNot(cause);
          asdu.setCommonAddress(address);
          Apdu apdu = new Apdu().setAsdu(asdu);
          SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
          Thread.sleep(20);
        }
      }
      log.info(logStr.toString());
    }
  }


  /**
   * 发送遥信数据帧 不连续
   *
   * @param channel 通达对象
   * @param dates   要发送的数据
   * @param address 公共地址位
   * @param cause   发送原因
   * @throws Exception 异常信息
   */
  public static void sendYxDataFrameDiscontinuity(Channel channel, Map<Integer, BaseDataGainer<Boolean>> dates, Integer address, Integer cause, Logger log) throws Exception {
    BooleanType booleanType;
    Asdu asdu;
    StringBuffer logStr = new StringBuffer("推送遥信数据，单一寻址数据：");
    Boolean value;
    if (dates.size() > 0) {
      for (Map<Integer, BaseDataGainer<Boolean>> m : split(dates, 100)) {
        booleanType = new BooleanType();
        for (Map.Entry<Integer, BaseDataGainer<Boolean>> em : m.entrySet()) {
          value = em.getValue().getData();
          logStr.append("{" + em.getKey() + ":" + value + " }; ");
          booleanType.addDataAndAdd(new InformationBodyAddress(em.getKey()), value);
        }
        asdu = booleanType.generateBack();
        asdu.setNot(cause);
        asdu.setCommonAddress(address);
        Apdu apdu = new Apdu().setAsdu(asdu);
        SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
        Thread.sleep(20);
      }
      log.info(logStr.toString());
    }
  }

  /**
   * 发送遥测 数据帧
   *
   * @param channel 通道对象
   * @param dates   需要发送的数据
   * @param address 公共地址位
   * @param cause   发送的原因
   * @throws Exception 异常
   */
  public static void sendYcDataFrame(Channel channel, Map<Integer, BaseDataGainer<Number>> dates, Integer address, Integer cause, Logger log) throws Exception {
    Asdu asdu;
    ShortFloatType shortFloatType;
    Set<Integer> keys;
    Integer max;
    Integer min;
    Float value;
    StringBuffer logStr = new StringBuffer("推送遥测数据：");
    if (dates.size() > 0) {
      keys = dates.keySet();
      max = Collections.max(keys);
      min = Collections.min(keys);
      if ((max - min) == (keys.size() - 1)) {
        logStr.append("连续寻址数据,起始地址-" + min + "|");
        for (List<Integer> li : splitAndSort(keys, 45)) {
          shortFloatType = new ShortFloatType();
          shortFloatType.addAddress(new InformationBodyAddress(li.get(0)));
          for (Integer i : li) {
            value = dates.get(i).getData().floatValue();
            logStr.append(value + " ; ");
            shortFloatType.addData(value);
          }
          asdu = shortFloatType.generateBack();
          asdu.setNot(cause);
          asdu.setCommonAddress(address);
          Apdu apdu = new Apdu().setAsdu(asdu);
          SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
          Thread.sleep(20);
        }
      } else {
        logStr.append("单一寻址数据");
        for (Map<Integer, BaseDataGainer<Number>> m : split(dates, 25)) {
          shortFloatType = new ShortFloatType();
          for (Map.Entry<Integer, BaseDataGainer<Number>> em : m.entrySet()) {
            value = em.getValue().getData().floatValue();
            logStr.append("{" + em.getKey() + ":" + value + "} ; ");
            shortFloatType.addDataAndAdd(new InformationBodyAddress(em.getKey()), value);
          }
          asdu = shortFloatType.generateBack();
          asdu.setNot(cause);
          asdu.setCommonAddress(address);
          Apdu apdu = new Apdu().setAsdu(asdu);
          SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
          Thread.sleep(20);
        }
      }
      log.info(logStr.toString());
    }
  }


  /**
   * 发送遥测 数据帧 不连续
   *
   * @param channel 通道对象
   * @param dates   需要发送的数据
   * @param address 公共地址位
   * @param cause   发送的原因
   * @throws Exception 异常
   */
  public static void sendYcDataFrameDiscontinuity(Channel channel, Map<Integer, BaseDataGainer<Number>> dates, Integer address, Integer cause, Logger log) throws Exception {
    Asdu asdu;
    ShortFloatType shortFloatType;
    StringBuffer logStr = new StringBuffer("推送遥测数据，单一寻址：");
    Float value;
    if (dates.size() > 0) {
      for (Map<Integer, BaseDataGainer<Number>> m : split(dates, 25)) {
        shortFloatType = new ShortFloatType();
        for (Map.Entry<Integer, BaseDataGainer<Number>> em : m.entrySet()) {
          value = em.getValue().getData().floatValue();
          logStr.append("{" + em.getKey() + ":" + value + "} ; ");
          shortFloatType.addDataAndAdd(new InformationBodyAddress(em.getKey()), value);
        }
        asdu = shortFloatType.generateBack();
        asdu.setNot(cause);
        asdu.setCommonAddress(address);
        Apdu apdu = new Apdu().setAsdu(asdu);
        SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
        Thread.sleep(20);
      }
      log.info(logStr.toString());
    }
  }

  /**
   * 发送总召唤 帧
   *
   * @param channel 通道对象
   * @param address 公共地址位
   * @param cause   发送的原因
   * @throws Exception 异常
   */
  public static void sendTotalSummonFrame(Channel channel, Integer address, Integer cause, Logger log) throws Exception {
    Asdu asdu;
    TotalSummonType dataFrameType = new TotalSummonType();
    dataFrameType.setAddress(new InformationBodyAddress(0));
    dataFrameType.setValue(20);
    asdu = dataFrameType.generateBack();
    asdu.setNot(cause);
    asdu.setCommonAddress(address);
    Apdu apdu = new Apdu().setAsdu(asdu);
    SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
  }


  /**
   * 发送电度总召唤 帧
   *
   * @param channel 通道对象
   * @param address 公共地址位
   * @param cause   发送的原因
   * @throws Exception 异常
   */
  public static void sendPulseTotalSummonFrame(Channel channel, Integer address, Integer cause, Logger log) throws Exception {
    Asdu asdu;
    PulseTotalSummonType dataFrameType = new PulseTotalSummonType();
    dataFrameType.setAddress(new InformationBodyAddress(0));
    dataFrameType.setValue(69);
    asdu = dataFrameType.generateBack();
    asdu.setNot(cause);
    asdu.setCommonAddress(address);
    Apdu apdu = new Apdu().setAsdu(asdu);
    SendAndReceiveNumUtil.sendIFrame(apdu, channel, log);
  }

  /**
   * 将发送的数据集合拆成n个长度合适的集合
   *
   * @param map    总集合
   * @param maxLen 设定最大的长度
   * @param <T>    类型T
   * @return 分集合
   */
  public static <T> List<HashMap<Integer, T>> split(Map<Integer, T> map, int maxLen) {
    List<HashMap<Integer, T>> list = new ArrayList<>();
    HashMap transfer = new HashMap(maxLen);
    int j = 0;
    for (Integer o : map.keySet()) {
      if (j < maxLen) {
        transfer.put(o, map.get(o));
        j++;
      } else {
        list.add(transfer);
        transfer = new HashMap(maxLen);
        transfer.put(o, map.get(o));
        j = 1;
      }
    }
    list.add(transfer);
    return list;
  }


  /**
   * 进行拆分并排序 主要是排序map 中的keyset 对keyset的int型进行排序
   *
   * @param set    要排序的set
   * @param maxLen 设定的最长长度
   * @return 拆分后的集合 的集合
   */
  public static List<List<Integer>> splitAndSort(Set<Integer> set, int maxLen) {
    List<List<Integer>> list = new ArrayList<>();
    List<Integer> ls = new ArrayList<>();
    List<Integer> transfer = new ArrayList<>(maxLen);
    for (Integer i : set) {
      ls.add(i);
    }
    Collections.sort(ls);
    int j = 0;
    for (Integer i : ls) {
      if (j < maxLen) {
        transfer.add(i);
        j++;
      } else {
        list.add(transfer);
        transfer = new ArrayList<>(maxLen);
        transfer.add(i);
        j = 1;
      }
    }
    list.add(transfer);
    return list;
  }
}
