/*
 * Copyright (c) 2014-2016 Jan Strauß <jan[at]over9000.eu>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package eu.over9000.skadi.handler;

import eu.over9000.skadi.model.Channel;
import eu.over9000.skadi.model.ChannelStore;
import eu.over9000.skadi.model.StateContainer;
import eu.over9000.skadi.model.StreamQuality;
import eu.over9000.skadi.ui.StatusBarWrapper;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * The handler for the livestreamer process.
 */
public class StreamHandler {

	private static final Logger LOGGER = LoggerFactory.getLogger(StreamHandler.class);
	private final Map<Channel, StreamProcessHandler> handlers = new HashMap<>();
	private final StatusBarWrapper statusBarWrapper;
	private final StateContainer state;

	public StreamHandler(final StatusBarWrapper statusBarWrapper, final ChannelStore channelStore, final StateContainer state) {
		this.statusBarWrapper = statusBarWrapper;
		this.state = state;

		channelStore.getChannels().addListener((final ListChangeListener.Change<? extends Channel> c) -> {
			while (c.next()) {
				if (c.wasRemoved()) {
					c.getRemoved().stream().filter(handlers::containsKey).forEach(channel -> {
						final StreamProcessHandler sph = handlers.remove(channel);
						sph.closeStream();
					});
				}
			}
		});
	}

	public void openStream(final Channel channel, final StreamQuality quality) {
		if (handlers.containsKey(channel)) {
			statusBarWrapper.updateStatusText("channel " + channel.getName() + " is already open");
			return;
		}

		try {
			statusBarWrapper.updateStatusText("opening channel " + channel.getName() + " (" + quality.getQuality() + ") ...");
			final StreamProcessHandler cph = new StreamProcessHandler(channel, quality);
			handlers.put(channel, cph);
		} catch (final IOException e) {
			LOGGER.error("exception opening stream", e);
			statusBarWrapper.updateStatusText("failed to open stream: " + e.getMessage());
		}
	}

	private void updateUIStatus(final String status) {
		Platform.runLater(() -> statusBarWrapper.updateStatusText(status));
	}

	private class StreamProcessHandler implements Runnable {
		private final Process process;
		private final Channel channel;
		private final Thread thread;

		private StreamProcessHandler(final Channel forChannel, final StreamQuality quality) throws IOException {
			thread = new Thread(this);
			channel = forChannel;
			thread.setName("StreamHandler Thread for " + channel.getName());

			final String livestreamerExec = state.getExecutableLivestreamer();

			final List<String> args = new LinkedList<>();

			args.add(livestreamerExec);

			if (state.getLivestreamerArgs() != null && !state.getLivestreamerArgs().isEmpty()) {
				args.addAll(state.getLivestreamerArgs());
			}

			args.add(channel.buildURL());
			args.add(quality.getQuality());

			process = new ProcessBuilder(args).redirectErrorStream(true).start();
			thread.start();
		}

		@Override
		public void run() {
			try {
				final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));

				String line;
				while ((line = br.readLine()) != null) {
					LOGGER.debug("LVSTRMR/VDPLYR: " + line);
					updateUIStatus("[" + channel.getName() + "] " + line.replaceAll("\\[(.*?)\\] ", ""));
				}

				process.waitFor();
				br.close();
			} catch (final InterruptedException | IOException e) {
				LOGGER.error("Exception handling stream process", e);
			}

			handlers.remove(channel);

		}

		public void closeStream() {
			process.destroy();
		}
	}

}

@@ -3907,97 +3907,8 @@
 );%0A%0A
-%09%09%09if (state.getLivestreamerArgs() != null && !state.getLivestreamerArgs().isEmpty()) %7B%0A%09
 %09%09%09a
@@ -3947,21 +3947,16 @@
 Args());
-%0A%09%09%09%7D
 %0A%0A%09%09%09arg

simplify code, list is always non-null also: prev commit changed authToken to auth_token in config file, will break auth