package com.usky.sas.common.hik.sdk;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.usky.sas.common.enums.BusinessEnum;
import com.usky.sas.common.exception.BusinessException;
import com.usky.sas.common.global.Constant;
import com.usky.sas.common.global.GlobalMemoryMap;
import com.usky.sas.common.global.StreamContext;
import com.usky.sas.common.hik.sdk.HCNetSDK.FPlayDataCallBack;
import com.usky.sas.common.hik.sdk.HCNetSDK.FRealDataCallBack_V30;
import com.usky.sas.common.hik.sdk.HCNetSDK.NET_DVR_PREVIEWINFO;
import com.usky.sas.common.hik.sdk.HCNetSDK.NET_DVR_VOD_PARA;
import com.usky.sas.common.listener.DataInitializer;
import com.usky.sas.common.util.GetIpUtils;
import com.usky.sas.common.entity.DeviceVideoInfo;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

import com.usky.sas.service.vo.VideoStreamVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class HikVideoStreamService {
    private static final Logger log = LoggerFactory.getLogger(HikVideoStreamService.class);
    static final int RTP_HEADER_SIZE = 12;
    static final int RTP_VERSION = 2;
    static final int RTP_PAYLOAD_TYPE = 96;
    static final int MAX_RTP_PACKET_SIZE = 1400;
    static Map<String, FRealDataCallBack_V30> fRealDataCallBackMap;
    static Map<String, FPlayDataCallBack> fPlayDataCallBackMap;
    static Map<String, DeviceVideoInfo> hikMediaInfo;
    static Map<String, StreamContext> streamContextMap;
    static Map<Integer, Integer> bit;

    public HikVideoStreamService() {
    }

    static byte[] createRtpPacket(byte[] data, int offset, int size, boolean marker, long timestamp, int sequenceNumber, long ssrc) {
        byte[] rtpPacket = new byte[12 + size];
        rtpPacket[0] = -128;
        rtpPacket[1] = 96;
        if (marker) {
            rtpPacket[1] |= -128;
        }

        rtpPacket[2] = (byte)(sequenceNumber >> 8);
        rtpPacket[3] = (byte)(sequenceNumber & 255);
        rtpPacket[4] = (byte)((int)(timestamp >> 24));
        rtpPacket[5] = (byte)((int)(timestamp >> 16));
        rtpPacket[6] = (byte)((int)(timestamp >> 8));
        rtpPacket[7] = (byte)((int)(timestamp & 255L));
        rtpPacket[8] = (byte)((int)(ssrc >> 24));
        rtpPacket[9] = (byte)((int)(ssrc >> 16));
        rtpPacket[10] = (byte)((int)(ssrc >> 8));
        rtpPacket[11] = (byte)((int)(ssrc & 255L));
        System.arraycopy(data, offset, rtpPacket, 12, size);
        return rtpPacket;
    }

    static byte[] createTcpPacket(byte[] rtpPacket) {
        byte[] tcpPacket = new byte[rtpPacket.length + 2];
        tcpPacket[0] = (byte)(rtpPacket.length >> 8 & 255);
        tcpPacket[1] = (byte)(rtpPacket.length & 255);
        System.arraycopy(rtpPacket, 0, tcpPacket, 2, rtpPacket.length);
        return tcpPacket;
    }

    public static VideoStreamVo getRealStreamData(String macAddr, int userID, int iChannelNo, int mediaRtpPort, Integer mediaHttpPort) {
        if (userID == -1) {
            log.info("请先注册");
            throw new BusinessException(BusinessEnum.DEVICE_NOT_LOGIN);
        } else {
            if (streamContextMap.containsKey(macAddr)) {
                ((StreamContext)streamContextMap.get(macAddr)).close();
                streamContextMap.remove(macAddr);
            }

            StreamContext context = null;

            try {
                context = new StreamContext(GetIpUtils.getServerIP(), mediaRtpPort);
                log.info("设备 {} RTP连接已建立: {}:{}", new Object[]{macAddr, GetIpUtils.getServerIP(), mediaRtpPort});
            } catch (IOException var11) {
                log.error("设备 {} 连接失败: {}", macAddr, var11.getMessage());
                throw new BusinessException("设备 " + macAddr + " 连接失败:" + var11.getMessage(), -1);
            }

            Constant.globalSsrc = Constant.globalSsrc >= 4294967295L ? 65535L : Constant.globalSsrc + 1L;
            context.ssrc = Constant.globalSsrc;
            streamContextMap.put(macAddr, context);
            FRealDataCallBack_V30 callback = (FRealDataCallBack_V30)fRealDataCallBackMap.get(macAddr);
            if (callback == null) {
                callback = new FRealDataCallBack(context, macAddr);
                fRealDataCallBackMap.put(macAddr, callback);
            }

            NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
            previewInfo.read();
            previewInfo.hPlayWnd = null;
            previewInfo.lChannel = iChannelNo;
            previewInfo.dwStreamType = 0;
            previewInfo.dwLinkMode = 0;
            previewInfo.bBlocked = 0;
            previewInfo.byProtoType = 0;
            previewInfo.write();
            int Handle = InitHCNetSDK.hCNetSDK.NET_DVR_RealPlay_V40(userID, previewInfo, (FRealDataCallBack_V30)callback, (Pointer)null);
            if (Handle == -1) {
                int iErr = InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError();
                throw new BusinessException("取流失败,错误码：" + iErr, -1);
            } else {
                log.info("取流成功，播放地址：http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
                VideoStreamVo streamVo = new VideoStreamVo();
                streamVo.setUserId(userID);
                streamVo.setHandle((long)Handle);
                streamVo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
                DeviceVideoInfo deviceVideoInfo = new DeviceVideoInfo();
                deviceVideoInfo.setSsrc(toHexString(context.ssrc));
                deviceVideoInfo.setHandle(Handle);
                deviceVideoInfo.setStreamType(1);
                deviceVideoInfo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
                deviceVideoInfo.setUserId(userID);
                hikMediaInfo.put(macAddr, deviceVideoInfo);
                return streamVo;
            }
        }
    }

    public static VideoStreamVo playBackByTime(String oldMac, String macAddr, Integer bitrate, int userID, int lChannel, int mediaRtpPort, Integer mediaHttpPort, Date beginTime, Date endTime) {
        if (StrUtil.isNotBlank(oldMac) && streamContextMap.containsKey(oldMac)) {
            ((StreamContext)streamContextMap.get(oldMac)).close();
            streamContextMap.remove(oldMac);
            fPlayDataCallBackMap.remove(oldMac);
        }

        StreamContext context = null;

        try {
            context = new StreamContext(GetIpUtils.getServerIP(), mediaRtpPort);
            log.info("设备 {} RTP连接已建立: {}:{}", new Object[]{macAddr, GetIpUtils.getServerIP(), mediaRtpPort});
        } catch (IOException var33) {
            log.error("设备 {} 连接失败: {}", macAddr, var33.getMessage());
            throw new BusinessException("设备 " + macAddr + " 连接失败: " + var33.getMessage(), -1);
        }

        String newMac = macAddr + System.currentTimeMillis();
        Constant.globalSsrc = Constant.globalSsrc >= 4294967295L ? 65535L : Constant.globalSsrc + 1L;
        context.ssrc = Constant.globalSsrc;
        streamContextMap.put(newMac, context);
        com.usky.sas.common.hik.sdk.FPlayDataCallBack playDataCallBack = new com.usky.sas.common.hik.sdk.FPlayDataCallBack(context, newMac);
        fPlayDataCallBackMap.put(newMac, playDataCallBack);
        NET_DVR_VOD_PARA net_dvr_vod_para = new NET_DVR_VOD_PARA();
        net_dvr_vod_para.dwSize = net_dvr_vod_para.size();
        net_dvr_vod_para.struIDInfo.dwChannel = lChannel;
        int beginYear = DateUtil.year(beginTime);
        int beginMonth = DateUtil.month(beginTime) + 1;
        int beginDay = DateUtil.dayOfMonth(beginTime);
        int beginHour = DateUtil.hour(beginTime, true);
        int beginMinute = DateUtil.minute(beginTime);
        int beginSecond = DateUtil.second(beginTime);
        net_dvr_vod_para.struBeginTime.dwYear = beginYear;
        net_dvr_vod_para.struBeginTime.dwMonth = beginMonth;
        net_dvr_vod_para.struBeginTime.dwDay = beginDay;
        net_dvr_vod_para.struBeginTime.dwHour = beginHour;
        net_dvr_vod_para.struBeginTime.dwMinute = beginMinute;
        net_dvr_vod_para.struBeginTime.dwSecond = beginSecond;
        int endYear = DateUtil.year(endTime);
        int endMonth = DateUtil.month(endTime) + 1;
        int endDay = DateUtil.dayOfMonth(endTime);
        int endHour = DateUtil.hour(endTime, true);
        int endMinute = DateUtil.minute(endTime);
        int endSecond = DateUtil.second(endTime);
        net_dvr_vod_para.struEndTime.dwYear = endYear;
        net_dvr_vod_para.struEndTime.dwMonth = endMonth;
        net_dvr_vod_para.struEndTime.dwDay = endDay;
        net_dvr_vod_para.struEndTime.dwHour = endHour;
        net_dvr_vod_para.struEndTime.dwMinute = endMinute;
        net_dvr_vod_para.struEndTime.dwSecond = endSecond;
        net_dvr_vod_para.hWnd = null;
        net_dvr_vod_para.write();
        int iPlayBack = InitHCNetSDK.hCNetSDK.NET_DVR_PlayBackByTime_V40(userID, net_dvr_vod_para);
        if (iPlayBack <= -1) {
            log.error("按时间回放失败，错误码为" + InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError());
            throw new BusinessException("按时间回放失败，错误码为" + InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError(), -1);
        } else {
            IntByReference intInlen = new IntByReference(0);
            boolean bCrtl = InitHCNetSDK.hCNetSDK.NET_DVR_PlayBackControl_V40((long)iPlayBack, 1, Pointer.NULL, 0, Pointer.NULL, intInlen);
            if (!bCrtl) {
                log.error("NET_DVR_PLAYSTART失败，错误码为" + InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError());
                throw new BusinessException("NET_DVR_PLAYSTART失败，错误码为" + InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError(), -1);
            } else {
                Memory memory = new Memory(4L);
                Integer i = (Integer)bit.get(bitrate);
                if (i != null) {
                    memory.setInt(0L, i);
                } else {
                    memory.setInt(0L, 2048);
                }

                InitHCNetSDK.hCNetSDK.NET_DVR_PlayBackControl_V40((long)iPlayBack, 24, memory, (int)memory.size(), Pointer.NULL, intInlen);
                InitHCNetSDK.hCNetSDK.NET_DVR_SetPlayDataCallBack_V40(iPlayBack, playDataCallBack, Pointer.NULL);
                log.info("取流成功，播放地址：http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
                VideoStreamVo streamVo = new VideoStreamVo();
                streamVo.setUserId(userID);
                streamVo.setHandle((long)iPlayBack);
                streamVo.setMac(newMac);
                streamVo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
                DeviceVideoInfo deviceVideoInfo = new DeviceVideoInfo();
                deviceVideoInfo.setSsrc(toHexString(context.ssrc));
                deviceVideoInfo.setHandle(iPlayBack);
                deviceVideoInfo.setUserId(userID);
                deviceVideoInfo.setBitrate(memory.getInt(0L));
                deviceVideoInfo.setStreamType(2);
                deviceVideoInfo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
                hikMediaInfo.put(newMac, deviceVideoInfo);
                return streamVo;
            }
        }
    }

    public static void stopRealStreamData(int PlayHandle, String macAddr) {
        if (PlayHandle == -1) {
            log.info("实时取流未开启，请先开启实时取流");
        } else if (!InitHCNetSDK.hCNetSDK.NET_DVR_StopRealPlay(PlayHandle)) {
            log.error("停止取流失败，err:" + InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError());
        } else {
            log.info("停止取流成功");
            StreamContext context = (StreamContext)streamContextMap.get(macAddr);
            if (context != null) {
                context.close();
            }

            fRealDataCallBackMap.remove(macAddr);
        }
    }

    public static void stopPlayStreamData(Long PlayHandle, String macAddr) {
        if (PlayHandle == -1L) {
            log.info("回放取流未开启，请先开启回放取流");
        } else if (!InitHCNetSDK.hCNetSDK.NET_DVR_StopPlayBack(PlayHandle)) {
            log.error("停止取流失败，err:" + InitHCNetSDK.hCNetSDK.NET_DVR_GetLastError());
        } else {
            log.info("停止取流成功");
            StreamContext context = (StreamContext)streamContextMap.get(macAddr);
            if (context != null) {
                context.close();
            }

            fPlayDataCallBackMap.remove(macAddr);
        }
    }

    public static String toHexString(long number) {
        if (number == 0L) {
            return "0";
        } else {
            StringBuilder hex = new StringBuilder();
            char[] hexChars = "0123456789ABCDEF".toCharArray();

            int zerosToAdd;
            for(long current = number; current != 0L; current >>>= 4) {
                zerosToAdd = (int)(current & 15L);
                hex.insert(0, hexChars[zerosToAdd]);
            }

            if (hex.length() < 8) {
                zerosToAdd = 8 - hex.length();

                for(int i = 0; i < zerosToAdd; ++i) {
                    hex.insert(0, '0');
                }
            }

            return hex.toString();
        }
    }

    static {
        fRealDataCallBackMap = GlobalMemoryMap.fRealDataCallBackMap;
        fPlayDataCallBackMap = GlobalMemoryMap.fPlayDataCallBackMap;
        hikMediaInfo = GlobalMemoryMap.hikVideoInfo;
        streamContextMap = GlobalMemoryMap.streamContextMap;
        bit = DataInitializer.bitrate;
    }
}

