package gov.va.med.esr;

import java.io.ByteArrayInputStream;
import java.util.StringTokenizer;

import javax.annotation.Resource;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingType;
import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.http.HTTPException;

@WebServiceProvider(targetNamespace = "http://example.org", serviceName = "NearbyCityService")
@BindingType(value = HTTPBinding.HTTP_BINDING)
public class NearbyCity implements Provider<Source> {
	@Resource(type = Object.class)
	protected WebServiceContext wsContext;

	public Source invoke(Source source) {
		try {
			MessageContext messageContext = wsContext.getMessageContext();

			// Obtain the HTTP mehtod of the input request.
			/*HttpServletRequest servletRequest = (HttpServletRequest) messageContext
					.get(MessageContext.SERVLET_REQUEST);
			String httpMethod = servletRequest.getMethod();*/
			String httpMethod = "GET";
			if (httpMethod.equalsIgnoreCase("GET")) {
				String query = (String) messageContext
						.get(MessageContext.QUERY_STRING);
				if (query != null && query.contains("lat=")
						&& query.contains("long=")) {
					return createSource(query);
				} else {
					System.err.println("Query String = " + query);
					throw new HTTPException(404);
				}
			} else {
				// This operation only supports "GET"
				throw new HTTPException(405);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new HTTPException(500);
		}
	}

	private Source createSource(String str) throws Exception {
		StringTokenizer st = new StringTokenizer(str, "=&/");
		String latLong = st.nextToken();
		double latitude = Double.parseDouble(st.nextToken());
		latLong = st.nextToken();
		double longitude = Double.parseDouble(st.nextToken());
		City nearby = City.findNearBy(latitude, longitude);
		String body = nearby.toXML();
		return new StreamSource(new ByteArrayInputStream(body.getBytes()));
	}

	static class City {
		String city;
		String state;
		double latitude;
		double longitude;

		City(String city, double lati, double longi, String st) {
			this.city = city;
			this.state = st;
			this.latitude = lati;
			this.longitude = longi;
		}

		double distance(double lati, double longi) {
			return Math.sqrt((lati - this.latitude) * (lati - this.latitude)
					+ (longi - this.longitude) * (longi - this.longitude));
		}

		static final City[] cities = {
				new City("San Francisco", 37.7749295, -122.4194155, "CA"),
				new City("Columbus", 39.9611755, -82.9987942, "OH"),
				new City("Indianapolis", 39.7683765, -86.1580423, "IN"),
				new City("Jacksonville", 30.3321838, -81.655651, "FL"),
				new City("San Jose", 37.3393857, -121.8949555, "CA"),
				new City("Detroit", 42.331427, -83.0457538, "MI"),
				new City("Dallas", 32.7830556, -96.8066667, "TX"),
				new City("San Diego", 32.7153292, -117.1572551, "CA"),
				new City("San Antonio", 29.4241219, -98.4936282, "TX"),
				new City("Phoenix", 33.4483771, -112.0740373, "AZ"),
				new City("Philadelphia", 39.952335, -75.163789, "PA"),
				new City("Houston", 29.7632836, -95.3632715, "TX"),
				new City("Chicago", 41.850033, -87.6500523, "IL"),
				new City("Los Angeles", 34.0522342, -118.2436849, "CA"),
				new City("New York", 40.7142691, -74.0059729, "NY") };

		static City findNearBy(double lati, double longi) {
			int n = 0;
			for (int i = 1; i < cities.length; i++) {
				if (cities[i].distance(lati, longi) < cities[n].distance(lati,
						longi)) {
					n = i;
				}
			}
			return cities[n];
		}

		public String toXML() {
			return "<ns:NearbyCity xmlns:ns=\"http://example.org\"><City>"
					+ this.city + "</City><State>" + this.state
					+ "</State><Lat>" + this.latitude + "</Lat><Lng>"
					+ this.longitude + "</Lng></ns:NearbyCity>";
		}
	}
}
