package org.springframework.cloud.cloudfoundry;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.cloud.ServiceInfoCreator;
import org.springframework.cloud.service.ServiceInfo;

/**
 * @author Ramnivas Laddad
 * @author Scott Frederick
 */
public abstract class CloudFoundryServiceInfoCreator<SI extends ServiceInfo> implements ServiceInfoCreator<SI, Map<String, Object>> {

	private Tags tags;
	private String[] uriSchemes;

	public CloudFoundryServiceInfoCreator(Tags tags, String... uriSchemes) {
		this.tags = tags;
		this.uriSchemes = uriSchemes;
	}

	public boolean accept(Map<String, Object> serviceData) {
		return tagsMatch(serviceData) || labelStartsWithTag(serviceData) ||
				uriMatchesScheme(serviceData) || uriKeyMatchesScheme(serviceData);
	}

	@SuppressWarnings("unchecked")
	protected boolean tagsMatch(Map<String, Object> serviceData) {
		List<String> serviceTags = (List<String>) serviceData.get("tags");
		return tags.containsOne(serviceTags);
	}

	protected boolean labelStartsWithTag(Map<String, Object> serviceData) {
		String label = (String) serviceData.get("label");
		return tags.startsWith(label);
	}

	protected boolean uriMatchesScheme(Map<String, Object> serviceData) {
		if (uriSchemes == null) {
			return false;
		}

		String uri = getUriFromCredentials(getCredentials(serviceData));
		if (uri != null) {
			for (String uriScheme : uriSchemes) {
				if (uri.startsWith(uriScheme + "://")) {
					return true;
				}
			}
		}
		return false;
	}

	protected boolean uriKeyMatchesScheme(Map<String, Object> serviceData) {
		if (uriSchemes == null) {
			return false;
		}

		Map<String, Object> credentials = getCredentials(serviceData);

		for (String uriScheme : uriSchemes) {
			if (credentials.containsKey(uriScheme + "Uri") || credentials.containsKey(uriScheme + "uri") ||
					credentials.containsKey(uriScheme + "Url") || credentials.containsKey(uriScheme + "url")) {
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	protected Map<String, Object> getCredentials(Map<String, Object> serviceData) {
		return (Map<String, Object>) serviceData.get("credentials");
	}

	protected String getUriFromCredentials(Map<String, Object> credentials) {
		List<String> keys = new ArrayList<String>();
		keys.addAll(Arrays.asList("uri", "url"));

		for (String uriScheme : uriSchemes) {
			keys.add(uriScheme + "Uri");
			keys.add(uriScheme + "uri");
			keys.add(uriScheme + "Url");
			keys.add(uriScheme + "url");
		}

		return getStringFromCredentials(credentials, keys.toArray(new String[keys.size()]));
	}

	protected String getStringFromCredentials(Map<String, Object> credentials, String... keys) {
		for (String key : keys) {
			if (credentials.containsKey(key)) {
				return (String) credentials.get(key);
			}
		}
		return null;
	}

	protected int getIntFromCredentials(Map<String, Object> credentials, String... keys) {
		for (String key : keys) {
			if (credentials.containsKey(key)) {
				// allows the value to be quoted as a String or native integer type
				return Integer.parseInt(credentials.get(key).toString());
			}
		}
		return -1;
	}

	public String[] getUriSchemes() {
		return uriSchemes;
	}

	public String getDefaultUriScheme() {
		return uriSchemes[0];
	}
}

@@ -2718,32 +2718,62 @@
 ring... keys) %7B%0A
+%09%09if (credentials != null) %7B%0A%09
 %09%09for (String ke
@@ -2779,32 +2779,33 @@
 ey : keys) %7B%0A%09%09%09
+%09
 if (credentials.
@@ -2820,24 +2820,25 @@
 Key(key)) %7B%0A
+%09
 %09%09%09%09return (
@@ -2867,16 +2867,22 @@
 t(key);%0A
+%09%09%09%09%7D%0A
 %09%09%09%7D%0A%09%09%7D
@@ -2989,16 +2989,46 @@
 keys) %7B%0A
+%09%09if (credentials != null) %7B%0A%09
 %09%09for (S
@@ -3042,32 +3042,33 @@
 ey : keys) %7B%0A%09%09%09
+%09
 if (credentials.
@@ -3083,24 +3083,25 @@
 Key(key)) %7B%0A
+%09
 %09%09%09%09// allow
@@ -3160,16 +3160,17 @@
 er type%0A
+%09
 %09%09%09%09retu
@@ -3223,16 +3223,22 @@
 ing());%0A
+%09%09%09%09%7D%0A
 %09%09%09%7D%0A%09%09%7D

Add null check for credentials map