import io.appium.java_client.android.AndroidDriver;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.ProcessBuilder.Redirect;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringJoiner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class Edition069_Audio_Capture {
    protected AndroidDriver driver;

    @Before
    public void setUp() throws MalformedURLException {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability("platformName", "Android");
        capabilities.setCapability("platformVersion", "9");
        capabilities.setCapability("deviceName", "Android Emulator");
        capabilities.setCapability("browserName", "Chrome");

        driver = new AndroidDriver<>(new URL("http://localhost:4723/wd/hub"), capabilities);
    }

    @After
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    public void testAudioCapture() throws Exception {
        WebDriverWait wait = new WebDriverWait(driver, 10);

        // navigate to band homepage
        driver.get("http://www.splendourhyaline.com");

        // click the amazon store icon for the first album
        WebElement store = wait.until(ExpectedConditions.presenceOfElementLocated(
            By.cssSelector("img[src='/img/store-amazon.png']")
        ));
        driver.executeScript("window.scrollBy(0, 100);");
        store.click();

        // start playing a sample of the first track
        WebElement play = wait.until(ExpectedConditions.presenceOfElementLocated(
            By.xpath("//div[@data-track-number='2']//div[@data-action='dm-playable']")
        ));

        driver.executeScript("window.scrollBy(0, 150);");

        // start the song sample
        play.click();

        // start an ffmpeg audio capture of system audio. Replace with a path and device id
        // appropriate for your system (list devices with `ffmpeg -f avfoundation -list_devices true -i ""`
        File audioCapture = new File("/Users/jlipps/Desktop/capture.wav");
        captureForDuration(audioCapture, 10000);

        assert(audioCapture.exists());
    }

    protected void captureForDuration(File audioCapture, int durationMs) throws Exception {
        FFmpeg capture = new FFmpeg(audioCapture, 0);
        Thread t = new Thread(capture);
        t.start();

        // wait for sufficient amount of song to play
        Thread.sleep(durationMs);

        // tell ffmpeg to stop sampling
        capture.stopCollection();

        // wait for ffmpeg thread to end on its own
        t.join();
    }
}

class FFmpeg implements Runnable {
    private Process proc;
    private File captureFile;
    private int deviceId;

    FFmpeg(File captureFile, int deviceId) {
        this.proc = null;
        this.captureFile = captureFile;
        this.deviceId = deviceId;
    }

    public void run() {
        ArrayList<String> cmd = new ArrayList<>();
        cmd.add("ffmpeg");       // binary should be on path
        cmd.add("-y");           // always overwrite files
        cmd.add("-f");           // format
        cmd.add("avfoundation"); // apple's system audio---something else for windows
        cmd.add("-i");           // input
        cmd.add(":" + deviceId); // device id returned by ffmpeg list
        cmd.add(captureFile.getAbsolutePath());

        ProcessBuilder pb = new ProcessBuilder(cmd);
        pb.redirectErrorStream(true);
        pb.redirectOutput(Redirect.PIPE);
        StringJoiner out = new StringJoiner("\n");
        try {
            proc = pb.start();
            try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(proc.getInputStream()))) {

                reader.lines().forEach(out::add);
            }
            proc.waitFor();
        } catch (IOException | InterruptedException ign) {}
        System.out.println("FFMpeg output was: " + out.toString());
    }

    public void stopCollection() throws IOException {
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
        writer.write("q");
        writer.flush();
        writer.close();
    }
}