/**
 * Copyright © 2013 by Data Virtuality GmbH
 */
package com.datavirtuality.dv.core.interfaces;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;

import javax.naming.NoInitialContextException;

import org.jboss.logging.Logger;

/**
 * DV extension aggregated caller class.
 * 
 * @author Roman Pedchenko
 * @date 2013.05.18
 *
 */
public class DVCaller {
	/**
	 * Logger.
	 */
	private static final Logger logger = Logger.getLogger(DVCaller.class);

	/**
	 * Cached common extension instance.
	 */
	private static GenericTeiidExtension extA = null;

	private static boolean noExtension = false;

	public static void setNoExtension(boolean noExtension) {
		DVCaller.noExtension = noExtension;
	}

	/**
	 * Calls a service with no exception catching.
	 * 
	 * @param objects parameters
	 * @return call result
	 * @throws Throwable any possible exception
	 */
	public static Object callWithExc(Object... objects) throws Throwable {
		lookupExtension();
		if (extA == null)
			return null;
		Object result = extA.run(objects);
//		logger.info("extA correctly punched");
		return result;
	}

	public static boolean available() {
		try {
			lookupExtension();
		} catch (Throwable t) {
			return false;
		}
		return extA != null;
	}

	/**
	 * Looks up the extension.
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 *
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	private static void lookupExtension() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		if (noExtension)
			return;
		try {
			if (extA == null) {
				final InputStream res = DVCaller.class.getResourceAsStream("/" + DVCaller.class.getCanonicalName());
				if (res != null) {
					try (Scanner s = new Scanner(res, "UTF8").useDelimiter("\\Z")) {
						String extFqn = s.next();
						Class<?> cl1 = Class.forName(extFqn, true, DVCaller.class.getClassLoader());
						Method m = cl1.getMethod("getInstance");
						if (Modifier.isStatic(m.getModifiers()))
							extA = (GenericTeiidExtension) m.invoke(null);
					}
				} else
					noExtension = true;
			}
		} catch (NullPointerException e) {
			logger.error("No common extension is configured", e);
			noExtension = true;
		} catch (ClassNotFoundException e) {
			// we're probably in tests
		}
	}

	/**
	 * Calls a service. If an exception occurs, wraps it with an exception of a specified type. 
	 * 
	 * @param exc an exception type to wrap any occurred exception with
	 * @param objects parameters
	 * @return call result
	 * @throws T any occurred exception wrapped with an exception of a specified type
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Throwable> Object callWithThrow(Class<? extends T> exc, Object... objects) throws T {
		T ne;
		try {
			return callWithExc(objects);
		} catch (NoInitialContextException nce) {
			return null;
			// we are probably inside tests
		} catch (Throwable e) {
			if (System.getProperty("dv.embedded") != null && e instanceof NoClassDefFoundError)
				return null;
			if (exc.isAssignableFrom(e.getClass()))
				throw (T) e;
			try {
				ne = exc.getConstructor(Throwable.class).newInstance(e);
			} catch (Throwable e1) {
				throw new RuntimeException(e1);
			}
			throw ne;
		}
	}

	/**
	 * Calls a service.
	 * 
	 * @param objects parameters
	 * @return call result
	 */
	public static Object call(Object... objects) {
		try {
			return callWithExc(objects);
		} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			logger.error("No common extension is configured or can't be instantiated");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// we're probably inside tests
		} catch (Throwable e) {
			logger.error("Can't run extA extension", e);
		}
		return null;
	}
}
