package de.mxro.process.internal;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.ProcessBuilder.Redirect;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;

import de.mxro.process.ProcessListener;
import de.mxro.process.XProcess;

public class Engine {

	public static XProcess startProcess(final String[] command,
			final ProcessListener listener, final File folder) {

		final ProcessBuilder pb;
		final Process process;

		try {

			pb = new ProcessBuilder(command);
			pb.directory(folder);
			pb.redirectOutput(Redirect.PIPE);
			pb.redirectInput(Redirect.PIPE);
			process = pb.start();
		} catch (final IOException e) {
			throw new RuntimeException(e);
		}

		final AtomicLong lastOutput = new AtomicLong();
		final long startTime = new Date().getTime();
		lastOutput.set(0);



		final OutputStream outputStream = process.getOutputStream();
		final InputStream inputStream = process.getInputStream();
		final InputStream errorStream = process.getErrorStream();


		//BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));


//		String line;
//		try {
//			line = reader.readLine();
//			while (line != null && !line.trim().equals("--EOF--")) {
//	             listener.onOutputLine(line);
//	             line = reader.readLine();
//	         }
//		} catch (IOException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}


		final StreamReader streamReader = new StreamReader(inputStream,
				new StreamListener() {

					@Override
					public void onOutputLine(final String line) {
						//System.out.println("Receiving line");
						lastOutput.set(new Date().getTime());
						listener.onOutputLine(line);
					}

					@Override
					public void onError(final Throwable t) {
						listener.onError(new Exception(
								"Error while reading standard output", t));
					}

					@Override
					public void onClosed() {

					}
				});



		final StreamReader errorStreamReader = new StreamReader(errorStream,
				new StreamListener() {

					@Override
					public void onOutputLine(final String line) {
						lastOutput.set(new Date().getTime());
						listener.onErrorLine(line);
					}

					@Override
					public void onError(final Throwable t) {
						listener.onError(new Exception(
								"Error while reading error output", t));
					}

					@Override
					public void onClosed() {

					}
				});

		new Thread(errorStreamReader::read).start();

        new Thread(() -> {
            try {
                streamReader.read();

                final int returnValue = process.waitFor();

                //while (lastOutput.get() == 0 && new Date().getTime() - startTime < 500) {
                //	Thread.sleep(10);
                //}

                //while ( new Date().getTime() - lastOutput.get() < 1000) {
                //	System.out.println("DELAY ... "+(new Date().getTime() - lastOutput.get()));
                //	Thread.sleep(500);
                //}
                //System.out.println("processes finished with "+returnValue);

                listener.onProcessQuit(returnValue);
            } catch (final InterruptedException e) {
                listener.onError(e);
            }
        }).start();

		return new XProcess() {

			@Override
			public synchronized void sendLine(final String line) {
				final BufferedWriter writer = new BufferedWriter(
						new OutputStreamWriter(outputStream));
				try {
					writer.append(line);
				} catch (final IOException e) {
					listener.onError(e);
				}
			}

			@Override
			public void destory() {
				process.destroy();
				try {
					process.waitFor();
				} catch (final InterruptedException e) {
					throw new RuntimeException(e);
				}

				try {
					errorStreamReader.stop();

					outputStream.close();
					inputStream.close();
					errorStream.close();

				} catch (final IOException e) {
					listener.onError(e);
				}

			}
		};

	}

}
