/*
 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * 
 * $Id: JAXRSClient.java 70656 2013-03-13 18:31:44Z supol $ 
 */

package com.sun.ts.tests.jaxrs.ee.rs.core.configuration;

import java.util.LinkedList;
import java.util.List;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Configurable;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.MediaType;

import com.sun.ts.tests.jaxrs.api.rs.core.configurable.Assertable;
import com.sun.ts.tests.jaxrs.api.rs.core.configurable.Registrar;
import com.sun.ts.tests.jaxrs.common.client.JaxrsCommonClient;

/*
 * @class.setup_props: webServerHost;
 *                     webServerPort;
 *                     ts_home;
 */

/**
 * <p>
 * These tests follow the intended JAXRS rule to enable Features in the order of
 * them being registered. By this rule, it is possible to check the previous
 * Feature has been / has not been enabled.
 * </p>
 * 
 * <p>
 * The isEnabled works only in runtime which has its own version of
 * configuration, i.e. it does not work on Configurable.getConfiguration()
 * </p>
 */
public class JAXRSClient extends JaxrsCommonClient {

	private static final long serialVersionUID = 7215781408688132392L;

	public JAXRSClient() {
		setContextRoot("/jaxrs_ee_core_configuration_web/resource/echo");
	}

	/**
	 * Entry point for different-VM execution. It should delegate to method
	 * run(String[], PrintWriter, PrintWriter), and this method should not
	 * contain any test configuration.
	 */
	public static void main(String[] args) {
		new JAXRSClient().run(args);
	}

	/* Run test */

	/*
	 * @testName: isEnabledFeatureReturningFalseTest
	 * @assertion_ids: JAXRS:JAVADOC:999; JAXRS:JAVADOC:1003;
	 * @test_Strategy: Check if a feature instance of featureClass class has 
	 * 					been previously enabled in the runtime configuration 
	 * 					context.
	 * 
	 * 					Returns true if the feature was successfully enabled, 
	 * 					false otherwise
	 */
	public void isEnabledFeatureReturningFalseTest() throws Fault {
		final CheckingFeature feature1 = new FeatureReturningFalse1();
		final CheckingFeature feature2 = new FeatureReturningFalse2();
		final CheckingFeature feature3 = new FeatureReturningFalse3();
		final CheckingFeature feature4 = new FeatureReturningFalse4();

		feature1.addDisabledFeatures(feature2, feature3, feature4).setName(
				"feature1");
		feature2.addDisabledFeatures(feature1, feature3, feature4).setName(
				"feature2");
		feature3.addDisabledFeatures(feature1, feature2, feature4).setName(
				"feature3");
		feature4.addDisabledFeatures(feature1, feature2, feature3).setName(
				"feature4");

		Assertable assertable = new Assertable() {
			@Override
			public void check1OnClient(Client client) throws Fault {
				assertIsDisabled(client, feature1);
				assertIsDisabled(client, feature2);
				assertIsDisabled(client, feature3);
				assertIsDisabled(client, feature4);
			}

			@Override
			public void check2OnTarget(WebTarget target) throws Fault {
				assertIsDisabled(target, feature1);
				assertIsDisabled(target, feature2);
				assertIsDisabled(target, feature3);
				assertIsDisabled(target, feature4);
			}

			void assertIsDisabled(Configurable<?> config,
					CheckingFeature feature) throws Fault {
				boolean isEnabled = config.getConfiguration()
						.isEnabled(feature);
				assertFalse(isEnabled, "Feature", feature.getName(),
						"is unexpectedly enabled", getLocation());
				logMsg("No feature enabled as expected", getLocation());
			}
		};
		Object[] instances = { feature1, feature2, feature3, feature4 };
		checkConfig(assertable, instances);
		logMsg("The provider with unassignable contract has ben ignored as expected");
	}

	/*
	 * @testName: isEnabledFeatureReturningTrueTest
	 * @assertion_ids: JAXRS:JAVADOC:999; JAXRS:JAVADOC:1003;
	 * @test_Strategy: Check if a feature instance of featureClass class has 
	 * 					been previously enabled in the runtime configuration 
	 * 					context.
	 * 
	 * 					Returns true if the feature was successfully enabled, 
	 * 					false otherwise
	 */
	public void isEnabledFeatureReturningTrueTest() throws Fault {
		final CheckingFeature feature1 = new FeatureReturningTrue1();
		final CheckingFeature feature2 = new FeatureReturningTrue2();
		final CheckingFeature feature3 = new FeatureReturningTrue3();
		final CheckingFeature feature4 = new FeatureReturningTrue4();

		feature1.addDisabledFeatures(feature2, feature3, feature4).setName(
				"feature1");
		feature2.addDisabledFeatures(feature3, feature4)
				.addEnabledFeatures(feature1).setName("feature2");
		feature3.addDisabledFeatures(feature4)
				.addEnabledFeatures(feature1, feature2).setName("feature3");
		feature4.addEnabledFeatures(feature1, feature2, feature3).setName(
				"feature4");

		Assertable assertable = new Assertable() {
			@Override
			public void check1OnClient(Client client) throws Fault {
				assertIsRegistered(client, feature1);
				assertIsNotRegistered(client, feature2);
				assertIsNotRegistered(client, feature3);
				assertIsNotRegistered(client, feature4);
			}

			@Override
			public void check2OnTarget(WebTarget target) throws Fault {
				assertIsRegistered(target, feature1);
				assertIsRegistered(target, feature2);
				assertIsNotRegistered(target, feature3);
				assertIsNotRegistered(target, feature4);
			}

			void assertIsRegistered(Configurable<?> config,
					CheckingFeature feature) throws Fault {
				Configuration configuration = config.getConfiguration();
				assertTrue(configuration.isRegistered(feature), "Feature",
						feature.getName(), "is NOT registered", getLocation());
				logMsg("Feature", feature.getName(), "registered as expected",
						getLocation());
			}

			void assertIsNotRegistered(Configurable<?> config,
					CheckingFeature feature) throws Fault {
				Configuration configuration = config.getConfiguration();
				assertFalse(configuration.isRegistered(feature), "Feature",
						feature.getName(), "is unexpectedly registered",
						getLocation());
				logMsg("Feature", feature.getName(),
						"NOT registered as expected", getLocation());
			}

		};
		Object[] instances = { feature1, feature2, feature3, feature4 };
		checkConfig(assertable, instances);
		logMsg("The provider with unassignable contract has ben ignored as expected");
	}

	/*
	 * @testName: isEnabledClassReturningFalseTest
	 * @assertion_ids: JAXRS:JAVADOC:1000; JAXRS:JAVADOC:1003;
	 * @test_Strategy: Check if a feature instance of featureClass class has 
	 * 					been previously enabled in the runtime configuration 
	 * 					context.
	 * 
	 * 					Returns true if the feature was successfully enabled, 
	 * 					false otherwise
	 */
	@SuppressWarnings("unchecked")
	public void isEnabledClassReturningFalseTest() throws Fault {
		final CheckingFeature feature1 = new FeatureReturningFalse1();
		final CheckingFeature feature2 = new FeatureReturningFalse2();
		final CheckingFeature feature3 = new FeatureReturningFalse3();
		final CheckingFeature feature4 = new FeatureReturningFalse4();

		feature1.addDisabledClasses(FeatureReturningFalse2.class,
				FeatureReturningFalse3.class, FeatureReturningFalse4.class)
				.setName("feature1");
		feature2.addDisabledClasses(FeatureReturningFalse1.class,
				FeatureReturningFalse3.class, FeatureReturningFalse4.class)
				.setName("feature2");
		feature3.addDisabledClasses(FeatureReturningFalse1.class,
				FeatureReturningFalse2.class, FeatureReturningFalse4.class)
				.setName("feature3");
		feature4.addDisabledClasses(FeatureReturningFalse1.class,
				FeatureReturningFalse2.class, FeatureReturningFalse3.class)
				.setName("feature4");

		Assertable assertable = new Assertable() {
			@Override
			public void check1OnClient(Client client) throws Fault {
				assertIsDisabled(client, feature1);
				assertIsDisabled(client, feature2);
				assertIsDisabled(client, feature3);
				assertIsDisabled(client, feature4);
			}

			@Override
			public void check2OnTarget(WebTarget target) throws Fault {
				assertIsDisabled(target, feature1);
				assertIsDisabled(target, feature2);
				assertIsDisabled(target, feature3);
				assertIsDisabled(target, feature4);
			}

			void assertIsDisabled(Configurable<?> config,
					CheckingFeature feature) throws Fault {
				boolean isEnabled = config.getConfiguration()
						.isEnabled(feature);
				assertFalse(isEnabled, "Feature", feature.getName(),
						"is unexpectedly enabled", getLocation());
				logMsg("No feature enabled as expected", getLocation());
			}
		};
		Object[] instances = { feature1, feature2, feature3, feature4 };
		checkConfig(assertable, instances);
		logMsg("The provider with unassignable contract has ben ignored as expected");
	}

	/*
	 * @testName: isEnabledFeatureClassReturningTrueTest
	 * @assertion_ids: JAXRS:JAVADOC:1000; JAXRS:JAVADOC:1003;
	 * @test_Strategy: Check if a feature instance of featureClass class has 
	 * 					been previously enabled in the runtime configuration 
	 * 					context.
	 * 
	 * 					Returns true if the feature was successfully enabled, 
	 * 					false otherwise
	 */
	@SuppressWarnings("unchecked")
	public void isEnabledFeatureClassReturningTrueTest() throws Fault {
		final CheckingFeature feature1 = new FeatureReturningTrue1();
		final CheckingFeature feature2 = new FeatureReturningTrue2();
		final CheckingFeature feature3 = new FeatureReturningTrue3();
		final CheckingFeature feature4 = new FeatureReturningTrue4();

		feature1.addDisabledClasses(FeatureReturningTrue2.class,
				FeatureReturningTrue3.class, FeatureReturningTrue4.class)
				.setName("feature1");
		feature2.addDisabledClasses(FeatureReturningTrue3.class,
				FeatureReturningTrue4.class)
				.addEnabledClasses(FeatureReturningTrue1.class)
				.setName("feature2");
		feature3.addDisabledClasses(FeatureReturningTrue4.class)
				.addEnabledClasses(FeatureReturningTrue1.class,
						FeatureReturningTrue2.class).setName("feature3");
		feature4.addEnabledClasses(FeatureReturningTrue1.class,
				FeatureReturningTrue2.class, FeatureReturningTrue3.class)
				.setName("feature4");

		Assertable assertable = new Assertable() {
			@Override
			public void check1OnClient(Client client) throws Fault {
				assertIsRegistered(client, feature1);
				assertIsNotRegistered(client, feature2);
				assertIsNotRegistered(client, feature3);
				assertIsNotRegistered(client, feature4);
			}

			@Override
			public void check2OnTarget(WebTarget target) throws Fault {
				assertIsRegistered(target, feature1);
				assertIsRegistered(target, feature2);
				assertIsNotRegistered(target, feature3);
				assertIsNotRegistered(target, feature4);
			}

			void assertIsRegistered(Configurable<?> config,
					CheckingFeature feature) throws Fault {
				Configuration configuration = config.getConfiguration();
				assertTrue(configuration.isRegistered(feature), "Feature",
						feature.getName(), "is NOT registered", getLocation());
				logMsg("Feature", feature.getName(), "registered as expected",
						getLocation());
			}

			void assertIsNotRegistered(Configurable<?> config,
					CheckingFeature feature) throws Fault {
				Configuration configuration = config.getConfiguration();
				assertFalse(configuration.isRegistered(feature), "Feature",
						feature.getName(), "is unexpectedly registered",
						getLocation());
				logMsg("Feature", feature.getName(),
						"NOT registered as expected", getLocation());
			}

		};
		Object[] instances = { feature1, feature2, feature3, feature4 };
		checkConfig(assertable, instances);
		logMsg("The provider with unassignable contract has ben ignored as expected");
	}

	// ///////////////////////////////////////////////////////////////////////

	/**
	 * Check on every possible setting of configuration by a Feature or a
	 * singleton
	 * 
	 */
	protected void checkConfig(Assertable assertable, Object[] registerables)
			throws Fault {
		checkConfig(new Registrar(), assertable, registerables);
	}

	protected void checkConfig(Registrar registrar, Assertable assertable,
			Object[] registerables) throws Fault {
		Entity<String> entity = Entity.entity("echo", MediaType.WILDCARD_TYPE);

		Client client = ClientBuilder.newClient();
		logMsg("Registering on Client");
		register(registrar, client, registerables[0]);

		WebTarget target = client.target(getAbsoluteUrl());
		logMsg("Registering on WebTarget");
		register(registrar, target, registerables[1]);

		Invocation.Builder builder = target.request();
		Invocation invocation = builder.buildPost(entity);

		String response = invocation.invoke(String.class);
		assertEquals(entity.getEntity(), response,
				"Unexpected response received", response);

		assertable.check1OnClient(client);
		assertable.incrementLocation();
		assertable.check2OnTarget(target);
	}

	protected void register(Registrar registrar, Configurable<?> config,
			Object registerable) {
		registrar.register(config, registerable);
	}

	// //////////////////////////////////////////////////////////////////////
	private abstract class NamedFeature implements Feature {
		String name;

		public String getName() {
			return name;
		}

		public NamedFeature setName(String name) {
			this.name = name;
			return this;
		}
	}

	class CheckingFeature extends NamedFeature {
		List<NamedFeature> enabledFeatures = new LinkedList<NamedFeature>();
		List<NamedFeature> disabledFeatures = new LinkedList<NamedFeature>();
		List<Class<? extends NamedFeature>> enabledFeatureClasses = new LinkedList<Class<? extends NamedFeature>>();
		List<Class<? extends NamedFeature>> disabledFeatureClasses = new LinkedList<Class<? extends NamedFeature>>();

		@Override
		public boolean configure(FeatureContext context) {
			for (NamedFeature feature : enabledFeatures) {
				if (!context.getConfiguration().isEnabled(feature))
					throw new RuntimeException("Feature " + feature.getName()
							+ " has NOT been enabled");
				logMsg("Feature", feature.getName(),
						"has been enabled as expected");
			}
			for (NamedFeature feature : disabledFeatures) {
				if (context.getConfiguration().isEnabled(feature))
					throw new RuntimeException("Feature " + feature.getName()
							+ " has been unexpectedly enabled");
				logMsg("Feature", feature.getName(),
						"has NOT been enabled as expected");
			}
			for (Class<? extends NamedFeature> feature : enabledFeatureClasses) {
				if (!context.getConfiguration().isEnabled(feature))
					throw new RuntimeException("Feature " + feature.getName()
							+ " has NOT been enabled");
				logMsg("Feature", feature.getName(),
						"has been enabled as expected");
			}
			for (Class<? extends NamedFeature> feature : disabledFeatureClasses) {
				if (context.getConfiguration().isEnabled(feature))
					throw new RuntimeException("Feature " + feature.getName()
							+ " has been unexpectedly enabled");
				logMsg("Feature", feature.getName(),
						"has NOT been enabled as expected");
			}
			return true;
		}

		public CheckingFeature addEnabledFeatures(NamedFeature... features) {
			if (features != null)
				for (NamedFeature feature : features)
					enabledFeatures.add(feature);
			return this;
		}

		public CheckingFeature addDisabledFeatures(NamedFeature... features) {
			if (features != null)
				for (NamedFeature feature : features)
					disabledFeatures.add(feature);
			return this;
		}

		public CheckingFeature addEnabledClasses(
				Class<? extends NamedFeature>... features) {
			if (features != null)
				for (Class<? extends NamedFeature> feature : features)
					enabledFeatureClasses.add(feature);
			return this;
		}

		public CheckingFeature addDisabledClasses(
				Class<? extends NamedFeature>... features) {
			if (features != null)
				for (Class<? extends NamedFeature> feature : features)
					disabledFeatureClasses.add(feature);
			return this;
		}
	}

	private class FeatureReturningFalse extends CheckingFeature {
		@Override
		public boolean configure(FeatureContext context) {
			super.configure(context);
			// false returning feature is not to be registered
			return false;
		}
	}

	private class FeatureReturningTrue extends CheckingFeature {
		@Override
		public boolean configure(FeatureContext context) {
			return super.configure(context);
		}
	}

	private class FeatureReturningFalse1 extends FeatureReturningFalse {
	};

	private class FeatureReturningFalse2 extends FeatureReturningFalse {
	};

	private class FeatureReturningFalse3 extends FeatureReturningFalse {
	};

	private class FeatureReturningFalse4 extends FeatureReturningFalse {
	};

	private class FeatureReturningTrue1 extends FeatureReturningTrue {
	};

	private class FeatureReturningTrue2 extends FeatureReturningTrue {
	};

	private class FeatureReturningTrue3 extends FeatureReturningTrue {
	};

	private class FeatureReturningTrue4 extends FeatureReturningTrue {
	};

}