package tim.prune.save.xml;

import java.util.ArrayList;
import java.util.HashSet;

import tim.prune.data.ExtensionInfo;
import tim.prune.data.FileType;
import tim.prune.data.SourceInfo;

/**
 * Class to combine the xml header information including namespaces
 * from multiple xml sources
 */
public class HeaderCombiner
{
	private final GpxVersion _targetVersion;
	private final GpxVersion _ignoreVersion;
	private boolean _foundLocations = false;
	private final ArrayList<String> _namespaces = new ArrayList<>();
	private final HashSet<String> _namespaceKeys = new HashSet<>();
	private final ArrayList<String> _locationPairs = new ArrayList<>();

	/** Constructor giving target version of Gpx1.0 or Gpx1.1 */
	HeaderCombiner(GpxVersion inTargetVersion)
	{
		_targetVersion = inTargetVersion;
		_ignoreVersion = (inTargetVersion == GpxVersion.GPX_1_0 ? GpxVersion.GPX_1_1 : GpxVersion.GPX_1_0);
	}

	/** Add a single source info to the collection */
	void addSourceInfo(SourceInfo inInfo)
	{
		if (inInfo == null || inInfo.getFileType() != FileType.GPX) {
			return;
		}
		ExtensionInfo extnInfo = inInfo.getExtensionInfo();
		if (extnInfo == null) {
			return;
		}
		// Deal with namespaces
		for (String s : extnInfo.getExtraNamespaces()) {
			addNamespace(s);
		}
		// Deal with locations of schemas
		final String[] locations = extnInfo.getSchemaLocations().split(" ");
		final int numPairs = locations.length / 2;
		for (int i=0; i<numPairs; i++) {
			addLocationPair(locations[i*2], locations[i*2 + 1]);
		}
	}

	/** Add a namespace to the collection */
	private void addNamespace(String inNamespace)
	{
		if (inNamespace == null || inNamespace.isEmpty()) {
			return;
		}
		final String lowerAttributeKey = getAttributeKey(inNamespace).toLowerCase();
		if (_namespaceKeys.contains(lowerAttributeKey)) {
			// key already present, so prevent duplicates
			return;
		}
		_namespaceKeys.add(lowerAttributeKey);
		_namespaces.add(inNamespace);
	}

	/** @return the key of the given xml attribute (the part before the equals sign) */
	private static String getAttributeKey(String inNamespace)
	{
		final int equalsPos = inNamespace.indexOf('=');
		if (equalsPos <= 0) {
			return inNamespace;
		}
		return inNamespace.substring(0, equalsPos).trim();
	}

	/** Add a location pair (id and url) to the collection */
	private void addLocationPair(String inId, String inUrl)
	{
		if (inId == null || inUrl == null) {
			return;
		}
		if (_ignoreVersion.matches(inId) && _ignoreVersion.matches(inUrl)) {
			// Found header for another Gpx version, ignore it
			return;
		}
		if (_targetVersion.matches(inId) && _targetVersion.matches(inUrl))
		{
			// Found header for target Gpx version, use it
			if (_foundLocations) {
				return;
			}
			_foundLocations = true;
		}
		String pairString = inId + " " + inUrl;
		for (String pair : _locationPairs)
		{
			if (pair.equalsIgnoreCase(pairString)) {
				return;
			}
		}
		_locationPairs.add(pairString);
	}

	/** @return a space-separated list of all location pairs */
	String getAllLocations(String inDefaultPair)
	{
		StringBuilder builder = new StringBuilder();
		boolean needSpace = !_foundLocations;
		if (!_foundLocations) {
			builder.append(inDefaultPair);
		}
		for (String pair : _locationPairs)
		{
			if (needSpace) {
				builder.append(' ');
			}
			builder.append(pair);
			needSpace = true;
		}
		return builder.toString();
	}

	/** @return a linefeed-separated list of all namespaces */
	String getNamespaces()
	{
		StringBuilder builder = new StringBuilder();
		for (String ns : _namespaces) {
			builder.append(' ').append(ns).append('\n');
		}
		return builder.toString();
	}
}
