/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * 
 * $Id: RequestFilter.java 71120 2013-03-26 17:42:41Z supol $ 
 */

package com.sun.ts.tests.jaxrs.ee.rs.container.requestcontext;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Priority;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;

import com.sun.ts.tests.jaxrs.common.impl.SecurityContextImpl;

/**
 * The methods are called here by reflection from the superclass
 */
@Provider
@Priority(100)
@PreMatching
public class RequestFilter extends TemplateFilter {

	public static final String PRINCIPAL = "PrincipalName";
	public static final String SETENTITYSTREAMENTITY = "SetEntityStreamEntity";
	public static final String URI = "http://xx.yy:888/base/resource/sub";

	public void abortWith() {
		abortWithEntity(null);
	}

	public void getAcceptableLanguages() {
		List<Locale> langs = requestContext.getAcceptableLanguages();
		abortWithEntity(collectionToString(langs));
	}

	public void getAcceptableLanguagesIsReadOnly() {
		List<Locale> langs = requestContext.getAcceptableLanguages();
		if (assertTrue(!langs.contains(Locale.CANADA_FRENCH),
				Locale.CANADA_FRENCH, "already included"))
			return;
		try {
			langs.add(Locale.CANADA_FRENCH);
		} catch (Exception e) {
			// expected, but optional
		}
		langs = requestContext.getAcceptableLanguages();
		if (assertTrue(!langs.contains(Locale.CANADA_FRENCH),
				Locale.CANADA_FRENCH, "included, the list is not read-only"))
			return;
		abortWithEntity(collectionToString(langs));
	}

	public void getAcceptableMediaTypes() {
		List<MediaType> types = requestContext.getAcceptableMediaTypes();
		abortWithEntity(collectionToString(types));
	}

	public void getAcceptableMediaTypesIsReadOnly() {
		List<MediaType> types = requestContext.getAcceptableMediaTypes();
		if (assertTrue(!types.contains(MediaType.APPLICATION_JSON_TYPE),
				MediaType.APPLICATION_JSON, "already included"))
			return;
		try {
			types.add(MediaType.APPLICATION_JSON_TYPE);
		} catch (Exception e) {
			// expected, but optional
		}
		types = requestContext.getAcceptableMediaTypes();
		if (assertTrue(!types.contains(MediaType.APPLICATION_JSON_TYPE),
				MediaType.APPLICATION_JSON,
				"included, the list is not read-only"))
			return;
		abortWithEntity(collectionToString(types));
	}

	public void getCookies() {
		Map<String, Cookie> cookies = requestContext.getCookies();
		Collection<Cookie> values = cookies.values();
		abortWithEntity(collectionToString(values));
	}

	public void getCookiesIsReadOnly() {
		Map<String, Cookie> cookies = requestContext.getCookies();
		int length = cookies.size();
		try {
			cookies.put("key", new Cookie("coo", "kie"));
		} catch (Exception e) {
			// expected, but optional
		}
		cookies = requestContext.getCookies();
		if (assertTrue(cookies.size() == length, "Cookie was added present"))
			return;
		abortWithEntity("#getCookies is read-only as expected");
	}

	public void getDate() {
		Date date = requestContext.getDate();
		abortWithEntity(String.valueOf(date == null ? "NULL" : date.getTime()));
	}

	public void getEntityStream() throws IOException {
		InputStream stream = requestContext.getEntityStream();
		InputStreamReader isr = new InputStreamReader(stream);
		BufferedReader br = new BufferedReader(isr);
		String entity = br.readLine();
		br.close();
		abortWithEntity(entity);
	}

	public void getHeaders() {
		MultivaluedMap<String, String> headers = requestContext.getHeaders();
		StringBuilder sb = new StringBuilder();
		for (Entry<String, List<String>> set : headers.entrySet()) {
			String first = headers.getFirst(set.getKey());
			sb.append(set.getKey()).append(":").append(first).append(";");
		}
		abortWithEntity(sb.toString());
	}

	public void getHeadersIsMutable() {
		String key = "KEY";
		MultivaluedMap<String, String> headers = requestContext.getHeaders();
		if (assertTrue(!headers.containsKey(key), "Key", key,
				"is already present"))
			return;
		headers.add(key, "VALUE");
		assertTrue(headers.containsKey(key), "Key", key, "not found");
	}

	public void getHeaderString2() {
		String string = requestContext.getHeaderString(OPERATION);
		abortWithEntity(string);
	}

	public void getLanguage() {
		Locale lang = requestContext.getLanguage();
		String entity = lang == null ? "NULL" : collectionToString(Collections
				.singleton(lang));
		abortWithEntity(entity);
	}

	public void getLength() {
		int len = requestContext.getLength();
		abortWithEntity(String.valueOf(len));
	}

	public void getMediaType() {
		MediaType media = requestContext.getMediaType();
		abortWithEntity(media == null ? "NULL" : media.toString());
	}

	public void getMethod() {
		String method = requestContext.getMethod();
		abortWithEntity(method);
	}

	public void getProperty() {
		Object o = requestContext.getProperty(PROPERTYNAME);
		abortWithEntity(o == null ? "NULL" : o.toString());
	}

	public void getPropertyNames() {		
		int length = requestContext.getPropertyNames().size();
		for (int i = 0; i != 5; i++)			
			requestContext.setProperty(PROPERTYNAME + i, PROPERTYNAME);
		Collection<String> names = requestContext.getPropertyNames();
		assertTrue(names.size() == 5 + length,
				"#getPropertyNames has unexpected number of elements",
				names.size(), "before any addition it was", length);
	}

	public void getPropertyNamesIsReadOnly() {
		Collection<String> names = requestContext.getPropertyNames();
		int length = names.size();
		for (int i = 0; i != 5; i++)
			try {
				names.add(PROPERTYNAME + i);
			} catch (Exception e) {
				// Exception here is possible
			}
		names = requestContext.getPropertyNames();
		if (assertTrue(names.size() == length,
				"#getPropertyNames has unexpected elements", names))
			return;
		abortWithEntity("0");
	}

	public void getRequest() {
		Request request = requestContext.getRequest();
		String method = request.getMethod();
		abortWithEntity(method);
	}

	public void getSecurityContext() {
		SecurityContext secCtx = requestContext.getSecurityContext();
		Principal principal = secCtx.getUserPrincipal();
		if (assertTrue(principal == null, "principal is not null"))
			return;
		abortWithEntity("NULL");
	}

	public void getUriInfo() {
		UriInfo info = requestContext.getUriInfo();
		abortWithEntity(info.getAbsolutePath().toASCIIString());
	}

	public void hasEntity() {
		boolean has = requestContext.hasEntity();
		abortWithEntity(String.valueOf(has).toLowerCase());
	}

	public void removeProperty() {
		setProperty();
		requestContext.removeProperty(PROPERTYNAME);
		getProperty();
	}

	public void setEntityStream() throws IOException {
		ByteArrayInputStream stream = new ByteArrayInputStream(
				SETENTITYSTREAMENTITY.getBytes());
		requestContext.setEntityStream(stream);
		getEntityStream();
	}

	public void setMethod() {
		requestContext.setMethod("OPTIONS");
		// really pass to the server to check the changes, do NOT abortWith
	}

	public void setProperty() {
		requestContext.setProperty(PROPERTYNAME, PROPERTYNAME + PROPERTYNAME);
		assertTrue(requestContext.getProperty(PROPERTYNAME) != null,
				"property", PROPERTYNAME, "not found");
	}

	public void setPropertyNull() {
		setProperty();
		requestContext.setProperty(PROPERTYNAME, null);
		getProperty();
	}

	public void setPropertyContext() {
		requestContext.setProperty(PROPERTYNAME, PROPERTYNAME);
	}

	public void setRequestUri1() throws IOException {
		URI uri;
		try {
			UriInfo info = requestContext.getUriInfo();
			String path = new StringBuilder().append(info.getAbsolutePath())
					.append("uri").toString();
			uri = new URI(path);
		} catch (URISyntaxException e) {
			throw new IOException(e);
		}
		requestContext.setRequestUri(uri);
		// pass to a resource to see whether the uri change has been reflected
	}

	public void setRequestUri2() throws IOException {
		URI base;
		URI suffix;
		try {
			base = new URI("http://localhost:888/otherbase");
			suffix = new URI(URI);
		} catch (URISyntaxException e) {
			throw new IOException(e);
		}
		requestContext.setRequestUri(base, suffix);
		getUriInfo();
	}

	public void setSecurityContext() throws IOException {
		SecurityContext secCtx = new SecurityContextImpl(PRINCIPAL, false,
				false, null);
		requestContext.setSecurityContext(secCtx);
	}

}
