/*
 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * 
 * $Id: BuilderClient.java 71895 2013-05-14 09:30:14Z supol $
 */
package com.sun.ts.tests.jaxrs.api.rs.core.responsebuilder;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Variant;
import javax.ws.rs.ext.RuntimeDelegate;

import com.sun.ts.lib.util.TestUtil;
import com.sun.ts.tests.jaxrs.api.rs.core.responseclient.VerificationResult;
import com.sun.ts.tests.jaxrs.common.impl.SinglevaluedMap;
import com.sun.ts.tests.jaxrs.common.util.JaxrsUtil;

public class BuilderClient extends
		com.sun.ts.tests.jaxrs.api.rs.core.responseclient.JAXRSClient {

	private static final long serialVersionUID = -6510800230328526760L;

	/**
	 * 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 BuilderClient().run(args);
	}

	/*
	 * @class.setup_props: webServerHost;
	 *                     webServerPort;
	 *                     ts_home;
	 */
	/* Run test */
	/*
	 * @testName: statusTest1
	 *
	 * @assertion_ids: JAXRS:JAVADOC:131; JAXRS:JAVADOC:153; JAXRS:JAVADOC:141;
	 *                 JAXRS:JAVADOC:123; JAXRS:JAVADOC:125; JAXRS:JAVADOC:124;
	 *
	 * @test_Strategy: Create an instance of ResponseBuilder Response.ok();
	 *                 Setting status using ResponseBuilder.status(int);
	 *                 verify that correct status code is returned
	 */
	public void statusTest1() throws Fault {
		VerificationResult result = new VerificationResult();
		Response resp = null;
		ResponseBuilder respb = null;
		for (int status : status_codes) {
			respb = Response.ok();
			respb = respb.status(status);
			resp = respb.build();
			result.append(verifyStatus(resp, status));
		}
		logMsg(result);
		assertFault(result);
	}

	/*
	 * @testName: statusTest2
	 * @assertion_ids: JAXRS:JAVADOC:131; JAXRS:JAVADOC:154; JAXRS:JAVADOC:141;
	 *                 JAXRS:JAVADOC:123; JAXRS:JAVADOC:124; JAXRS:JAVADOC:125;
	 * @test_Strategy: Create an instance of ResponseBuilder Response.ok();
	 *                 Setting status using ResponseBuilder.status(Status);
	 *                 verify that correct status code is returned
	 */
	public void statusTest2() throws Fault {
		VerificationResult result = new VerificationResult();
		Response resp = null;
		ResponseBuilder respb = null;
		for (int i = 0; i < status_codes.length - 1; i++) {
			respb = Response.ok();
			respb = respb.status(resp_status[i]);
			resp = respb.build();
			result.append(verifyStatus(resp, status_codes[i]));
		}
		logMsg(result);
		assertFault(result);
	}

	/*
	 * @testName: expiresTest
	 * @assertion_ids: JAXRS:JAVADOC:147;
	 * @test_Strategy: Set Expires to ResponseBuilder, build a response and 
	 * 				   check expires timestamp. 
	 */
	public void expiresTest() throws Fault {
		Date now = Calendar.getInstance().getTime();
		ResponseBuilder rs = Response.ok();
		rs.expires(now);
		Response response = rs.build();
		MultivaluedMap<String, Object> metadata = response.getMetadata();
		if (metadata == null)
			throw new Fault("No metadata in response");
		List<Object> expires = response.getMetadata().get("Expires");
		if (expires == null || expires.isEmpty())
			throw new Fault("No Expires property in metadata");
		boolean condition = false;
		Object fetched = expires.iterator().next();
		if (Date.class.isInstance(fetched))
			condition = ((Date) fetched).compareTo(now) == 0;
		else if (String.class.isInstance(fetched))
			condition = formats(now).contains(fetched.toString());
		else
			throw new Fault("Fetched object not recognised");

		assertFault(condition, "Expires value not matched, set: ",
				now.toString(), "fetched:", fetched.toString());
		TestUtil.logMsg("Set and fetched expire dates matched");
	}

	/*
	 * @testName: allowStringArrayTest
	 * @assertion_ids: JAXRS:JAVADOC:875;
	 * @test_Strategy: Set the list of allowed methods for the resource.
	 */
	public void allowStringArrayTest() throws Fault {
		String[] methods = { Request.OPTIONS.name(), Request.TRACE.name() };
		ResponseBuilder rb = RuntimeDelegate.getInstance()
				.createResponseBuilder();
		Response response = rb.allow(methods).build();
		Set<String> set = response.getAllowedMethods();
		String responseMethods = JaxrsUtil.iterableToString(" ", set);

		for (String method : methods) {
			assertContains(responseMethods, method, "Expected allow method",
					method, "was not found in response allowed methods",
					responseMethods);
			logMsg("Found expected allowed method", method);
		}
	}

	/*
	 * @testName: allowStringArrayTruncateDuplicatesTest
	 * @assertion_ids: JAXRS:JAVADOC:875;
	 * @test_Strategy: Set the list of allowed methods for the resource.
	 */
	public void allowStringArrayTruncateDuplicatesTest() throws Fault {
		String[] methods = { Request.OPTIONS.name(), Request.OPTIONS.name() };
		ResponseBuilder rb = RuntimeDelegate.getInstance()
				.createResponseBuilder();
		Response response = rb.allow(methods).build();
		Set<String> set = response.getAllowedMethods();
		assertEqualsInt(1, set.size(),
				"Only one allow method should be present");
		assertEquals(set.iterator().next(), Request.OPTIONS.name(),
				Request.OPTIONS.name(), "has not been found in allowed methods");
		logMsg(Request.OPTIONS.name(), "has been found in allowed methods");
	}

	/*
	 * @testName: allowStringArrayNullRemovesAllTest
	 * @assertion_ids: JAXRS:JAVADOC:875;
	 * @test_Strategy:  if null any existing allowed method list will be removed.
	 */
	public void allowStringArrayNullRemovesAllTest() throws Fault {
		String[] methods = { Request.OPTIONS.name(), Request.GET.name() };
		ResponseBuilder rb = RuntimeDelegate.getInstance()
				.createResponseBuilder();
		Response response = rb.allow(methods).allow((String[]) null).build();
		Set<String> set = response.getAllowedMethods();
		assertEqualsInt(0, set.size(), "No one allow method should be present");
		logMsg("Allowed methods has been removed by null value as expected");
	}

	/*
	 * @testName: allowStringSetTest
	 * @assertion_ids: JAXRS:JAVADOC:876;
	 * @test_Strategy: Set the list of allowed methods for the resource.
	 */
	public void allowStringSetTest() throws Fault {
		Set<String> methods = new TreeSet<String>();
		methods.add(Request.OPTIONS.name());
		methods.add(Request.TRACE.name());

		ResponseBuilder rb = RuntimeDelegate.getInstance()
				.createResponseBuilder();
		Response response = rb.allow(methods).build();
		Set<String> set = response.getAllowedMethods();
		String responseMethods = JaxrsUtil.iterableToString(" ", set);

		for (String method : methods) {
			assertContains(responseMethods, method, "Expected allow method",
					method, "was not found in response allowed methods",
					responseMethods);
			logMsg("Found expected allowed method", method);
		}
	}

	/*
	 * @testName: allowStringSetNullRemovesAllTest
	 * @assertion_ids: JAXRS:JAVADOC:876;
	 * @test_Strategy:  if null any existing allowed method list will be removed.
	 */
	public void allowStringSetNullRemovesAllTest() throws Fault {
		Set<String> methods = new TreeSet<String>();
		methods.add(Request.OPTIONS.name());
		methods.add(Request.TRACE.name());

		ResponseBuilder rb = RuntimeDelegate.getInstance()
				.createResponseBuilder();
		Response response = rb.allow(methods).allow((Set<String>) null).build();
		Set<String> set = response.getAllowedMethods();
		assertEqualsInt(0, set.size(), "No one allow method should be present");
		logMsg("Allowed methods has been removed by null value as expected");
	}

	/*
	 * @testName: encodingTest
	 * @assertion_ids: JAXRS:JAVADOC:878;
	 * @test_Strategy: Set the message entity content encoding.
	 */
	public void encodingTest() throws Fault {
		String[] encodings = { "gzip", "ccitt", "pic" };
		VerificationResult vr = new VerificationResult();
		for (String encoding : encodings) {
			Response response = Response.ok().encoding(encoding).build();
			vr.append(verifyEncoding(response,
					Collections.singletonList(encoding)));
		}
		logMsg(vr.message);
		assertFault(vr);
		logMsg("Found expected encodings");
	}

	/*
	 * @testName: linkUriStringTest
	 * @assertion_ids: JAXRS:JAVADOC:880;
	 * @test_Strategy: Add a link header.
	 */
	public void linkUriStringTest() throws Fault {
		URI uri;
		try {
			uri = new URI(URL);
		} catch (URISyntaxException e) {
			throw new Fault(e);
		}
		String rel = "REL";
		Response response = Response.ok().link(uri, rel).build();
		Link link = response.getLink(rel);
		assertFault(link != null, "link is null");
		assertFault(link.toString().contains(URL), "link", link,
				"does not contain expected", URL);
		logMsg("Found expected link", link);
	}

	/*
	 * @testName: linkStringStringTest
	 * @assertion_ids: JAXRS:JAVADOC:881;
	 * @test_Strategy: Add a link header.
	 */
	public void linkStringStringTest() throws Fault {
		String rel = "REL";
		Response response = Response.ok().link(URL, rel).build();
		Link link = response.getLink(rel);
		assertFault(link != null, "link is null");
		assertFault(link.toString().contains(URL), "link", link,
				"does not contain expected", URL);
		logMsg("Found expected link", link);
	}

	/*
	 * @testName: linksTest
	 * @assertion_ids: JAXRS:JAVADOC:882;
	 * @test_Strategy: Add one or more link headers.
	 */
	public void linksTest() throws Fault {
		String rel = "REL";
		Link link1 = Link.fromUri(URL).rel(rel + "1").build();
		Link link11 = Link.fromUri(URL).rel(rel + "11").build();
		Link link2 = Link.fromUri(URL + "/link2").rel(rel + "2").build();

		Response response = Response.ok().links(link1, link11, link2).build();
		Link link = response.getLink(rel + "1");
		assertFault(link != null, "link is null");
		assertFault(link.toString().contains(URL), "link", link,
				"does not contain expected", URL);
		link = response.getLink(rel + "11");
		assertFault(link != null, "link is null");
		assertFault(link.toString().contains(URL), "link", link,
				"does not contain expected", URL);
		link = response.getLink(rel + "2");
		assertFault(link != null, "link is null");
		assertFault(link.toString().contains(URL + "/link2"), "link", link,
				"does not contain expected", URL + "/link2");

		logMsg("Found expected links");
	}

	/*
	 * @testName: replaceAllTest
	 * @assertion_ids: JAXRS:JAVADOC:883;
	 * @test_Strategy: Replaces all existing headers with 
	 * 					the newly supplied headers.
	 */
	public void replaceAllTest() throws Fault {
		String[] headers = { "header1", "header2", "header3" };
		String header99 = "header99";
		MultivaluedMap<String, Object> mv = new SinglevaluedMap<String, Object>();
		mv.add(header99, header99);
		Response response = Response.ok().header(headers[0], headers[0])
				.header(headers[1], headers[1]).header(headers[2], headers[2])
				.replaceAll(mv).build();
		for (String header : headers)
			assertFault(response.getHeaderString(header) == null,
					"response contains non replaced header", header);

		assertFault(response.getHeaderString(header99).equals(header99),
				"response does not contain header from replacedAll map",
				header99);
	}

	/*
	 * @testName: replaceAllByNullTest
	 * @assertion_ids: JAXRS:JAVADOC:883;
	 * @test_Strategy: Replaces all existing headers with 
	 * 					the newly supplied headers. if null all existing 
	 * 					headers will be removed.
	 */
	public void replaceAllByNullTest() throws Fault {
		String[] headers = { "header1", "header2", "header3" };
		Response response = Response.ok().header(headers[0], headers[0])
				.header(headers[1], headers[1]).header(headers[2], headers[2])
				.replaceAll(null).build();
		for (String header : headers)
			assertFault(response.getHeaderString(header) == null,
					"response contains non replaced header", header);
	}

	/*
	 * @testName: variantsTest
	 * @assertion_ids: JAXRS:JAVADOC:884;
	 * @test_Strategy: Add a Vary header that lists the available variants.
	 */
	public void variantsTest() throws Fault {
		List<String> encoding = Arrays.asList("gzip", "compress");
		List<String> vars = Arrays.asList(HttpHeaders.ACCEPT_LANGUAGE,
				HttpHeaders.ACCEPT_ENCODING);
		MediaType mt = MediaType.APPLICATION_JSON_TYPE;
		ResponseBuilder rb = Response.ok();
		rb = rb.variants(getVariantList(encoding, mt).toArray(new Variant[0]));
		Response response = rb.build();
		VerificationResult result = new VerificationResult();
		result.append(verifyVary(response, vars));
		logMsg(result.message);
		assertFault(result);
	}

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

	private static String formats(Date date) {
		DateFormat format;
		TestUtil.logMsg("Creating possible string format list");
		StringBuilder sb = new StringBuilder();
		for (String tz : TimeZone.getAvailableIDs()) {
			format = JaxrsUtil.createDateFormat(TimeZone.getTimeZone(tz));
			sb.append(format.format(date));
		}
		return sb.toString();
	}

	protected Invocation.Builder invocation() {
		Client client = ClientBuilder.newClient();
		WebTarget target = client.target(URL);
		Builder b = target.request();
		return b;
	}

	static final String URL = "http://localhost:888/noUrl";
}