package com.persagy.cameractl.service;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.persagy.cameractl.conf.AllStaticConfig;
import com.persagy.cameractl.service.windows.DpsdkMainWindows;
import com.persagy.cameractl.utils.Camera;
import com.persagy.cameractl.utils.CmdStreamThread;
import com.persagy.cameractl.utils.JsonTools;
import com.persagy.cameractl.utils.OtherTools;
import com.persagy.cameractl.utils.ResultClass;
import com.persagy.cameractl.utils.SdkClient;
import com.persagy.socket.IotSocketClient;

import cn.hutool.core.date.DateUtil;

public class DahCameraExecuteApi {
    Logger logger = LoggerFactory.getLogger(DahCameraExecuteApi.class);

    // http请求超时时间 单位都是毫秒 设置为15分钟
    static int timeOut = 1000 * 60 * 15;

    // token 大华8900平台上的超时时间为30分钟，所以此处设置为28分钟
    static String publickToken = null;
    // 用户ID
    static String userId = null;
    // 当前token的创建时间，毫秒数
    static long tokenCreateTime = 0;
    // token过期时间
    static long tokenExpires = 1000 * 60 * 28;

    /*
     * 检测token是否过期
     * */
    private boolean validTokenExpires() {
        if (publickToken == null)
            return true;
        long currTime = System.currentTimeMillis();
        if (currTime - tokenCreateTime >= tokenExpires)
            return true;
        return false;
    }

    private boolean login() {
        try {
            publickToken = null;
            userId = null;
            tokenCreateTime = 0;

            String ip = AllStaticConfig.dahuaApiIp;
            String port = AllStaticConfig.dahuaApiPort;
            String userName = AllStaticConfig.dahuaApiKey;
            String password = AllStaticConfig.dahuaApiSecret;
            Map<String, String> headMap = new HashMap<String, String>();
            headMap.put("Content-Type", "application/json;charset=UTF-8");
            String responseString = SdkClient.login(ip, port, userName, password, headMap);
            ResultClass resultClass = parseResult(responseString);
            if (resultClass.name) {
                String jsonDataStr = JsonTools.obj2Str(resultClass.resultData);
                Map<String, Object> dataMap = JsonTools.str2Map(jsonDataStr);
                publickToken = String.valueOf(dataMap.get("token"));
                userId = String.valueOf(dataMap.get("id"));
                tokenCreateTime = System.currentTimeMillis();
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("登录失败：", e);
            return false;
        }
    }

    /*
     * 拼接请求url，resetful风格的请求
     * */
    private String joinUrl(String urlPrefix, String urlParamStr) {
        String hostString = AllStaticConfig.dahuaApiIp + ":" + AllStaticConfig.dahuaApiPort;
        return hostString.equals("") ? ""
                : "http://" + hostString + "/" + urlPrefix
                + (urlParamStr == null || urlParamStr.equals("") ? "" : "?" + urlParamStr);
    }

    /*
     * 解析返回结果
     * */
    private ResultClass parseResult(String responseString) {
        try {
            Map<String, Object> resultMap = JsonTools.str2Map(responseString);

            ResultClass resultClass = new ResultClass();
            resultClass.name = String.valueOf(resultMap.get("success")).equals("true") ? true : false;
            if (resultClass.name)
                resultClass.resultData = resultMap;
            else
                resultClass.reason = String.valueOf(resultMap.get("errMsg"));
            return resultClass;
        } catch (Exception e) {
            logger.error("解析返回结果出错:", e);
            return new ResultClass(false, "解析返回结果出错");
        }
    }

    /*
     * 构造参数string
     * */
    private String getParamString(Map<String, Object> paramMap) {
        try {
            return JsonTools.obj2Str(paramMap);
        } catch (Exception e) {
            logger.error("转换参数错误：", e);
            return null;
        }
    }

    /*
     * 根据传入的url来请求对应的8900平台上的接口
     * */
    public ResultClass requestAll(Camera dahCamera) {
        try {
            dahCamera.url = dahCamera.url == null ? "" : dahCamera.url;
            if (dahCamera.url == null || "".equals(dahCamera.url) || dahCamera.paramStr == null
                    || "".equals(dahCamera.paramStr))
                return new ResultClass(false, "缺少参数");
            // paramStr 传给海康开放平台接口的参数，格式：{_type:"get | post",interfaceId:"可为空",isToken:"1 |
            // 0"}
            Map<String, Object> paramMap = JsonTools.str2Map(dahCamera.paramStr);
            String httpMethodType = String.valueOf(paramMap.get("_type"));
            httpMethodType = httpMethodType == null || "".equals(httpMethodType) ? "post" : httpMethodType;
            // 是否需要token 1 需要 0 不需要
            String isToken = String.valueOf(paramMap.get("isToken"));
            isToken = isToken == null || "".equals(isToken) ? "1" : isToken;
            String interfaceId = String.valueOf(paramMap.get("interfaceId"));

            String urlParamStr = null;
            // 需要token时，先验证token是否过期，过期的话先登录
            switch (isToken) {
                case "1":
                    boolean tokenIsExpires = validTokenExpires();
                    if (tokenIsExpires) {
                        boolean loginResult = login();
                        if (!loginResult)
                            return new ResultClass(false, "登录失败");
                    }
                    urlParamStr = "userId=" + userId + "&userName=" + AllStaticConfig.dahuaApiKey + "&token="
                            + publickToken;
                    break;
                default:
                    break;
            }

            String reqUrl = joinUrl(dahCamera.url, urlParamStr);
            Map<String, String> headMap = new HashMap<String, String>();
            headMap.put("content-Type", "application/json");

            Map<String, Object> realParamMap = new HashMap<String, Object>();
            realParamMap.put("interfaceId", interfaceId);
            realParamMap.put("jsonParam", dahCamera.jsonParam);
            String realParamStr = getParamString(realParamMap);

            String responseString = httpMethodType.equals("post") ? SdkClient.post(reqUrl, realParamStr, headMap)
                    : SdkClient.get(reqUrl, headMap);

            ResultClass resultClass = new ResultClass();
            Map<String, Object> resultMap = JsonTools.str2Map(responseString);
            resultClass.name = true;
            resultClass.resultData = resultMap.get("data");
            return resultClass;
        } catch (Exception e) {
            String errStr = "执行" + dahCamera.url + "异常：";
            logger.error(errStr, e);
            return new ResultClass(false, errStr);
        }
    }

    /*
     * 根据监控点获取预览流地址
     */
    public ResultClass GetCameraPreviewURL(Camera dahCamera) {
        logger.info("调用大华平台预览接口start,入参 = " + dahCamera);
        long start = System.currentTimeMillis();
        try {
            /*
             * http://[服务IP]:[服务端口]/live/cameraid/[摄像头ID]/substream/[码流类型].m3u8
             * [摄像头ID]：请参看大华标准URL文档说明，一般型如：1000000%40001%240 1000000代表设备编号，001代表级联号，
             * 两者间以“%40”即“@”分隔 %24代表符号“$”，后面的0代表通道1（从0开始） [码流类型]：1表示主码流，2表示辅码流
             */
            ResultClass resultClass = new ResultClass();
            /*
             * cameraIndexCode为视频通道编码，格式如下：
             * 1000001$1$0$0(设备编号$通道类型$单元序号$通道序号),在用于HLS或者RTSP拉流的时候,只需要截取 1000000$0(设备编号$通道序号)即可,其中$需要转义为%24
             * */
            if (dahCamera.cameraIndexCode == null || dahCamera.cameraIndexCode.equals("")) {
                resultClass.name = false;
                resultClass.reason = "缺少参数";
                logger.error("调用大华平台预览接口error,入参 = " + dahCamera + "错误原因:缺少参数");
                return resultClass;
            }
            // 取得设备编号和通道序号
            String[] arrCode = dahCamera.cameraIndexCode.split("\\$");
            if (arrCode.length < 4) {
                resultClass.name = false;
                resultClass.reason = "非法的视频通道编码";
                logger.error("调用大华平台预览接口error,入参 = " + dahCamera + "错误原因:非法的视频通道编码");
                return resultClass;
            }
            String cameraCode = arrCode[0];
            String channelNo = arrCode[3];

            String host = AllStaticConfig.dahuaMesHost;
            // 大华的码流类型：1 主码流 2 子码流 3 其他码流
            int streamType = dahCamera.streamType == 0 ? 1 : dahCamera.streamType == 1 ? 2 : 3;
            String levelStr = dahCamera.levelNo == null || dahCamera.levelNo.equals("") ? ""
                    : "%40" + dahCamera.levelNo;

            String cameraId = cameraCode + levelStr + "%24" + channelNo;
            String playUrl = "http://" + host + "/live/cameraid/" + cameraId + "/substream/" + streamType + ".m3u8";

            Map<String, String> map = new HashMap<String, String>();
            map.put("url", playUrl);

            resultClass.name = true;
            resultClass.resultData = map;
            logger.info("调用大华平台预览接口end,入参 = " + dahCamera +
                    "返回参数 = " + resultClass +
                    "耗时 = " + (System.currentTimeMillis() - start) + "ms");
            return resultClass;
        } catch (Exception e) {
            String errStr = "大华拼接实时预览url出错";
            ResultClass resultClass = new ResultClass(false, errStr);
            logger.error("调用大华平台预览接口error,入参 = " + dahCamera +
                    "返回参数 = " + resultClass +
                    "耗时 = " + (System.currentTimeMillis() - start) + "ms");
            return resultClass;
        }

    }

    /*
     * 云台控制,调用DSS平台的SDK
     * */
    public ResultClass ptzControl(Camera dahCamera) {
        // cameraIndexCode(视频通道编码,例1000000$1$0$0)、command(控制命令)、speed(速度，值范围1~8，默认8)
        if (dahCamera.cameraIndexCode == null || "".equals(dahCamera.cameraIndexCode) || dahCamera.command == null
                || "".equals(dahCamera.command)) {
            logger.info("调用云台控制sdk失败，入参=" + dahCamera + "返回参数:缺少参数");
            return new ResultClass(false, "缺少参数");
        }
        return new DpsdkMainWindows(dahCamera).controlMain();
    }

    /*
     * 根据监控点获取回放流地址
     */
    public ResultClass GetCameraPlaybackURL(Camera dahCamera, boolean isHls) {
        logger.info("调用大华视频回放接口，入参 = " + dahCamera);
        long start = System.currentTimeMillis();
        try {
            /*
             * http://[服务IP]:[服务端口]/vod/center/cameraid/[摄像头ID]/substream/[码流类型]/totallength/[实际总录像时长]/
             * 		begintime/[开始时间]/endtime/[结束时间].m3u8
             */
            ResultClass resultClass = new ResultClass();
            /*
             * cameraIndexCode为视频通道编码，格式如下：
             * 1000001$1$0$0(设备编号$通道类型$单元序号$通道序号),在用于HLS或者RTSP拉流的时候,只需要截取 1000000$0(设备编号$通道序号)即可,其中$需要转义为%24
             * */
            if (dahCamera.cameraIndexCode == null || dahCamera.cameraIndexCode.equals("")
                    || dahCamera.startDateStr == null || dahCamera.startDateStr.equals("")
                    || dahCamera.endDateStr == null || dahCamera.endDateStr.equals("")) {
                resultClass.name = false;
                resultClass.reason = "缺少参数";
                return resultClass;
            }

            Date startDate = DateUtil.parse(dahCamera.startDateStr);
            long startMillSeconds = startDate.getTime();
            long startSeconds = startMillSeconds / 1000;

            Date endDate = DateUtil.parse(dahCamera.endDateStr);
            long endMillSeconds = endDate.getTime();
            long endSeconds = endMillSeconds / 1000;
            // 两者之间相差的秒数
            //long between = DateUtil.between(startDate, endDate, DateUnit.SECOND, true);

            // 取得设备编号和通道序号
            String[] arrCode = dahCamera.cameraIndexCode.split("\\$");
            if (arrCode.length < 4) {
                resultClass.name = false;
                resultClass.reason = "非法的视频通道编码";
                return resultClass;
            }
            String cameraCode = arrCode[0];
            String channelNo = arrCode[3];

            String host = AllStaticConfig.dahuaMesHost;
            // 大华的码流类型：1 主码流 2 子码流 3 其他码流
            int streamType = dahCamera.streamType == 0 ? 1 : dahCamera.streamType == 1 ? 2 : 3;
            String levelStr = dahCamera.levelNo == null || dahCamera.levelNo.equals("") ? ""
                    : "%40" + dahCamera.levelNo;

            String cameraId = cameraCode + levelStr + "%24" + channelNo;

            // 总时长怎么获取，单位秒:邮件回复说用结束时间减去开始时间
            long totalLength = endSeconds - startSeconds;

            /*
             * http://[服务IP]:[服务端口]/vod/center/cameraid/[摄像头ID]/substream/[码流类型]/totallength/[实际总录像时长]/
             * 		begintime/[开始时间]/endtime/[结束时间].m3u8
             */
            String playBackUrl = "http://" + host + "/vod/device/cameraid/" + cameraId + "/substream/" + streamType
                    + "/totallength/" + totalLength + "/begintime/" + startSeconds + "/endtime/" + endSeconds + ".m3u8";

            logger.info("调用大华视频回放接口，视频回放地址拼接完成，url = " + playBackUrl);
            if (isHls) {
    			Map<String, String> map = new HashMap<String, String>();
    			String replace = playBackUrl.replace("%24", "$");
    			map.put("videoUrl", replace);
    			resultClass.name = true;
    			resultClass.resultData = map;
    			return resultClass;
			}

            /*
             * 抓取hls转成mp4
             * */
            String playFilePath = OtherTools.getVideoFilePath();
            if (playFilePath == null || playFilePath.equals("")) {
                logger.error("调用大华视频回放接口 error,返回信息 = 回放文件名称生成失败");
                return new ResultClass(false, "回放文件名称生成失败");
            }
            
            int retCode = -1;
            Process p = null;
            CmdStreamThread cmdStreamThread = null;
            try {
                Runtime run = Runtime.getRuntime();
                if ("h265".equals(dahCamera.streamFormat)) {
                	p = run.exec("ffmpeg -i \"" + playBackUrl + "\" -map 0 -c:v libx264 -crf 28 -vf format=yuv420p -c:a copy -y \"" + playFilePath + "\"");
				} else {
					p = run.exec("ffmpeg -i \"" + playBackUrl + "\" -c copy -y \"" + playFilePath + "\"");
				}

                // 读取标准输入流、输出流，防止进程阻塞
                cmdStreamThread = new CmdStreamThread(p.getInputStream(), p.getErrorStream());
                retCode = p.waitFor();
                //retCode = p.waitFor(between, TimeUnit.SECONDS);
                p.destroy();

            } catch (Exception e) {

                logger.error("调用大华视频回放接口，视频流转mp4 error,系统报错信息:" + e +
                        "耗时" + (System.currentTimeMillis() - start) + "ms");
                return new ResultClass(false, "ffmpeg抓取rtsp转为mp4时失败");
            }
            if (retCode == 0) {
            //if (retCode) {
            	// 正常转换结束
                File mp4File = new File(playFilePath);
                String playFileName = mp4File.getName();
                String token = OtherTools.getMp4NamePrefix(playFileName);
                String url = OtherTools.playMp4RootUrl + token;
                Map<String, String> dataMap = new HashMap<String, String>();
                dataMap.put("url", url);
                dataMap.put("hls", playBackUrl);
                logger.info("调用大华视频回放接口，视频流转mp4 success,返回信息 = " + dataMap.toString() +
                        "耗时" + (System.currentTimeMillis() - start) + "ms");
                return new ResultClass(true, dataMap);
            } else {
                String errStr = cmdStreamThread.errString != null ? cmdStreamThread.errString
                        : cmdStreamThread.outString;
                logger.error("调用大华视频回放接口，视频流转mp4 error ,返回信息 = " + errStr +
                        "耗时" + (System.currentTimeMillis() - start) + "ms");
                return new ResultClass(false, errStr);
            }

        } catch (Exception e) {
            String errStr = "大华拼接实时预览url出错";
            logger.error("调用大华视频回放接口，拼接实时预览url出错,返回信息 = " + errStr +
                    "耗时" + (System.currentTimeMillis() - start) + "ms");
            ResultClass resultClass = new ResultClass(false, errStr);
            return resultClass;
        }

    }

    /*
     * 获取电视墙列表
     */
    public ResultClass GetTvWallList(Camera dahCamera) {
        try {
            DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
            return dpsdkMainWindows.getTvWallListMain();
        } catch (Exception e) {
            String errStr = "获取电视墙列表时出错";
            logger.error(errStr + ":", e);
            ResultClass resultClass = new ResultClass(false, errStr);
            return resultClass;
        }
    }

    /*
     * 获取电视墙布局列表
     */
    public ResultClass GetTvWallLayoutList(Camera dahCamera) {
        try {
            dahCamera.tvWallId = dahCamera.tvWallId == -1 ? Integer.parseInt(AllStaticConfig.dahuaTvWallId)
                    : dahCamera.tvWallId;
            DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
            return dpsdkMainWindows.getTvWallLayoutListMain();
        } catch (Exception e) {
            String errStr = "获取电视墙布局列表时出错";
            logger.error(errStr + ":", e);
            ResultClass resultClass = new ResultClass(false, errStr);
            return resultClass;
        }
    }

    /*开窗*/
    public ResultClass kaichuang(Camera dahCamera) {
        DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
        int tvWallId = Integer.parseInt(AllStaticConfig.dahuaTvWallId);
        int screenId = Integer.parseInt(AllStaticConfig.dahuaTvScreenId);
        ResultClass resultClass = dpsdkMainWindows.kaichuang(tvWallId, screenId);
        return resultClass;
    }

    /*设置窗口视频源*/
    public ResultClass setWindowSource(Camera dahCamera) {
        DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
        int tvWallId = Integer.parseInt(AllStaticConfig.dahuaTvWallId);
        int screenId = Integer.parseInt(AllStaticConfig.dahuaTvScreenId);
        ResultClass resultClass = dpsdkMainWindows.setWindowSource(tvWallId, screenId);
        return resultClass;
    }

    /*投屏*/
    public ResultClass touP(Camera dahCamera) {
    	if ("Pj3101150010".equals(AllStaticConfig.projectId)) {
    		try {
    			IotSocketClient.sendMessage("C611432002BB");
    			Thread.sleep(500);
    			IotSocketClient.sendMessage("C611432011BB");
    			return OtherTools.executeSuccess("投屏成功");
			} catch (Exception e) {
				e.printStackTrace();
			}
    		return OtherTools.executeErr("投屏失败");
		}
        DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
        int tvWallId = Integer.parseInt(AllStaticConfig.dahuaTvWallId);
        int screenId = Integer.parseInt(AllStaticConfig.dahuaTvScreenId);
        ResultClass resultClass = dpsdkMainWindows.touP(tvWallId, screenId);
        return resultClass;
    }

    /*退出投屏*/
    public ResultClass exitTouP(Camera dahCamera) {
    	if ("Pj3101150010".equals(AllStaticConfig.projectId)) {
    		try {
    			IotSocketClient.sendMessage("C611432003BB");
    			Thread.sleep(500);
    			IotSocketClient.sendMessage("C611432011BB");
    			return OtherTools.executeSuccess("退出大屏成功");
			} catch (Exception e) {
				e.printStackTrace();
			}
    		return OtherTools.executeErr("退出大屏失败");
		}
    	
        DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
        int tvWallId = Integer.parseInt(AllStaticConfig.dahuaTvWallId);
        int screenId = Integer.parseInt(AllStaticConfig.dahuaTvScreenId);
        ResultClass resultClass = dpsdkMainWindows.exitToup(tvWallId, screenId);
        return resultClass;
    }

    /*
     * 轮巡
     * */
    public ResultClass dapLx(Camera dahCamera) {
        DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
        return dpsdkMainWindows.lunx();
    }

    /*
     * 获取所有组织树串
     * */
    public ResultClass getGroupTree(Camera dahCamera) {
        DpsdkMainWindows dpsdkMainWindows = new DpsdkMainWindows(dahCamera);
        return dpsdkMainWindows.getGroupTree();
    }

}
