/*
 * Created on Jul 29, 2008 (C) Copyright TANDBERG Television Ltd.
 */

package com.tandbergtv.watchpoint.pmm.web.title;

import java.util.HashMap;
import java.util.Map;

/**
 * Serializes and deserializes the External Key for a Title
 * 
 * @author Vijay Silva
 */
public class TitleExternalKeySerializer {

	private static final char ENTRY_SEPARATOR = ';';

	private static final char VALUE_SEPARATOR = '=';

	private static final char ESCAPE_SEQUENCE = '[';

	/* Constructor */
	private TitleExternalKeySerializer() {
	}

	/**
	 * Creates an ExternalTitleKeySerializer instance
	 * 
	 * @return ExternalTitleKeySerializer instance
	 */
	public static TitleExternalKeySerializer newInstance() {
		return new TitleExternalKeySerializer();
	}

	/**
	 * Serialize the External Key Map into a single String
	 * 
	 * @param externalKey The Map of keys and their values
	 * @return the serailized string equivalent
	 */
	public String serialize(Map<String, String> externalKey) {
		StringBuilder buf = new StringBuilder();

		boolean isFirst = true;
		for (String key : externalKey.keySet()) {
			String value = externalKey.get(key);

			if (!isFirst) {
				buf.append(ENTRY_SEPARATOR);
			} else
				isFirst = false;

			buf.append(this.escapeValue(key));
			buf.append(VALUE_SEPARATOR);
			buf.append(this.escapeValue(value));
		}

		return buf.toString();
	}

	/**
	 * Deserialize the string into a map of key value pairs that represent the external key for a
	 * title.
	 * 
	 * @param externalKey The serialized external key
	 * @return The Map of Title external key name-value pairs
	 */
	public Map<String, String> deserialize(String externalKey) {
		Map<String, String> keys = new HashMap<String, String>();
		if (externalKey == null)
			return keys;

		String currentKey = null;
		StringBuilder buf = new StringBuilder();
		int currentIndex = 0;
		while (currentIndex < externalKey.length()) {
			char c = externalKey.charAt(currentIndex);
			if (c == ESCAPE_SEQUENCE) {
				buf.append(externalKey.charAt(currentIndex + 1));
				currentIndex++;
			} else if (c == VALUE_SEPARATOR) {
				if (currentKey != null) {
					String msg = "Failure deserializing title external key:  " + externalKey
							+ ", found unexpected character: " + c + " at index: " + currentIndex
							+ " when expecting character: " + ENTRY_SEPARATOR;
					throw new RuntimeException(msg);
				}

				currentKey = buf.toString();
				buf.setLength(0);
			} else if (c == ENTRY_SEPARATOR) {
				if (currentKey == null) {
					String msg = "Failure deserializing title external key: " + externalKey
							+ ", found unexpected character: " + c + " at index: " + currentIndex
							+ " when expecting character: " + VALUE_SEPARATOR;
					throw new RuntimeException(msg);
				}

				keys.put(currentKey, buf.toString());
				currentKey = null;
				buf.setLength(0);
			} else {
				buf.append(c);
			}

			currentIndex++;
		}

		if (currentKey != null) {
			keys.put(currentKey, buf.toString());
		} else if (buf.length() > 0) {
			String msg = "Failure deserializing title external key: " + externalKey
					+ ", no value specified for key: " + buf.toString()
					+ " at end external key string.";
			throw new RuntimeException(msg);
		}

		return keys;
	}

	/* Escape the value provided to ensure separators and the escape sequence are all escaped */
	private String escapeValue(String value) {
		if (value == null)
			return "";

		StringBuilder buf = new StringBuilder();
		for (int index = 0; index < value.length(); index++) {
			char c = value.charAt(index);
			if (c == ENTRY_SEPARATOR || c == VALUE_SEPARATOR || c == ESCAPE_SEQUENCE) {
				buf.append(ESCAPE_SEQUENCE);
			}
			buf.append(c);
		}

		return buf.toString();
	}

}
