package com.chuanxia.mcp.util;

import com.chuanxia.mcp.exception.ResultException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
 
/**
 * @Description  m3u8解析
 * @project
 * @author：hf
 * @date：
 */
@Slf4j
public class M3u8DownloadFactory {
 
 
    //重试次数
    private static int retryCount = 30;
 
    //链接连接超时时间（单位：毫秒）
    private static long timeoutMillisecond = 1000L;
 
    //密钥字节
    private static byte[] keyBytes = new byte[16];
 
    //自定义请求头
    private static Map<String, Object> requestHeaderMap = new HashMap<>();
 
 
    /**
     * 获取所有的ts片段下载链接
     *
     * @return 链接是否被加密，null为非加密
     */
    private static  Map getTsUrl(String url) {
        StringBuilder content = getUrlContent(url, false);
        //判断是否是m3u8链接
        if (!content.toString().contains("#EXTM3U")){
            throw new ResultException(500,url + "不是m3u8链接！");
        }
        String[] split = content.toString().split("\\n");
        String keyUrl = "";
        boolean isKey = false;
        for (String s : split) {
            //如果含有此字段，则说明只有一层m3u8链接
            if (s.contains("#EXT-X-KEY") || s.contains("#EXTINF")) {
                isKey = true;
                keyUrl = url;
                break;
            }
        }
        if (StringUtils.isEmpty(keyUrl)){
            throw new ResultException(500,"未发现key链接！");
        }
        //获取密钥
        Map key = isKey ? getKey(keyUrl, content) : getKey(keyUrl, null);
 
        return key;
    }
 
    /**
     * @Description: 判断是否是为url链接
     * @Author: hf
     * @Time: 2021/7/13 16:44
     */
    public static boolean isUrl(String str) {
        if (StringUtils.isEmpty(str)){
            return false;
        }
        str = str.trim();
        return str.matches("^(http|https)://.+");
    }
    /**
     * @Description:
     * @Return:
     * @Author: hf
     * @Time: 2021/7/13 16:44
     */
    private static String mergeUrl(String start, String end) {
        if (end.startsWith("/")){
            end = end.replaceFirst("/", "");
        }
        int position = 0;
        String subEnd, tempEnd = end;
        while ((position = end.indexOf("/", position)) != -1) {
            subEnd = end.substring(0, position + 1);
            if (start.endsWith(subEnd)) {
                tempEnd = end.replaceFirst(subEnd, "");
                break;
            }
            ++position;
        }
        return start + tempEnd;
    }
  /**
   * @Description:
   * @param url     密钥链接，如果无密钥的m3u8，则此字段可为空
   * @param content 内容，如果有密钥，则此字段可以为空
   * @Return:
   * @Author: hf
   * @Time: 2021/7/13 16:44
   */
    private static Map getKey(String url, StringBuilder content) {
        Map<String,String> map=new HashMap<String,String>();
        StringBuilder urlContent;
        if (content == null || StringUtils.isEmpty(content.toString())){
            urlContent = getUrlContent(url, false);
        }else{
            urlContent = content;
        }
        if (!urlContent.toString().contains("#EXTM3U")){
            throw new ResultException(500,url + "不是m3u8链接！");
        }
        String method=null;
        String keyUrl=null;
        String iv=null;
        String[] split = urlContent.toString().split("\\n");
        for (String s : split) {
            //如果含有此字段，则获取加密算法以及获取密钥的链接
            if (s.contains("EXT-X-KEY")) {
                String[] split1 = s.split(",");
                for (String s1 : split1) {
                    if (s1.contains("METHOD")) {
                        method = s1.split("=", 2)[1];
                        map.put("method",method);
                        continue;
                    }
                    if (s1.contains("URI")) {
                        keyUrl = s1.split("=", 2)[1];
                        continue;
                    }
                    if (s1.contains("IV")){
                        iv = s1.split("=", 2)[1];
                        map.put("iv",iv);
                    }
                }
                //只需要 key和vi 直接结束循环
                break;
            }
        }
        String aesKey=null;
        String relativeUrl = url.substring(0, url.lastIndexOf("/") + 1);
        if (!StringUtils.isEmpty(keyUrl)) {
            keyUrl = keyUrl.replace("\"", "");
            aesKey= getUrlContent(isUrl(keyUrl) ? keyUrl : mergeUrl(relativeUrl, keyUrl), true).toString().replaceAll("\\s+", "");
            map.put("aesKey",aesKey);
        }
        return map;
    }
  /**
   * @Description:
   * @param urls  http链接
   * @param isKey 这个url链接是否用于获取key
   * @Return:
   * @Author: hf
   * @Time: 2021/7/13 16:43
   */
    private static StringBuilder getUrlContent(String urls, boolean isKey) {
        int count = 1;
        HttpURLConnection httpURLConnection = null;
        StringBuilder content = new StringBuilder();
        while (count <= retryCount) {
            try {
                URL url = new URL(urls);
                httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setConnectTimeout((int) timeoutMillisecond);
                httpURLConnection.setReadTimeout((int) timeoutMillisecond);
                httpURLConnection.setUseCaches(false);
                httpURLConnection.setDoInput(true);
                for (Map.Entry<String, Object> entry : requestHeaderMap.entrySet()){
                    httpURLConnection.addRequestProperty(entry.getKey(), entry.getValue().toString());
                }
                String line;
                InputStream inputStream = httpURLConnection.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                //这个url链接是否用于获取key
                if (isKey) {
                    byte[] bytes = new byte[128];
                    int len;
                    len = inputStream.read(bytes);
                    if (len == 1 << 4) {
                        keyBytes = Arrays.copyOf(bytes, 16);
                        String aesKey= bytesTohex(keyBytes);
                        content.append(aesKey);
                    } else{
                        content.append(new String(Arrays.copyOf(bytes, len)));
                    }
                    return content;
                }
                while ((line = bufferedReader.readLine()) != null){
                    content.append(line).append("\n");
                }
                bufferedReader.close();
                inputStream.close();
                //log.info(content.toString());
                break;
            } catch (Exception e) {
                log.error("第" + count + "获取链接重试！\t" + urls+";"+e.getMessage());
                count++;
            } finally {
                if (httpURLConnection != null) {
                    httpURLConnection.disconnect();
                }
            }
        }
        if (count > retryCount){
            throw new ResultException(500,"连接超时！");
        }
        return content;
     }
    /**
     * @Description: 转16进制
     * @Author: hf
     * @Time: 2021/7/13 16:35
     */
    public static  String bytesTohex(byte[] bytes) {
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            boolean flag = false;
            if(b < 0){
                flag = true;
            }
            int absB = Math.abs(b);
            if(flag){
                absB = absB | 0x80;
            }
           // System.out.println(absB & 0xFF);
            String tmp = Integer.toHexString(absB & 0xFF);
            //转化的十六进制不足两位，需要补0
            if (tmp.length() == 1) {
                hex.append("0");
            }
            hex.append(tmp.toUpperCase());
            if(i!=bytes.length-1){
                hex.append(" ");
            }
        }
        return hex.toString();
    }
    public static void main(String[] args) {
        String url="xxxx.m3u8";
        System.out.println("getTsUrl:"+getTsUrl(url));
    }
    /**
     * @Description: 获取16进制随机数
     * @Author: hf
     * @Time: 2021/7/13 10:49
     */
    public static String randomHexString(int len) {
        try {
            StringBuffer result = new StringBuffer();
            for (int i = 1; i <= len; i++) {
                result.append(Integer.toHexString(new Random().nextInt(16)));
                if(i%2==0 && i!=len){
                    result.append(" ");
                }
            }
            return result.toString().toUpperCase();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }
}