package com.yutou.common.utils;

import com.yutou.bilibili.Tools.ProcessUtils;
import com.yutou.bilibili.interfaces.DownloadInterface;
import com.yutou.common.record.AbsVideoRecord;
import lombok.Getter;
import org.apache.commons.io.FileUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

public class FFmpegUtils extends AbsVideoRecord {
    private static final Map<String, Task> pidMap = new HashMap<>();
    private String command;
    @Getter
    private String outputFilePath;
    private String uid;
    @Getter
    private String commandDecode;
    @Getter
    private Long pid;

    public FFmpegUtils() {
    }

    @Override
    public List<String> getRoomIds() {
        pidMap.keySet().removeIf(key -> !check(key));
        return pidMap.keySet().stream().toList();
    }

    @Override
    public boolean check(String roomId) {
        try {
            if (!pidMap.containsKey(roomId)) {
                return false;
            }
            return ProcessUtils.isProcessRunning(pidMap.get(roomId).pid);
        } catch (Exception e) {
            Log.e(e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void kill(String roomId) {
        try {
            // ProcessUtils.killProcess(pidMap.get(roomId).pid);
            Task utils = pidMap.get(roomId);
            if (utils != null) {
                utils.kill();
            }
        } catch (Exception e) {
            Log.e(e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void killAll() {
        for (Task pid : pidMap.values()) {
            try {
                pid.kill();
            } catch (Exception e) {
                Log.e(e);
            }
        }
        pidMap.clear();
    }

    //  ffmpeg.exe -i '.\[2024-11-01 19-56-06]喻黎黎来卷饼啦.flv' -f segment -segment_time 30 -segment_format mpegts -segment_list ..\..\..\..\Web\video\v.m3u8 -c copy -bsf:v h264_mp4toannexb -map 0 ..\..\..\..\Web\video\course-%04d.ts
    //ffmpeg -i "[2024-10-31 16-23-48]学习柯学.flv"        -f segment -segment_time 30 -segment_format mpegts -segment_list "C:\Users\583   \video.m3u8" -c copy -bsf:v h264_mp4toannexb -map 0  "C:\Users\58381\Documents\biliob\play\course-%04d.ts"
    public static FFmpegUtils segment(String videoId, String ffmpegPath, File video, String outputPath) {
        try {
            FileUtils.deleteDirectory(new File(outputPath));
        } catch (IOException ignored) {

        }
        new File(outputPath).mkdirs();
        FFmpegUtils ffmpeg = new FFmpegUtils();
        ffmpeg.uid = videoId;
        ffmpeg.command = new Builder().withNotSymbolParam("-f", "segment")
                .withNotSymbolParam("-segment_time", "30")
                .withNotSymbolParam("-segment_format", "mpegts")
                .withParam("-segment_list", outputPath + File.separator + "video.m3u8")
                .withNotSymbolParam("-c", "copy")
                .withNotSymbolParam("-bsf:v", "h264_mp4toannexb")
                .withNotSymbolParam("-map", "0")
                .withParam("-y", outputPath + File.separator + "course-%04d.ts")
                .getCommand(ffmpegPath, video.getAbsolutePath(), null);
        ffmpeg.outputFilePath = "video.m3u8";
        ffmpeg.commandDecode= Arrays.stream(ffmpeg.command.split(" ")).map(Base64Tools::decode).collect(Collectors.joining(" "));
        return ffmpeg;
    }
// ffmpeg 切片

    //
    public static class Builder {
        private final LinkedHashMap<String, String> params = new LinkedHashMap<>();

        public Builder withParam(String key, String value) {
            if (value != null) {
                params.put(key, "\"" + value + "\"");
            }
            return this;
        }

        public Builder withNotSymbolParam(String key, String value) {
            if (value != null) {
                params.put(key, value);
            }
            return this;
        }

        private String getCommand(String ffmpegPath, String inputUrl, String outputPath) {
            StringBuilder command = new StringBuilder(Base64Tools.encode(ffmpegPath));

            command.append(" ").append(Base64Tools.encode("-re")).append(" ").append(Base64Tools.encode("-i")).append(" ").append(Base64Tools.encode("\""+inputUrl+"\""));
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if (StringUtils.hasText(entry.getKey())) {
                    command.append(" ");
                    command.append(Base64Tools.encode(entry.getKey()));
                }
                if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                    command.append(" ").append(Base64Tools.encode(entry.getValue()));
                }
            }
            if (outputPath != null) {
                command.append(" ").append(Base64Tools.encode("\""+outputPath+"\""));
            }
            return command.toString();

        }

        public FFmpegUtils build(String uid, String ffmpegPath, String inputUrl, String outputPath) {
            FFmpegUtils ffmpeg = new FFmpegUtils();
            ffmpeg.command = getCommand(ffmpegPath, inputUrl, outputPath);
            ffmpeg.commandDecode= Arrays.stream(ffmpeg.command.split(" ")).map(Base64Tools::decode).collect(Collectors.joining(" "));
            ffmpeg.outputFilePath = outputPath;
            ffmpeg.uid = uid;
            return ffmpeg;
        }
    }

    @Override
    public void start(DownloadInterface downloadInterface) {
        Task task = new Task(downloadInterface);
        task.start();
        pid= task.pid;
    }

    @Override
    public void stop() {
        pidMap.get(uid).kill();
    }

    /**
     * 获取视频时长
     *
     * @param video 视频文件
     * @return 毫秒
     */
    public static long getVideoTime(File video) {
        String ffprobe = ConfigTools.load(ConfigTools.CONFIG, "ffprobe", String.class);
        String exec = ffprobe + " -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"" + video.getAbsolutePath() + "\"";
        try {
            Log.i(exec);
            Process process = ProcessUtils.exec(exec);
            InputStream inputStream = process.getInputStream();
            byte[] bytes = new byte[2048];
            String data;
            inputStream.read(bytes);
            data = new String(bytes);
            if (!StringUtils.hasText(data)) {
                data = "0";
            }
            inputStream.close();
            process.destroy();
            process.exitValue();
            return (long) (Double.parseDouble(data) * 1000);
        } catch (Exception e) {
            return 0;
        }
    }

    private class Task extends Thread {
        private long pid;
        InputStream inputStream;
        OutputStream outputStream;
        DownloadInterface downloadInterface;

        public Task(DownloadInterface downloadInterface) {
            this.downloadInterface = downloadInterface;
        }

        @Override
        public void run() {
            super.run();
            try {
                if (check(uid)) {
                    return;
                }
                Process process = ProcessUtils.exec(command.split(" "));
                pid = process.toHandle().pid();
                Log.i("进程id " + pid);
                inputStream = process.getErrorStream();
                outputStream = process.getOutputStream();
                byte[] bytes = new byte[2048];
                if (downloadInterface != null) {
                    downloadInterface.onDownloadStart();
                }
                pidMap.put(uid, this);
                long startTimer = System.currentTimeMillis();
                ByteArrayOutputStream os=new ByteArrayOutputStream();
                int len;
                while ((len=inputStream.read(bytes)) > -1) {
                    os.write(bytes, 0, len);
                    if (downloadInterface != null) {
                        downloadInterface.onDownloading(System.currentTimeMillis() - startTimer, 0);
                    }
                }
                pidMap.remove(uid);
                //获取视频时长：ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
                //获取到结果:5372.432000
                if (downloadInterface != null) {
                    //Log.i("触发下载完成", command);
                    Log.i("触发下载完成", new String(os.toByteArray()));
                    downloadInterface.onDownload(null);
                }
            } catch (Exception e) {
                Log.e(e);
            }
        }

        public void kill() {
            try {
                outputStream.write("q".getBytes());
                outputStream.flush();
                Thread.sleep(1000);
                if (check(uid)) {
                    ProcessUtils.killProcess(pid);
                }
                pidMap.remove(uid);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}
