package org.osgi.service.indexer.impl.util;

import java.util.List;
import java.util.Map;

public class OSGiHeader {

	private static final char DUPLICATE_MARKER = '~';

	static public Map<String,Map<String,String>> parseHeader(String value) {
		return parseHeader(value, null);
	}

	/**
	 * Standard OSGi header parser. This parser can handle the format clauses
	 * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '='
	 * value ) This is mapped to a Map { name => Map { attr|directive => value }
	 * }
	 * 
	 * @param value A string
	 * @return a Map<String,Map<String,String>>
	 */
	static public Map<String,Map<String,String>> parseHeader(String value, Reporter logger) {
		if (value == null || value.trim().length() == 0)
			return Create.map();

		Map<String,Map<String,String>> result = Create.map();
		QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
		char del = 0;
		do {
			boolean hadAttribute = false;
			Map<String,String> clause = Create.map();
			List<String> aliases = Create.list();
			String name = qt.nextToken(",;");

			del = qt.getSeparator();
			if (name == null || name.length() == 0) {
				if (logger != null && logger.isPedantic()) {
					logger.warning(
							"Empty clause, usually caused by repeating a comma without any name field or by having spaces after the backslash of a property file: "
									+ value);
				}
				if (name == null)
					break;
			} else {
				name = name.trim();

				aliases.add(name);
				while (del == ';') {
					String adname = qt.nextToken();
					if ((del = qt.getSeparator()) != '=') {
						if (hadAttribute)
							if (logger != null) {
								logger.error("Header contains name field after attribute or directive: " + adname
										+ " from " + value
										+ ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
							}
						if (adname != null && adname.length() > 0)
							aliases.add(adname.trim());
					} else {
						String advalue = qt.nextToken();
						if (advalue == null) {
							if (logger != null)
								logger.error("No value after '=' sign for attribute " + adname);
							advalue = "";
						}

						while (clause.containsKey(adname.trim()))
							adname = adname.trim() + DUPLICATE_MARKER;

						clause.put(adname.trim(), advalue.trim());
						del = qt.getSeparator();
						hadAttribute = true;
					}
				}

				// Check for duplicate names. The aliases list contains
				// the list of nams, for each check if it exists. If so,
				// add a number of "~" to make it unique.
				for (String clauseName : aliases) {
					if (result.containsKey(clauseName)) {
						if (logger != null && logger.isPedantic())
							logger.warning("Duplicate name " + clauseName + " used in header: '" + clauseName
									+ "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time).");
						while (result.containsKey(clauseName))
							clauseName += DUPLICATE_MARKER;
					}
					result.put(clauseName, clause);
				}
			}
		} while (del == ',');
		return result;
	}

	public static Map<String,String> parseProperties(String input) {
		return parseProperties(input, null);
	}

	public static Map<String,String> parseProperties(String input, Reporter logger) {
		if (input == null || input.trim().length() == 0)
			return Create.map();

		Map<String,String> result = Create.map();
		QuotedTokenizer qt = new QuotedTokenizer(input, "=,");
		char del = ',';

		while (del == ',') {
			String key = qt.nextToken(",=");
			String value = "";
			del = qt.getSeparator();
			if (del == '=') {
				value = qt.nextToken(",=");
				del = qt.getSeparator();
			}
			result.put(key, value);
		}
		if (del != 0)
			if (logger == null)
				throw new IllegalArgumentException("Invalid syntax for properties: " + input);
			else
				logger.error("Invalid syntax for properties: " + input);

		return result;
	}

	public static String removeDuplicateMarker(String key) {
		int i = key.length() - 1;
		while (i >= 0 && key.charAt(i) == DUPLICATE_MARKER)
			--i;

		return key.substring(0, i + 1);
	}

}
