package tim.prune.save.xml;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;

/**
 * Collection of utility functions for handling XML
 */
public abstract class XmlUtils
{
	/** Start of Cdata sequence */
	private static final String CDATA_START = "<![CDATA[";
	/** End of Cdata sequence */
	private static final String CDATA_END = "]]>";
	/** Cached copy of system encoding string */
	private static String _systemEncoding = null;

	/**
	 * Fix the CDATA blocks in the given String to give valid xml
	 * @param inString String to modify
	 * @return fixed String
	 */
	public static String fixCdata(String inString)
	{
		if (inString == null || inString.isEmpty()) {
			return "";
		}
		if (!hasIllegalCharacter(inString)) {
			return inString;
		}
		String result = inString;
		// Remove cdata block at start if present
		if (result.startsWith(CDATA_START)) {
			result = result.substring(CDATA_START.length());
		}
		// Remove all instances of end block
		result = result.replaceAll(CDATA_END, "");
		// Now check whether cdata block is required
		if (!hasIllegalCharacter(result)) {
			return result;
		}
		return CDATA_START + result + CDATA_END;
	}

	/**
	 * Checks the input string for the three illegal characters,
	 * but only looping through the string once instead of three times
	 * @param inValue string to check
	 * @return true if at least one of the illegal characters is found
	 */
	public static boolean hasIllegalCharacter(String inValue)
	{
		if (inValue == null) return false;
		final int numChars = inValue.length();
		for (int i=0; i<numChars; i++)
		{
			final char c = inValue.charAt(i);
			if (c == '<' || c == '>' || c == '&')
			{
				return true;
			}
		}
		return false;
	}

	/**
	 * @return true if system uses UTF-8 by default
	 */
	public static boolean isSystemUtf8()
	{
		String systemEncoding = getSystemEncoding();
		return (systemEncoding != null && systemEncoding.equalsIgnoreCase("UTF-8"));
	}

	/**
	 * @return name of the system's character encoding
	 */
	public static String getSystemEncoding()
	{
		if (_systemEncoding == null) {
			_systemEncoding = determineSystemEncoding();
		}
		return _systemEncoding;
	}

	/**
	 * Use a temporary file to obtain the name of the default system encoding
	 * @return name of default system encoding, or null if write failed
	 */
	private static String determineSystemEncoding()
	{
		File tempFile = null;
		String encoding = null;
		try
		{
			tempFile = File.createTempFile("gpsprune", null);
			OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile));
			encoding = getEncoding(writer);
			writer.close();
		}
		catch (IOException e) {} // value stays null
		// Delete temp file
		if (tempFile != null && tempFile.exists()) {
			if (!tempFile.delete()) {
				System.err.println("Cannot delete temp file: " + tempFile.getAbsolutePath());
			}
		}
		// If writing failed (eg permissions) then just ask system for default
		if (encoding == null) encoding = Charset.defaultCharset().name();
		return encoding;
	}


	/**
	 * Get the default system encoding using a writer
	 * @param inWriter writer object
	 * @return string defining encoding
	 */
	public static String getEncoding(OutputStreamWriter inWriter)
	{
		String encoding = inWriter.getEncoding();
		try {
			encoding =  Charset.forName(encoding).name();
		}
		catch (Exception e) {} // ignore failure to find encoding
		return encoding;
	}
}
