package com.yanzuoguang.media;

import com.yanzuoguang.util.helper.HttpHelper;
import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.thread.ThreadHelper;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * m3mu文件下载
 *
 * @author 颜佐光
 */
public class HlsDownloader {
    /**
     * 匹配ts文件
     */
    private static Pattern pattern = Pattern.compile(".*ts");
    /**
     * 源地址
     */
    private String serverUrl = StringHelper.EMPTY;
    /**
     *
     */
    private String localUrl = StringHelper.EMPTY;
    /**
     * 线程数量
     */
    private int threadCount;
    /**
     * 下载ts的文件数量
     */
    private int maxTs;
    /**
     * 服务器url路径
     */
    private String tempServerPath;

    public HlsDownloader(String serverUrl, String localUrl) {
        this(serverUrl, localUrl, 5, -1);
    }

    public HlsDownloader(String serverUrl, String localUrl, int threadCount, int maxTs) {
        if (StringHelper.isEmpty(localUrl)) {
            return;
        }
        this.serverUrl = serverUrl;
        this.localUrl = localUrl;
        this.threadCount = threadCount;
        this.maxTs = maxTs;
    }

    /**
     * 开启下载
     *
     * @param isAsync  是否异步下载
     * @param removeTs 删除ts临时文件
     * @return
     * @throws Exception
     */
    public String download(boolean isAsync, boolean removeTs) throws Exception {
        // 处理临时服务器路径
        tempServerPath = StringHelper.left(this.serverUrl, this.serverUrl.length() - new File(this.serverUrl).getName().length());
        // 用于判断url是否已经存在
        HashMap<String, Integer> urlHas = new HashMap<>();
        // 待下载的数组
        HashMap<Integer, String> waitDown = new HashMap<>();
        // 已下载数组文件列表
        HashMap<Integer, String> hasDown = new HashMap<>();
        // 开启线程
        if (isAsync) {
            for (int i = 0; i < threadCount; i++) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 循环下载
                        while (isDown(hasDown)) {
                            // 下载片段
                            downLoadIndexFile(waitDown, hasDown, hasDown.size());
                            // 等到100ms
                            if (waitDown.isEmpty()) {
                                ThreadHelper.sleep(100);
                            }
                        }
                    }
                });
                thread.start();
            }
        }
        do {
            // 添加m3mu到待下载目录
            addWaitDown(urlHas, waitDown, hasDown);
            // 同步或者异步下载
            if (isAsync) {
                // 等待500毫秒
                Thread.sleep(500);
            } else {
                downLoadIndexFile(waitDown, hasDown, hasDown.size());
            }
        } while (isDown(hasDown));
        // 组合下载的ts文件为视频
        composeFile(hasDown, removeTs);
        // 下载后的本地文件地址
        return this.localUrl;
    }

    private boolean isDown(HashMap<Integer, String> hasDown) {
        return hasDown.size() <= this.maxTs || this.maxTs < 0;
    }

    /**
     * 添加m3mu到待下载目录
     *
     * @param urlPos   用于判断url是否已经存在
     * @param waitDown 待下载的数组
     * @param hasDown  已下载数组文件列表
     * @throws MalformedURLException
     */
    private void addWaitDown(HashMap<String, Integer> urlPos, HashMap<Integer, String> waitDown, HashMap<Integer, String> hasDown) throws MalformedURLException {
        // 当没有需要下载的时候,则继续读取列表下载
        if (waitDown.isEmpty()) {
            // 解析的索引文件内容
            String m3muTsList = getIndexFile();
            // 解析成 *.ts 的文件列表
            List<String> urlList = analysisIndex(m3muTsList);
            // 添加到待下载目录中
            for (int i = 0; i < urlList.size(); i++) {
                String url = urlList.get(i);
                // 处理url
                url = tempServerPath + url;
                // 判断url是否存在
                if (urlPos.containsKey(url)) {
                    continue;
                }
                // 已经下载的ts包含的url数量
                int totalSize = waitDown.size() + hasDown.size();
                // 写入url
                urlPos.put(url, totalSize);
                waitDown.put(totalSize, url);
            }
        }

    }

    /**
     * 下载索引文件
     *
     * @return 返回索引文件的呢日哦嗯
     * @throws Exception
     */
    private String getIndexFile() throws MalformedURLException {
        StringBuilder content = new StringBuilder();
        URL url = new URL(this.serverUrl);
        //下载资源
        try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) {
            String line;
            while ((line = in.readLine()) != null) {
                content.append(line);
                content.append("\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content.toString();
    }

    /**
     * 解析索引文件
     *
     * @param content m3mu文件内容
     * @return
     * @throws Exception
     */
    private List<String> analysisIndex(String content) {
        List<String> list = new ArrayList<String>();
        Matcher ma = pattern.matcher(content);
        while (ma.find()) {
            list.add(ma.group());
        }
        return list;
    }

    /**
     * 下载视频片段
     *
     * @return
     */
    private void downLoadIndexFile(HashMap<Integer, String> waitDown, HashMap<Integer, String> hasDown, int pos) {
        // 获取源文件地址
        String serverTsUrl;
        synchronized (waitDown) {
            serverTsUrl = waitDown.remove(pos);
        }
        if (StringHelper.isEmpty(serverTsUrl)) {
            return;
        }
        // 写入生成地址到文件列表
        try {
            // 获取生成的地址
            String localTs = this.localUrl + "." + pos + ".ts";
            // 设置文件下载成功
            hasDown.put(pos, localTs);
            // 下载服务器文件
            HttpHelper.downToLocal(serverTsUrl, localTs);
        } catch (Exception e) {
            // 判断文件是否下载失败
            e.printStackTrace();
        }
    }

    /**
     * 视频片段合成
     *
     * @param hasDown 已经下载的文件段列表
     * @return
     * @throws Exception
     */
    private void composeFile(HashMap<Integer, String> hasDown, boolean removeTs) throws Exception {
        if (hasDown.isEmpty()) {
            return;
        }
        File fileTo = new File(this.localUrl);
        if (hasDown.size() == 1 && removeTs) {
            // 获取第一个 ts 文件
            File file = new File(hasDown.get(0));
            file.renameTo(fileTo);
        } else {
            // 订单输出文件流
            try (FileOutputStream fileOutputStream = new FileOutputStream(fileTo)) {
                // 遍历已经下载的ts文件
                for (int i = 0; i < hasDown.size(); i++) {
                    // 获取 ts 文件
                    File file = new File(hasDown.get(i));
                    if (!file.exists()) {
                        continue;
                    }
                    // 读取文件
                    try (FileInputStream fis = new FileInputStream(file)) {
                        // 定义文件缓存
                        byte[] bytes = new byte[1024];
                        // 读取的文件长度
                        int length = 0;
                        // 读取来源文件
                        while ((length = fis.read(bytes)) != -1) {
                            // 写入目标文件
                            fileOutputStream.write(bytes, 0, length);
                        }
                    }
                    // 删除ts文件
                    if (removeTs) {
                        file.delete();
                    }
                }
            }
        }
    }


}