package aQute.bnd.osgi.resource;

import static aQute.bnd.osgi.resource.ResourceUtils.requireNonNull;
import static java.util.Collections.unmodifiableMap;

import java.util.HashMap;
import java.util.Map;

import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;

class CapReq {

	static enum MODE {
		Capability, Requirement
	}

	private final MODE					mode;
	private final String				namespace;
	private final Resource				resource;
	private final Map<String,String>	directives;
	private final Map<String,Object>	attributes;
	private transient int				hashCode	= 0;

	CapReq(MODE mode, String namespace, Resource resource, Map<String,String> directives,
			Map<String,Object> attributes) {
		this.mode = requireNonNull(mode);
		this.namespace = requireNonNull(namespace);
		this.resource = resource;
		this.directives = unmodifiableMap(new HashMap<>(directives));
		this.attributes = unmodifiableMap(new HashMap<>(attributes));
	}

	public String getNamespace() {
		return namespace;
	}

	public Map<String,String> getDirectives() {
		return directives;
	}

	public Map<String,Object> getAttributes() {
		return attributes;
	}

	public Resource getResource() {
		return resource;
	}

	@Override
	public int hashCode() {
		if (hashCode != 0) {
			return hashCode;
		}
		final int prime = 31;
		int result = 1;
		result = prime * result + attributes.hashCode();
		result = prime * result + directives.hashCode();
		result = prime * result + mode.hashCode();
		result = prime * result + namespace.hashCode();
		result = prime * result + ((resource == null) ? 0 : resource.hashCode());
		return hashCode = result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (obj instanceof CapReq)
			return equalsNative((CapReq) obj);
		if ((mode == MODE.Capability) && (obj instanceof Capability))
			return equalsCap((Capability) obj);
		if ((mode == MODE.Requirement) && (obj instanceof Requirement))
			return equalsReq((Requirement) obj);
		return false;
	}

	private boolean equalsCap(Capability other) {
		if (!namespace.equals(other.getNamespace()))
			return false;
		if (!attributes.equals(other.getAttributes()))
			return false;
		if (!directives.equals(other.getDirectives()))
			return false;
		return (resource == null) ? (other.getResource() == null) : resource.equals(other.getResource());
	}

	private boolean equalsNative(CapReq other) {
		if (mode != other.mode)
			return false;
		if (!namespace.equals(other.namespace))
			return false;
		if (!attributes.equals(other.attributes))
			return false;
		if (!directives.equals(other.directives))
			return false;
		return (resource == null) ? (other.resource == null) : resource.equals(other.resource);
	}

	private boolean equalsReq(Requirement other) {
		if (!namespace.equals(other.getNamespace()))
			return false;
		if (!attributes.equals(other.getAttributes()))
			return false;
		if (!directives.equals(other.getDirectives()))
			return false;
		return (resource == null) ? (other.getResource() == null) : resource.equals(other.getResource());
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		if (mode == MODE.Capability) {
			Object value = attributes.get(namespace);
			builder.append(namespace).append('=').append(value);
		} else {
			String filter = directives.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
			builder.append(filter);
			if (Namespace.RESOLUTION_OPTIONAL.equals(directives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
				builder.append("%OPT");
			}
		}
		return builder.toString();
	}

	protected void toString(StringBuilder sb) {
		sb.append("[").append(namespace).append("]");
		sb.append(attributes);
		sb.append(directives);
	}

}
