package com.usky.sas.common.dahua;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.usky.sas.common.global.Constant;
import com.usky.sas.common.global.StreamContext;
import com.usky.sas.common.util.GetIpUtils;
import com.usky.sas.service.vo.VideoStreamVo;
import com.usky.sas.common.exception.BusinessException;
import com.usky.sas.common.global.GlobalMemoryMap;
import com.usky.sas.common.entity.DahuaNvrInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.lang.Long.toHexString;

@Component
public class DahuaVideoStreamService {
    private static final Logger log = LoggerFactory.getLogger(DahuaVideoStreamService.class);
    static int iPlayBack;
    static final int RTP_HEADER_SIZE = 12;
    static final int RTP_VERSION = 2;
    static final int RTP_PAYLOAD_TYPE = 96;
    public static final int MAX_RTP_PACKET_SIZE = 1400;
    static Map<String, NetSDKLib.fDataCallBack> dahuaPlayDataCallBackMap;
    static Map<String, StreamContext> streamContextMap;
    static Map<String, DahuaVideoInfo> dahuaVideoInfo;

    public DahuaVideoStreamService() {
    }

    public 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;
    }

    public 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 playBackByTime(String oldMac, String macAddr, Long 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);
            dahuaPlayDataCallBackMap.remove(oldMac);
        }

        StreamContext context = null;

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

        String newMac = macAddr + System.currentTimeMillis();
        Constant.globalSsrc = Constant.globalSsrc >= 4294967295L ? 65535L : Constant.globalSsrc + 1L;
        context.ssrc = Constant.globalSsrc;
        streamContextMap.put(newMac, context);
        FPlayDataCallBackEx playDataCallBack = new FPlayDataCallBackEx(context, newMac);
        dahuaPlayDataCallBackMap.put(newMac, playDataCallBack);
        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);
        NetSDKLib.NET_TIME begin = new NetSDKLib.NET_TIME();
        begin.setTime(beginYear, beginMonth, beginDay, beginHour, beginMinute, 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);
        NetSDKLib.NET_TIME end = new NetSDKLib.NET_TIME();
        end.setTime(endYear, endMonth, endDay, endHour, endMinute, endSecond);
        NetSDKLib.NET_IN_PLAYBACK_BY_DATA_TYPE stIn = new NetSDKLib.NET_IN_PLAYBACK_BY_DATA_TYPE();
        stIn.emDataType = 1;
        stIn.nChannelID = lChannel;
        stIn.stStartTime = begin;
        stIn.stStopTime = end;
        stIn.nPlayDirection = 0;
        stIn.dwPosUser = null;
        stIn.hWnd = null;
        stIn.cbDownLoadPos = null;
        stIn.fDownLoadDataCallBack = playDataCallBack;
        stIn.fDownLoadDataCallBackEx = null;
        stIn.dwDataUser = null;
        NetSDKLib.NET_OUT_PLAYBACK_BY_DATA_TYPE stOut = new NetSDKLib.NET_OUT_PLAYBACK_BY_DATA_TYPE();
        Long lPlayHandle = InitNetSDKLib.dhNetSDK.CLIENT_PlayBackByDataType(userID, stIn, stOut, 5000);
        if (lPlayHandle != 0L) {
            log.info("回放取流成功，回放句柄：{}", lPlayHandle);
            log.info("取流成功，播放地址：" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + context.ssrc + "&type=play");
            VideoStreamVo streamVo = new VideoStreamVo();
            streamVo.setHandle(lPlayHandle);
            streamVo.setMac(newMac);
            streamVo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
            DahuaVideoInfo videoInfo = new DahuaVideoInfo();
            videoInfo.setSsrc(toHexString(context.ssrc));
            videoInfo.setUrl("http://" + GetIpUtils.getServerIP() + ":" + mediaHttpPort + "/index/api/webrtc?app=rtp&stream=" + toHexString(context.ssrc) + "&type=play");
            videoInfo.setHandle(lPlayHandle);
            dahuaVideoInfo.put(newMac, videoInfo);
            return streamVo;
        } else {
            log.info("回放取流失败，原因码：{}", InitNetSDKLib.dhNetSDK.CLIENT_GetLastError());
            throw new BusinessException("回放失败", -1);
        }
    }

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

            dahuaPlayDataCallBackMap.remove(macAddr);
        }
    }

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

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

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

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

            return hex.toString();
        }
    }

    static {
        dahuaPlayDataCallBackMap = GlobalMemoryMap.dahuaPlayDataCallBackMap;
        streamContextMap = GlobalMemoryMap.streamContextMap;
        dahuaVideoInfo = GlobalMemoryMap.dahuaVideoInfo;
    }
}
