package com.peterphi.std.guice.web.rest.setup;

import com.google.inject.AbstractModule;
import com.google.inject.ImplementedBy;
import com.google.inject.spi.Message;
import com.peterphi.std.guice.common.ClassScanner;
import com.peterphi.std.guice.serviceregistry.rest.RestResourceRegistry;
import org.apache.log4j.Logger;

import javax.ws.rs.Path;
import java.util.List;

/**
 * Discovers all interfaces annotated with {@link javax.ws.rs.Path} and registers them with the {@link
 * com.peterphi.std.guice.serviceregistry.rest.RestResource}.<br />
 * <p/>
 * In addition, searches for implementations (unless the interface is annotated with {@link com.google.inject.ImplementedBy}) and
 * auto-binds a REST interface to an implementation. If there is more than one implementation then auto-binding will fail and
 * startup will not be able to proceed.
 */
class JAXRSAutoRegisterServicesModule extends AbstractModule
{
	private static final Logger log = Logger.getLogger(JAXRSAutoRegisterServicesModule.class);

	private final ClassScanner scanner;


	public JAXRSAutoRegisterServicesModule(final ClassScanner scanner)
	{
		this.scanner = scanner;
	}


	@Override
	protected void configure()
	{
		// scan for @Path annotated interfaces
		for (Class<?> iface : scanner.getAnnotatedClasses(Path.class, ClassScanner.interfaceClass()))
		{
			try
			{
				autobind(iface);
			}
			catch (Exception e)
			{
				addError(new Message("Error auto-binding discovered REST interface " + iface, e));
			}
		}
	}


	private <T> void autobind(Class<T> iface)
	{
		if (iface.isAnnotationPresent(ImplementedBy.class))
		{
			// Guice knows how to bind this already so no need to search for an implementation
			RestResourceRegistry.register(iface);
		}
		else
		{
			// Search for implementations
			final List<Class<? extends T>> implementations = scanner.getExtendingClasses(iface);

			if (implementations.size() == 1)
			{
				RestResourceRegistry.register(iface);

				bind(iface).to(implementations.get(0));
			}
			else if (implementations.size() == 0)
			{
				// TODO bind a client provider if an endpoint is available in the config?
				log.debug("Found JAX-RS interface with no implementation. Assuming it is a client interface: " + iface);
				return;
			}
			else
			{
				// Too many interfaces
				throw new IllegalArgumentException("Expected 0 or 1 implementation for auto-discovered REST interface " +
				                                   iface +
				                                   " but found " +
				                                   implementations + ". Auto-binding REST interfaces is not possible.");
			}
		}
	}
}

@@ -387,16 +387,24 @@
 ers all 
+classes/
 interfac
@@ -853,16 +853,158 @@
 roceed.%0A
+ * %3Cp/%3E%0A * If there are no implementations found then it is assumed the interface is a client interface in this context and it is%0A * ignored.%0A
  */%0Aclas
@@ -1368,24 +1368,21 @@
 notated 
-interfac
+class
 es%0A%09%09for
@@ -1442,39 +1442,8 @@
 lass
-, ClassScanner.interfaceClass()
 ))%0A%09
@@ -1652,29 +1652,141 @@
 %3CT%3E 
-iface)%0A%09%7B%0A%09%09if (iface
+clazz)%0A%09%7B%0A%09%09// If the class is a regular class (or an interface annotated with @ImplementedBy)...%0A%09%09if (!clazz.isInterface() %7C%7C clazz
 .isA
@@ -1913,16 +1913,43 @@
 entation
+, just expose it via JAX-RS
 %0A%09%09%09Rest
@@ -1974,21 +1974,21 @@
 egister(
-iface
+clazz
 );%0A%09%09%7D%0A%09
@@ -2110,21 +2110,21 @@
 Classes(
-iface
+clazz
 );%0A%0A%09%09%09i
@@ -2194,21 +2194,21 @@
 egister(
-iface
+clazz
 );%0A%0A%09%09%09%09
@@ -2212,21 +2212,21 @@
 %09%09%09bind(
-iface
+clazz
 ).to(imp
@@ -2477,21 +2477,21 @@
 ce: %22 + 
-iface
+clazz
 );%0A%09%09%09%09r
@@ -2691,21 +2691,21 @@
         
-iface
+clazz
  +%0A%09%09%09%09 

Allow auto-registering of JAX-RS resources that are classes, not just interfaces