package aQute.bnd.build.model.clauses;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;

import aQute.bnd.header.Attrs;

public class HeaderClause implements Cloneable, Comparable<HeaderClause> {

	private static final String	INTERNAL_LIST_SEPARATOR				= ";";
	private static final String	INTERNAL_LIST_SEPARATOR_NEWLINES	= INTERNAL_LIST_SEPARATOR + "\\\n\t\t";

	protected String			name;
	protected Attrs				attribs;

	public HeaderClause(String name, Attrs attribs) {
		assert name != null;

		this.name = name;
		this.attribs = attribs == null ? new Attrs() : attribs;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public Attrs getAttribs() {
		return attribs;
	}

	public List<String> getListAttrib(String attrib) {
		String string = attribs.get(attrib);
		if (string == null)
			return null;

		List<String> result = new ArrayList<String>();
		StringTokenizer tokenizer = new StringTokenizer(string, ",");
		while (tokenizer.hasMoreTokens()) {
			result.add(tokenizer.nextToken().trim());
		}

		return result;
	}

	public void setListAttrib(String attrib, Collection< ? extends String> value) {
		if (value == null || value.isEmpty())
			attribs.remove(attrib);
		else {
			StringBuilder buffer = new StringBuilder();
			boolean first = true;
			for (String string : value) {
				if (!first)
					buffer.append(',');
				buffer.append(string);
				first = false;
			}
			attribs.put(attrib, buffer.toString());
		}
	}

	public void formatTo(StringBuilder buffer) {
		formatTo(buffer, null);
	}

	public void formatTo(StringBuilder buffer, Comparator<Entry<String,String>> sorter) {
		String separator = newlinesBetweenAttributes() ? INTERNAL_LIST_SEPARATOR_NEWLINES : INTERNAL_LIST_SEPARATOR;
		// If the name contains a comma, then quote the whole thing
		String tmpName = name;
		if (tmpName.indexOf(',') > -1)
			tmpName = "'" + tmpName + "'";
		buffer.append(tmpName);

		if (attribs != null) {
			Set<Entry<String,String>> set;
			if (sorter != null) {
				set = new TreeSet<Map.Entry<String,String>>(sorter);
				set.addAll(attribs.entrySet());
			} else {
				set = attribs.entrySet();
			}

			for (Iterator<Entry<String,String>> iter = set.iterator(); iter.hasNext();) {
				Entry<String,String> entry = iter.next();
				String name = entry.getKey();
				String value = entry.getValue();

				if (value != null && value.length() > 0) {
					buffer.append(separator);

					// If the value contains any comma or equals, then quote the
					// whole thing
					if (value.indexOf(',') > -1 || value.indexOf('=') > -1)
						value = "'" + value + "'";

					buffer.append(name).append('=').append(value);
				}
			}
		}
	}

	protected boolean newlinesBetweenAttributes() {
		return false;
	}

	@Override
	public HeaderClause clone() {
		try {
			HeaderClause clone = (HeaderClause) super.clone();
			clone.name = this.name;
			clone.attribs = new Attrs(this.attribs);
			return clone;
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
	}

	public int compareTo(HeaderClause other) {
		return this.name.compareTo(other.name);
	}

	@SuppressWarnings("deprecation")
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((attribs == null) ? 0 : attribs.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		HeaderClause other = (HeaderClause) obj;
		if (attribs == null) {
			if (other.attribs != null)
				return false;
		} else if (!attribs.isEqual(other.attribs))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString() {
		StringBuilder b = new StringBuilder();
		formatTo(b);
		return b.toString();
	}
}
