class MainDispatch {

	public static void dispatch(Class<?> main, String[] args, InstanceType instance, InstanceFlavor flavor) {
		if (MainDispatch.main != null)
			throw new IllegalStateException("Dispatch cannot be called more than once");

		MainDispatch.main = Objects.requireNonNull(main);
		MainDispatch.instance = Objects.requireNonNull(instance);
		MainDispatch.flavor = Objects.requireNonNull(flavor);

		final long timestamp = System.currentTimeMillis();

		// The logger must be initialized after setting the main class
		final Logger log = LoggerFactory.getLogger(MainDispatch.class);

		// Setup exception handler
		Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
			log.error("An unexpected exception has occurred", throwable);
		});

		// Setup shutdown hook
		Runtime.getRuntime().addShutdownHook(new Thread(() -> {
			shutdown.forEach(task -> {
				try {
					task.execute(new TaskOutcome(task.toString()));
				} catch (Exception e) {
					log.error("Failed to execute shutdown task", e);
				}
			});
		}));

		// Invoke the main method
		try {
			main.getDeclaredMethod("main", String[].class).invoke(null, (Object) args);
		} catch (Exception e) {
			throw new RuntimeException("Failed to invoke main method in class: " + main.getName(), e);
		}

		// Execute tasks
		for (Task task : tasks) {
			if (task.initMetadata == null)
				throw new RuntimeException("Unregistered initialization task class");

			TaskOutcome outcome = new TaskOutcome(task.initMetadata.name());

			try {
				task.outcome = task.execute(outcome);
			} catch (Exception e) {
				task.outcome = outcome.failure(e);
			}

			if (!task.outcome.isSkipped() && !outcome.getOutcome().getResult() && task.initMetadata.fatal()) {
				logTaskSummary(log);
				System.exit(1);
			}
		}

		// Print task summary if any task failed
		if (tasks.stream().filter(t -> !t.outcome.getOutcome().getResult()).count() != 0)
			logTaskSummary(log);

		// Print task summary if required
		else if (Config.STARTUP_SUMMARY.value().orElse(true))
			logTaskSummary(log);

		// Launch idle loop
		if (idle != null)
			idle.start();

		// Cleanup
		tasks = null;

		log.info("Initialization completed in {} ms", System.currentTimeMillis() - timestamp);
	}

	private  MainDispatch();

}