package com.netsdk.lib.callback.impl;

import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.callback.BasicVideoStatHeatMapCallBack;
import com.netsdk.lib.callback.fVideoStatHeatMapCallBack;
import com.netsdk.lib.enumeration.EM_HEATMAP_TYPE;
import com.netsdk.module.HeatMapModule;
import com.netsdk.module.entity.HeatMapData;
import com.netsdk.module.entity.HeatMapGrayData;
import com.sun.jna.Pointer;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 47081
 * @version 1.0
 * @description 热度图回调函数的默认实现, 回调函数建议写成单例模式, 不然容易导致sdk出现意想不到的错误
 * @date 2020/9/24
 */
public class DefaultVideoStatHeatMapCallBack extends BasicVideoStatHeatMapCallBack {
  private HeatMapModule module;
  /** 由于热度图数据是分多次发送,需要判断数据接收完整再处理 */
  private ConcurrentHashMap<Integer, HeatMapData> binDatas;
  /** 用于外部获取灰度图数据 */
  public static ConcurrentHashMap<Integer, HeatMapGrayData> grayDatas =
      new ConcurrentHashMap<Integer, HeatMapGrayData>();

  private static DefaultVideoStatHeatMapCallBack instance;

  private DefaultVideoStatHeatMapCallBack() {
    this.module = new HeatMapModule();
    this.binDatas = new ConcurrentHashMap<Integer, HeatMapData>();
  }

  public static fVideoStatHeatMapCallBack getInstance() {
    if (instance == null) {
    	instance = new DefaultVideoStatHeatMapCallBack();
    }
    return instance;
  }

  /**
   * 解析数据
   *
   * @param attachHandle 订阅句柄
   * @param nToken 获取数据的句柄,即调用{@link
   *     com.netsdk.lib.NetSDKLib#CLIENT_GetVideoStatHeatMap(NetSDKLib.LLong, Pointer, Pointer,
   *     int)} 对应其出参的nToken字段{@link com.netsdk.lib.structure.NET_OUT_GET_VIDEOSTAT_HEATMAP#nToken}
   * @param type 热度图类型
   * @param binData 热度图字节数据
   */
  @Override
  public void parseData(long attachHandle, int nToken, EM_HEATMAP_TYPE type, byte[] binData) {
    System.out.println(
        "receive heatmap data. attachHandle:"
            + attachHandle
            + ",nToken:"
            + nToken
            + ",type:"
            + type.getType()
            + ","
            + type.getDesc()
            + " data length:"
            + binData.length);
    // 协议描述:每个getHeatMap请求的结果分多个包来发送，每个包的大小在 “4~7此次数据总长度”中描述，每个包的 “8~11
    // 总行数（总轨迹条数）”相同，每个包的“12~15本次行数（本次轨迹条数）”表示本包的数据数量，最后一个包的“12~15本次行数（本次轨迹条数）”值为0，表示数据发送完毕。
    // 当总行数（或者总轨迹条数）和本次行数（或者本次轨迹条数）都为0时，表示没有数据。
    // 当总行数（或者总轨迹条数）不为0，而本次行数（或者本次轨迹条数）为0时表示，数据已经传完
    // 对binData进行过滤
    byte[] temp = new byte[4];
    System.arraycopy(binData, 7, temp, 0, 4);
    // 总行数
    int totalLine = byte2Int(temp);
    System.arraycopy(binData, 11, temp, 0, 4);
    // 本次行数
    int currentLine = byte2Int(temp);
    if (totalLine == 0 && currentLine == 0) {
      System.out.println("token:" + nToken + ",本次没有数据");
      return;
    }
    // 总行数和本次行数都不为0时,说明数据有效
    if (totalLine != 0 && currentLine != 0) {
      HeatMapData heatMapData = binDatas.get(nToken);
      // 获取宽高,宽16-17位,高18-19位
      int width = ((binData[16] & 0xFF)) | ((binData[17] & 0xFF) << 8);
      int height = ((binData[18] & 0xFF)) | ((binData[19] & 0xFF) << 8);

      if (heatMapData == null) {
        heatMapData = new HeatMapData(width, height, binData);
      } else {
        /** 目前设备端处理是一个完整数据包和一个结束包,以下代码基本不会被执行到, 先按协议进行多包数据拼接处理 */
        // 获取已保存数据的数据行数
        System.arraycopy(heatMapData.getData(), 11, temp, 0, 4);
        int savedLine = byte2Int(temp);
        // 合并后的本次行数
        int currentTotal = savedLine + currentLine;
        byte[] bytes = int2ByteArr(currentTotal);
        System.arraycopy(bytes, 0, heatMapData.getData(), 11, bytes.length);
        // 去掉数据头,数据头为32位
        byte[] data = new byte[binData.length - 32];
        System.arraycopy(binData, 32, data, 0, data.length);
        // 追加到完整数据包中
        heatMapData.addData(data);
      }
      // 将数据存到map中
      binDatas.put(nToken, heatMapData);
    }
    // 数据接收完成
    if (totalLine != 0 && currentLine == 0) {

      // 将数据取出
      HeatMapData heatMapData = binDatas.get(nToken);

      if (heatMapData != null) {
        System.out.println(
            "width:"
                + heatMapData.getWidth()
                + ",height:"
                + heatMapData.getHeight()
                + ",data length:"
                + heatMapData.getData().length);
        // 热度图数据转灰度图
        byte[] grayData =
            module.transferGray(
                heatMapData.getData(), heatMapData.getWidth(), heatMapData.getHeight());
        // 销毁binData数据
        binDatas.remove(nToken);
        // 保存灰度图数据
        grayDatas.put(
            nToken, new HeatMapGrayData(heatMapData.getWidth(), heatMapData.getHeight(), grayData));
      }
    }
  }

  /**
   * 将byte[4]转成int
   *
   * @param src
   * @return
   */
  private int byte2Int(byte[] src) {
    int value;
    value =
        (int)
            ((src[0] & 0xFF)
                | ((src[1] & 0xFF) << 8)
                | ((src[2] & 0xFF) << 16)
                | ((src[3] & 0xFF) << 24));
    return value;
  }

  /**
   * int转成byte[],长度为4
   *
   * @param value
   * @return
   */
  private byte[] int2ByteArr(int value) {
    byte[] bytes = new byte[4];
    bytes[3] = (byte) (value >> 24);
    bytes[2] = (byte) (value >> 16);
    bytes[1] = (byte) (value >> 8);
    bytes[0] = (byte) (value);
    return bytes;
  }
}
