package org.frameworkset.web.socket.inf;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.frameworkset.util.Assert;
import org.frameworkset.util.CollectionUtils;
import org.frameworkset.util.LinkedCaseInsensitiveMap;

import com.frameworkset.util.StringUtil;


public class WebSocketExtension {
	private final String name;

	private final Map<String, String> parameters;


	/**
	 * Create a WebSocketExtension with the given name.
	 * @param name the name of the extension
	 */
	public WebSocketExtension(String name) {
		this(name, null);
	}

	/**
	 * Create a WebSocketExtension with the given name and parameters.
	 * @param name the name of the extension
	 * @param parameters the parameters
	 */
	public WebSocketExtension(String name, Map<String, String> parameters) {
		Assert.hasLength(name, "extension name must not be empty");
		this.name = name;
		if (!CollectionUtils.isEmpty(parameters)) {
			Map<String, String> m = new LinkedCaseInsensitiveMap<String>(parameters.size(), Locale.ENGLISH);
			m.putAll(parameters);
			this.parameters = Collections.unmodifiableMap(m);
		}
		else {
			this.parameters = Collections.emptyMap();
		}
	}

	/**
	 * @return the name of the extension
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * @return the parameters of the extension, never {@code null}
	 */
	public Map<String, String> getParameters() {
		return this.parameters;
	}

	/**
	 * Parse the given, comma-separated string into a list of {@code WebSocketExtension} objects.
	 * <p>This method can be used to parse a "Sec-WebSocket-Extension" extensions.
	 * @param extensions the string to parse
	 * @return the list of extensions
	 * @throws IllegalArgumentException if the string cannot be parsed
	 */
	public static List<WebSocketExtension> parseExtensions(String extensions) {
		if (extensions == null || !StringUtil.hasText(extensions)) {
			return Collections.emptyList();
		}
		else {
			List<WebSocketExtension> result = new ArrayList<WebSocketExtension>();
			for(String token : extensions.split(",")) {
				result.add(parseExtension(token));
			}
			return result;
		}
	}

	private static WebSocketExtension parseExtension(String extension) {
		Assert.doesNotContain(extension, ",", "Expected a single extension value: " + extension);
		String[] parts = StringUtil.tokenizeToStringArray(extension, ";");
		String name = parts[0].trim();

		Map<String, String> parameters = null;
		if (parts.length > 1) {
			parameters = new LinkedHashMap<String, String>(parts.length - 1);
			for (int i = 1; i < parts.length; i++) {
				String parameter = parts[i];
				int eqIndex = parameter.indexOf('=');
				if (eqIndex != -1) {
					String attribute = parameter.substring(0, eqIndex);
					String value = parameter.substring(eqIndex + 1, parameter.length());
					parameters.put(attribute, value);
				}
			}
		}

		return new WebSocketExtension(name, parameters);
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if ((o == null) || (getClass() != o.getClass())) {
			return false;
		}
		WebSocketExtension that = (WebSocketExtension) o;
		if (!name.equals(that.name)) {
			return false;
		}
		if (!parameters.equals(that.parameters)) {
			return false;
		}
		return true;
	}

	@Override
	public int hashCode() {
		int result = name.hashCode();
		result = 31 * result + parameters.hashCode();
		return result;
	}

	@Override
	public String toString() {
		StringBuilder str = new StringBuilder();
		str.append(this.name);
		for (String param : parameters.keySet()) {
			str.append(';');
			str.append(param);
			str.append('=');
			str.append(this.parameters.get(param));
		}
		return str.toString();
	}
}
