package sublandroid.command;

import sublandroid.*;
import sublandroid.core.*;
import sublandroid.messages.*;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;

import org.gradle.api.*;
import org.gradle.tooling.*;
import org.gradle.tooling.model.*;

public abstract class Command {

	public static class Gradle {

		public static Gradle from(ProjectConnection connection) {
			return new Gradle(connection);
		}

		private final ProjectConnection connection;
		private final Collection<File> jars;
		private final Collection<Class<? extends Plugin<?>>> plugins;

		private Gradle(final ProjectConnection connection) {
			this(connection, null, null);
		}

		private Gradle(
			final ProjectConnection connection,
			Collection<File> jars,
			Collection<Class<? extends Plugin<?>>> plugins) {

			this.connection = connection;
			this.jars = jars;
			this.plugins = plugins;
		}

		public <T extends Model> ModelInvocation<T> model(Class<T> modelClass, String... tasks) {

			final InitScript initScript = new InitScript();
			initScript.jars(jars);
			initScript.plugins(plugins);

			return new ModelInvocation(
				initScript, modelClass, connection, tasks
			);
		}

		public Gradle plugins(Class<? extends Plugin<?>>... pluginClasses) {

			final TreeSet<File> jars = new TreeSet<>();
			final List<Class<? extends Plugin<?>>> plugins = new ArrayList<>(pluginClasses.length);

			for (Class<? extends Plugin<?>> pluginClass : pluginClasses) {
				final File jar = InitScript.findJar(pluginClass);

				if (!jars.contains(jar))
					jars.add(InitScript.findJar(pluginClass));

				plugins.add(pluginClass);
			}

			return new Gradle(connection, jars, plugins);
		}
	}

	public static class Invocation {

		protected final ProjectConnection connection;
		protected final InitScript initScript;
		
		private ByteArrayOutputStream standardErr = null;
		private ByteArrayOutputStream standardOut = null;

		protected boolean invoked = false;

		protected Invocation(InitScript initScript, ProjectConnection connection) {
			this.initScript = initScript;
			this.connection = connection;
		}

		public ByteArrayOutputStream getStandardErr() {
			return standardErr;
		}

		public ByteArrayOutputStream getStandardOut() {
			return standardOut;
		}

		protected void setup(LongRunningOperation longRunningOperation) {
			if (!invoked) {
				longRunningOperation.setStandardError(standardErr = new ByteArrayOutputStream());
				longRunningOperation.setStandardOutput(standardOut = new ByteArrayOutputStream());

				if (initScript.isNecessary())
					longRunningOperation.withArguments("--init-script", initScript.fileName());
			}
		}
	}

	public static class ModelInvocation<T extends Model> extends Invocation {

		private final String[] tasks;
		private final Class<T> modelClass;

		public ModelInvocation(
			InitScript initScript,
			Class<T> modelClass,
			ProjectConnection connection,
			String... tasks
		) {
			super(initScript, connection);
			this.modelClass = modelClass;
			this.tasks = tasks;
		}

		public synchronized T get() {
			if (invoked)
				throw new IllegalStateException();

			final ModelBuilder<T> builder = connection.<T> model(modelClass);
			setup(builder);

			invoked = true;

			return builder.get();
		}
	}

	protected static final Pattern LINE_BREAK_PATTERN = Pattern.compile("[\\r\\n]+");

	public abstract Message execute(MCommand mCommand, ProjectConnection connection);

	protected void println(String message, Object... objects) {
		System.out.printf(message + '\n', objects);
	}
}
@@ -2292,19 +2292,19 @@
 ted 
-void setup(
+%3CR extends 
 Long
@@ -2323,21 +2323,21 @@
 tion
- longRunningO
+%3E R setup(R o
 pera
@@ -2363,36 +2363,25 @@
 oked) %7B%0A%09%09%09%09
-longRunningO
+o
 peration.set
@@ -2438,36 +2438,25 @@
 eam());%0A%09%09%09%09
-longRunningO
+o
 peration.set
@@ -2558,20 +2558,9 @@
 %09%09%09%09
-longRunningO
+o
 pera
@@ -2623,16 +2623,38 @@
 ));%0A%09%09%09%7D
+%0A%0A%09%09%09return operation;
 %0A%09%09%7D%0A%09%7D%0A
@@ -3152,16 +3152,22 @@
 ilder = 
+setup(
 connecti
@@ -3194,26 +3194,8 @@
 ass)
-;%0A%09%09%09setup(builder
 );%0A%0A

use setup in longrunningoperation