package com.gentics.mesh.cli;

import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.jacpfx.vertx.spring.SpringVerticleFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.gentics.mesh.Mesh;
import com.gentics.mesh.etc.MeshCustomLoader;
import com.gentics.mesh.etc.MeshSpringConfiguration;
import com.gentics.mesh.etc.config.MeshOptions;
import com.gentics.mesh.impl.MeshFactoryImpl;

import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.logging.SLF4JLogDelegateFactory;

/**
 * @see Mesh
 */
public class MeshImpl implements Mesh {

	private static final Logger log;

	private MeshCustomLoader<Vertx> verticleLoader;

	private MeshOptions options;
	private Vertx vertx;

	static {
		// Use slf4j instead of jul
		System.setProperty(LoggerFactory.LOGGER_DELEGATE_FACTORY_CLASS_NAME, SLF4JLogDelegateFactory.class.getName());
		log = LoggerFactory.getLogger(MeshImpl.class);
	}

	public MeshImpl(MeshOptions options) {
		Objects.requireNonNull(options, "Please specify a valid options object.");
		this.options = options;
	}

	public MeshImpl(MeshOptions options, Vertx vertx) {
		Objects.requireNonNull(options, "Please specify a valid options object.");
		Objects.requireNonNull(vertx, "Please specify a vertx instance.");
		this.options = options;
		this.vertx = vertx;
	}

	@Override
	public Vertx getVertx() {
		if (vertx == null) {
			VertxOptions options = new VertxOptions();
			//options.setClustered(true);
			options.setBlockedThreadCheckInterval(1000 * 60 * 60);
			// TODO configure worker pool size
			options.setWorkerPoolSize(12);
			vertx = Vertx.vertx(options);
		}
		return vertx;
	}

	/**
	 * Main entry point for mesh. This method will initialize the spring context and deploy mandatory verticles and extensions.
	 * 
	 * @throws Exception
	 */
	@Override
	public void run() throws Exception {
		registerShutdownHook();

		printProductInformation();
		invokeUpdateCheck();

		// Start the spring context
		try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MeshSpringConfiguration.class)) {
			setParentContext(ctx);
			BootstrapInitializer initalizer = ctx.getBean(BootstrapInitializer.class);
			ctx.start();
			initalizer.init(options, verticleLoader);
			ctx.registerShutdownHook();

			dontExit();

		}
	}

	/**
	 * Send a request to the update checker.
	 */
	private void invokeUpdateCheck() {
		log.info("Checking for updates..");
		Mesh.vertx().createHttpClient().getNow("updates.getmesh.io", "/?v=" + Mesh.getVersion(), rh -> {
			rh.bodyHandler(bh -> {
				JsonObject info = bh.toJsonObject();
			});
		});
	}

	/**
	 * This method injects the spring context into the spring verticle factory. This method should be replaced by SpringVerticleFactory.setParentContext(ctx);
	 * as soon as version 2.0.1 is released.
	 * 
	 * @param ctx
	 */
	@Deprecated
	private void setParentContext(AnnotationConfigApplicationContext ctx) {
		java.lang.reflect.Field field;
		try {
			field = SpringVerticleFactory.class.getDeclaredField("parentContext");
			field.setAccessible(true);
			field.set(null, ctx);
		} catch (Exception e) {
			log.error("Could not set spring context", e);
			throw new RuntimeException("Spring context setup failed");
		}

	}

	private void registerShutdownHook() {
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				try {
					shutdown();
				} catch (Exception e) {
					log.error("Error while shutting down mesh.", e);
				}
			}
		});
	}

	private void dontExit() {
		while (true) {
			// TODO use unsafe park instead
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void printProductInformation() {
		log.info("###############################################################");
		log.info(infoLine("Mesh Version " + Mesh.getVersion()));
		log.info(infoLine("Gentics Software"));
		log.info("#-------------------------------------------------------------#");
		// log.info(infoLine("Neo4j Version : " + Version.getKernel().getReleaseVersion()));
		log.info(infoLine("Vert.x Version: " + getVertxVersion()));
		log.info(infoLine("Name: " + MeshNameProvider.getInstance().getName()));
		log.info("###############################################################");
	}

	/**
	 * Return the vertx version.
	 * 
	 * @return
	 */
	@SuppressWarnings("deprecation")
	private String getVertxVersion() {
		return new io.vertx.core.Starter().getVersion();
	}

	private static String infoLine(String text) {
		return "# " + StringUtils.rightPad(text, 59) + " #";
	}

	@Override
	public void setCustomLoader(MeshCustomLoader<Vertx> verticleLoader) {
		this.verticleLoader = verticleLoader;
	}

	@Override
	public MeshOptions getOptions() {
		return options;
	}

	@Override
	public void shutdown() throws Exception {
		log.info("Mesh shutting down...");
		MeshSpringConfiguration.getInstance().database().stop();
		MeshSpringConfiguration.getInstance().searchProvider().stop();
		getVertx().close();
		MeshFactoryImpl.clear();
	}

}

@@ -2036,16 +2036,45 @@
 ption %7B%0A
+%09%09checkSystemRequirements();%0A
 %09%09regist
@@ -2513,24 +2513,421 @@
 );%0A%0A%09%09%7D%0A%09%7D%0A%0A
+%09/**%0A%09 * Check mesh system requirements.%0A%09 */%0A%09private void checkSystemRequirements() %7B%0A%09%09try %7B%0A%09%09%09getClass().getClassLoader().loadClass(%22jdk.nashorn.api.scripting.ClassFilter%22);%0A%09%09%7D catch (ClassNotFoundException e) %7B%0A%09%09%09log.error(%0A%09%09%09%09%09%22The nashorn classfilter could not be found. You are most likely using an outdated JDK 8. Please update to at least JDK 1.8.0_40%22);%0A%09%09%09System.exit(10);%0A%09%09%7D%0A%09%7D%0A%0A
 %09/**%0A%09 * Sen

Add check for jdk version dependency