/*
 *    Copyright  2017 Denis Kokorin
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 */

package com.github.kokorin.jaffree;

import com.github.kokorin.jaffree.result.FFmpegResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public class FFmpeg extends Executable<FFmpegResult> {
    private List<Input> inputs;
    private List<Output> outputs;
    private List<Option> additionalOptions;
    private boolean overwriteOutput;
    private ProgressListener progressListener;
    //-progress url (global)
    //-filter_threads nb_threads (global)
    //-debug_ts (global)
    private FilterGraph complexFilter;

    private static final Logger LOGGER = LoggerFactory.getLogger(FFmpeg.class);

    public FFmpeg(Path executablePath) {
        super(executablePath, true);
    }

    public FFmpeg addInput(Input input) {
        if (inputs == null) {
            inputs = new ArrayList<>();
        }

        inputs.add(input);

        return this;
    }

    public FFmpeg addInput(Option option) {
        if (additionalOptions == null) {
            additionalOptions = new ArrayList<>();
        }

        additionalOptions.add(option);

        return this;
    }

    public FFmpeg setComplexFilter(FilterGraph graph) {
        this.complexFilter = graph;
        return this;
    }

    public FFmpeg addOutput(Output output) {
        if (outputs == null) {
            outputs = new ArrayList<>();
        }

        outputs.add(output);

        return this;
    }


    /**
     * Whether to overwrite or to stop. False by default.
     * @param overwriteOutput true if forcibly overwrite, false if to stop
     * @return this
     */
    public FFmpeg setOverwriteOutput(boolean overwriteOutput) {
        this.overwriteOutput = overwriteOutput;
        return this;
    }

    public FFmpeg setProgressListener(ProgressListener progressListener) {
        this.progressListener = progressListener;
        return this;
    }

    @Override
    protected List<Option> buildOptions() {
        List<Option> result = new ArrayList<>();

        if (inputs != null) {
            for (Input input : inputs) {
                List<Option> inputOptions = input.buildOptions();
                if (inputOptions != null) {
                    result.addAll(inputOptions);
                }
            }
        }

        if (overwriteOutput) {
            //Overwrite output files without asking.
            result.add(new Option("-y"));
        } else {
            // Do not overwrite output files, and exit immediately if a specified output file already exists.
            result.add(new Option("-n"));
        }

        if (complexFilter != null) {
            result.add(new Option("-filter_complex", complexFilter.getValue()));
        }

        if (additionalOptions != null) {
            result.addAll(additionalOptions);
        }

        if (outputs != null) {
            for (Output output : outputs) {
                List<Option> outputOptions = output.buildOptions();
                if (outputOptions != null) {
                    result.addAll(outputOptions);
                }
            }
        }


        return result;
    }

    @Override
    protected FFmpegResult parseStdOut(InputStream stdOut) {
        //just read stdOut fully
        BufferedReader reader = new BufferedReader(new InputStreamReader(stdOut));
        String line;
        FFmpegResult result = null;

        try {
            while ((line = reader.readLine()) != null) {
                LOGGER.info(line);
                if (progressListener != null) {
                    FFmpegProgress progress = FFmpegProgress.fromString(line);
                    if (progress != null) {
                        progressListener.onProgress(progress);
                        continue;
                    }
                }

                FFmpegResult possibleResult = FFmpegResult.fromString(line);

                if (possibleResult != null) {
                    result = possibleResult;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return result;
    }

    @Override
    protected void parseStdErr(InputStream stdErr) throws Exception {
        throw new RuntimeException("Error output stream should be redirected to standard output");
    }

    public static FFmpeg atPath(Path pathToDir) {
        String os = System.getProperty("os.name");
        if (os == null) {
            throw new RuntimeException("Failed to detect OS");
        }

        Path executable;
        if (os.toLowerCase().contains("win")) {
            executable = pathToDir.resolve("ffmpeg.exe");
        } else {
            executable = pathToDir.resolve("ffmpeg");
        }

        return new FFmpeg(executable);
    }
}

@@ -1038,24 +1038,30 @@
     private 
+final 
 List%3CInput%3E 
@@ -1062,24 +1062,44 @@
 nput%3E inputs
+ = new ArrayList%3C%3E()
 ;%0A    privat
@@ -1096,24 +1096,30 @@
     private 
+final 
 List%3COutput%3E
@@ -1126,16 +1126,36 @@
  outputs
+ = new ArrayList%3C%3E()
 ;%0A    pr
@@ -1160,16 +1160,22 @@
 private 
+final 
 List%3COpt
@@ -1196,16 +1196,36 @@
 lOptions
+ = new ArrayList%3C%3E()
 ;%0A    pr
@@ -1650,89 +1650,8 @@
 ) %7B%0A
-        if (inputs == null) %7B%0A            inputs = new ArrayList%3C%3E();%0A        %7D%0A%0A
     
@@ -1669,25 +1669,24 @@
 add(input);%0A
-%0A
         retu
@@ -1726,133 +1726,31 @@
  add
-Input(Option option) %7B%0A        if (additionalOptions == null) %7B%0A            additionalOptions = new ArrayList%3C%3E();%0A        %7D%0A
+Option(Option option) %7B
 %0A   
@@ -1785,17 +1785,16 @@
 ption);%0A
-%0A
         
@@ -1982,91 +1982,8 @@
 ) %7B%0A
-        if (outputs == null) %7B%0A            outputs = new ArrayList%3C%3E();%0A        %7D%0A%0A
     
@@ -2007,17 +2007,16 @@
 utput);%0A
-%0A
         

Fix typo in FFmpeg method