package com.example.math.controller;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@Slf4j
public class AudioTransformProcess {
    private final Process ffmpegProcess;
    private final String threadName;

    private static int processCount;

    public AudioTransformProcess(String[] command) throws IOException {
        log.debug("initializing ffmpegProcess...");
        ProcessBuilder pb = new ProcessBuilder(command);
        this.ffmpegProcess = pb.start();
        this.threadName = Thread.currentThread().getName();
        synchronized (AudioTransformProcess.class){
            processCount++;
        }
        log.debug("ffmpegProcess initialized.Alive process count:{}", processCount);
    }

    /**
     * 获取流式地从wav或者mp3转为opus格式的指令示例,实时管道方式
     * -i:  输入文件
     * -vn: 输出文件禁用视频流
     * -ac: 设置输出文件音频通道的数量
     * -ar: 设置输出文件音频采样率
     * -acodec: 设置输出文件音频编解码器
     * -y: 无需询问即可覆盖输出文件

     * @return
     */
    public static String[] getTransformToOpusPipeCommandExample(String originFormat){
        return new String[]{"ffmpeg", "-y", "-f", originFormat, "-i", "pipe:", "-f", "opus", "pipe:"};
    }

    /**
     * 获取流式地从MP3或者wav转为pcm格式的指令示例,实时管道方式
     * -i:  输入文件
     * -vn: 输出文件禁用视频流
     * -ac: 设置输出文件音频通道的数量
     * -ar: 设置输出文件音频采样率
     * -acodec: 设置输出文件音频编解码器
     * -y: 无需询问即可覆盖输出文件

     * @return
     */
    public static String[] getAudioToPcmPipeCommandExample(){
        return new String[]{"ffmpeg","-i", "pipe:0", "-f", "s16le","-ac","1","-ar","16000", "pipe:1","-y"};
    }

    /**
     * 获取通过文件方式从wav或者mp3转为pcm格式的流指令示例
     * -i:  输入文件
     * -vn: 输出文件禁用视频流
     * -ac: 设置输出文件音频通道的数量
     * -ar: 设置输出文件音频采样率
     * -acodec: 设置输出文件音频编解码器
     * -y: 无需询问即可覆盖输出文件
     * -b:a 指定比特率
     * @param sourceFile 源格式文件
     * @return
     */
    public static String[] getAudioToPcmFileCommandExample(String sourceFile,String targetFile){//注意，ffmpeg不会创建文件夹，当不存在文件夹时不会输出到文件，也不会报错
        if((sourceFile.endsWith(".wav") || sourceFile.endsWith(".mp3"))&& targetFile.endsWith(".pcm"))
            return new String[]{"ffmpeg", "-i", sourceFile,"-acodec","pcm_s16le","-ac","1","-ar","16000","-f","s16le", targetFile,"-y"};
        else throw new RuntimeException("param invalid");
    }

    /**
     * 获取通过文件方式从opus转为wav格式的流指令示例
     * -i:  输入文件
     * -vn: 输出文件禁用视频流
     * -ac: 设置输出文件音频通道的数量
     * -ar: 设置输出文件音频采样率
     * -acodec: 设置输出文件音频编解码器
     * -y: 无需询问即可覆盖输出文件
     * @param sourceFile 源格式文件
     * @return
     */
    public static String[] getOpusToWavFileCommandExample(String sourceFile,String targetFile){//注意，ffmpeg不会创建文件夹，当不存在文件夹时不会输出到文件，也不会报错
        if(sourceFile.endsWith(".opus")&& targetFile.endsWith(".wav"))
            return new String[]{"ffmpeg", "-i", sourceFile,"-vn","-acodec","pcm_s16le","-ac","1","-ar","48000","-b:a","16k",targetFile,"-y"};
        else throw new RuntimeException("param invalid");
    }

    /**
     * 获取通过文件方式从wav或者mp3转为opus格式的流指令示例
     * -i:  输入文件
     * -vn: 输出文件禁用视频流
     * -ac: 设置输出文件音频通道的数量
     * -ar: 设置输出文件音频采样率
     * -acodec: 设置输出文件音频编解码器
     * -y: 无需询问即可覆盖输出文件
     * -b:a 指定比特率
     * @param sourceFile 源格式文件
     * @return
     */
    public static String[] getTransformToOpusFileCommandExample(String sourceFile, String targetFile){//注意，ffmpeg不会创建文件夹，当不存在文件夹时不会输出到文件，也不会报错
        if((sourceFile.endsWith(".wav")|| sourceFile.endsWith(".mp3")) && targetFile.endsWith(".opus"))
            return new String[]{"ffmpeg", "-i", sourceFile,"-vn","-acodec","libopus","-ac","1","-ar","16000","-b:a","16k",targetFile,"-y"};
        else throw new RuntimeException("param invalid");
    }

    /**
     * 抽取音频文件前n秒指令示例
     * @param sourceFile
     * @param targetFile
     * @param seconds
     * @return
     */
    public static String[] getExtractFirstSecondsCommandExample(String sourceFile,String targetFile,int seconds){//注意，ffmpeg不会创建文件夹，当不存在文件夹时不会输出到文件，也不会报错
        return new String[]{"ffmpeg", "-i", sourceFile,"-vn","-c:a", "copy", "-t", String.valueOf(seconds),targetFile,"-y"};
    }

    /**
     * 异步写入管道，由用户决定执行的线程
     * @param inputStream
     */

    public void asyncWriteToPipe(InputStream inputStream){
        String currentThreadName = Thread.currentThread().getName();
        if(currentThreadName.equals(threadName)){
            throw new RuntimeException("not async.");
        }
        try (OutputStream stdin = ffmpegProcess.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int b = 0;
            while((b = inputStream.read(buffer))!=-1) {
                stdin.write(buffer, 0, b);
                log.debug("write to pip {} B.",b);
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }  finally {
            try {
                log.debug("closing stdin pipeline...");
                ffmpegProcess.getOutputStream().close();
                log.debug("stdin closed.");
            } catch (IOException e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * 从ffmpeg管道中获取输入流，用于异步流式读取转换结果
     * @return
     */
    public InputStream getReaderFromPipe(){
        return ffmpegProcess.getInputStream();
    }

    /**
     * 等待ffmpeg命令执行结束
     * @return
     * @throws InterruptedException
     */
    public int waitFor() throws InterruptedException {
        return ffmpegProcess.waitFor();
    }

    /**
     * 等待ffmpeg命令执行结束
     * @param timeout
     * @param unit
     * @return
     * @throws InterruptedException
     */
    public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException {
        return ffmpegProcess.waitFor(timeout,unit);
    }


    /**
     * 获取存在的AudioTransformProcess对象的个数
     * @return
     */
    public int getProcessCount(){
        return processCount;
    }

    @Override
    protected void finalize() throws Throwable {
        synchronized (AudioTransformProcess.class){
            processCount--;
        }
    }

}
