/*
 * Copyright (c) 2007, intarsys consulting GmbH
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of intarsys nor the names of its contributors may be used
 *   to endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package de.intarsys.tools.preferences;

import java.awt.Rectangle;
import java.io.StringReader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import de.intarsys.tools.enumeration.EnumItem;
import de.intarsys.tools.enumeration.EnumMeta;
import de.intarsys.tools.string.CharacterTools;
import de.intarsys.tools.string.Converter;
import de.intarsys.tools.string.ConverterException;

/**
 * A tool class for some common tasks when dealing with {@link IPreferences}.
 * 
 */
public class PreferencesTools {

	public static final String ELEMENT_SEPARATOR = ";"; //$NON-NLS-1$

	public static final String KEY_VALUE_SEPARATOR = "="; //$NON-NLS-1$

	static public String fitKey(String name) {
		int length = name.length();
		if (length <= Preferences.MAX_KEY_LENGTH) {
			return name;
		}
		StringBuilder sb = new StringBuilder();
		char[] chars = new char[length];
		name.getChars(0, length, chars, 0);
		boolean start = true;
		for (int i = 0; i < length; i++) {
			char c = chars[i];
			if (c == '.' || c == '/' || c == '\\' || c == '_') {
				start = true;
				continue;
			}
			if (Character.isUpperCase(c) || c == '.') {
				start = true;
				sb.append(c);
				continue;
			}
			if (CharacterTools.isVowel(c)) {
				if (start) {
					start = false;
					sb.append(c);
				}
				continue;
			}
			sb.append(c);
		}
		name = sb.toString();
		length = name.length();
		if (length <= Preferences.MAX_KEY_LENGTH) {
			return name;
		}
		return name.substring(length - Preferences.MAX_KEY_LENGTH, length);
	}

	public static <T extends EnumItem> T getEnumItem(IPreferences preferences,
			EnumMeta<T> meta, String name) {
		if (preferences == null) {
			return meta.getDefault();
		}
		String optionValue = preferences.get(name);
		return meta.getItemOrDefault(optionValue);
	}

	public static <T extends EnumItem> T getEnumItem(IPreferences preferences,
			EnumMeta<T> meta, String name, String defaultValue) {
		if (preferences == null) {
			return meta.getItemOrDefault(defaultValue);
		}
		String optionValue = preferences.get(name, defaultValue);
		return meta.getItemOrDefault(optionValue);
	}

	public static String getLarge(IPreferences preferences, String key,
			String defaultValue) {
		if (preferences == null) {
			return null;
		}
		IPreferences childNode = preferences.node(key);
		int i = 0;
		String subKey = "part" + i++; //$NON-NLS-1$
		String subValue = childNode.get(subKey, null);
		if (subValue == null) {
			return null;
		}
		StringBuilder sb = new StringBuilder();
		while ((subValue != null)
				&& (subValue.length() == Preferences.MAX_VALUE_LENGTH)) {
			sb.append(subValue);
			subKey = "part" + i++; //$NON-NLS-1$
			subValue = childNode.get(subKey, null);
		}

		// read terminator
		if (subValue != null) {
			sb.append(subValue);
		}
		return sb.toString();
	}

	public static void importPreferences(IPreferences root, IPreferences source)
			throws BackingStoreException {
		String[] childrenNames = source.childrenNames();
		for (int i = 0; i < childrenNames.length; i++) {
			String childName = childrenNames[i];
			IPreferences rootChild = root.node(childName);
			IPreferences sourceChild = source.node(childName);
			importPreferences(rootChild, sourceChild);
		}
		String[] keys = source.keys();
		for (int i = 0; i < keys.length; i++) {
			String key = keys[i];
			root.put(key, source.get(key));
		}
	}

	public static void putEnumItem(IPreferences preferences, String name,
			EnumItem item) {
		preferences.put(name, item.getId());
	}

	public static void putLarge(IPreferences preferences, String key,
			String longValue) {
		try {
			preferences.remove(key);
			IPreferences childNode = preferences.node(key);
			childNode.clear();
			StringReader reader = new StringReader(longValue);
			char[] buffer = new char[Preferences.MAX_VALUE_LENGTH];
			int i = 0;
			String subKey;
			String subValue = null;
			int length = reader.read(buffer);
			while (length != -1) {
				subKey = "part" + i++; //$NON-NLS-1$
				subValue = new String(buffer, 0, length);
				childNode.put(subKey, subValue);
				length = reader.read(buffer);
			}

			// write terminator
			if ((subValue != null)
					&& (subValue.length() == Preferences.MAX_VALUE_LENGTH)) {
				subKey = "part" + i++; //$NON-NLS-1$
				subValue = ""; //$NON-NLS-1$
				childNode.put(subKey, subValue);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static Rectangle toRect(String value) {
		if (value == null) {
			return null;
		}
		int[] rectDef = Converter.asIntArray(value);
		if ((rectDef == null) || (rectDef.length < 4)) {
			return null;
		}
		return new Rectangle(rectDef[0], rectDef[1], rectDef[2], rectDef[3]);
	}

	public static Rectangle toRect(String value, int[] ranges) {
		if (value == null) {
			return null;
		}
		String[] rectDefs = Converter.asStringArray(value);
		if ((rectDefs == null) || (rectDefs.length != 4)) {
			return null;
		}
		int[] rectValues = new int[4];
		for (int i = 0; i < rectDefs.length; i++) {
			String rectDef = rectDefs[i];
			if (rectDef.indexOf("%") >= 0) {
				rectDef = rectDef.replaceAll("%", "");
				try {
					rectValues[i] = (int) ((float) Converter.asInteger(rectDef)
							* (float) ranges[i] / 100f);
				} catch (ConverterException e) {
					rectValues[i] = 0;
				}
			} else {
				try {
					rectValues[i] = Converter.asInteger(rectDef);
				} catch (ConverterException e) {
					rectValues[i] = 0;
				}
			}
		}
		return new Rectangle(rectValues[0], rectValues[1], rectValues[2],
				rectValues[3]);
	}

	public static String toString(float[] value) {
		if (value == null) {
			return ""; //$NON-NLS-1$
		}
		StringBuilder sb = new StringBuilder();
		if (value != null) {
			for (int i = 0; i < value.length; i++) {
				if (i != 0) {
					sb.append(ELEMENT_SEPARATOR);
				}
				sb.append(value[i]);
			}
		}
		return sb.toString();
	}

	public static String toString(int[] value) {
		if (value == null) {
			return ""; //$NON-NLS-1$
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < value.length; i++) {
			if (i != 0) {
				sb.append(ELEMENT_SEPARATOR);
			}
			sb.append(value[i]);
		}
		return sb.toString();
	}

	public static String toString(List value) {
		if (value == null) {
			return ""; //$NON-NLS-1$
		}
		StringBuilder sb = new StringBuilder();
		for (Iterator it = value.iterator(); it.hasNext();) {
			Object element = it.next();
			sb.append(String.valueOf(element));
			if (it.hasNext()) {
				sb.append(ELEMENT_SEPARATOR);
			}
		}
		return sb.toString();
	}

	public static String toString(Map map) {
		if (map == null) {
			return ""; //$NON-NLS-1$
		}
		StringBuilder sb = new StringBuilder();
		for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
			Map.Entry entry = (Map.Entry) i.next();
			sb.append(String.valueOf(entry.getKey()));
			sb.append(KEY_VALUE_SEPARATOR);
			Object value = (entry.getValue() == null) ? "" : String
					.valueOf(entry.getValue()); //$NON-NLS-1$
			sb.append(value);
			if (i.hasNext()) {
				sb.append(ELEMENT_SEPARATOR);
			}
		}
		return sb.toString();
	}

	public static String toString(Rectangle rect) {
		if (rect == null) {
			return ""; //$NON-NLS-1$
		}
		int[] rectDef = new int[] { rect.x, rect.y, rect.width, rect.height };
		return PreferencesTools.toString(rectDef);
	}

	public static String toString(String[] value) {
		if (value == null) {
			return ""; //$NON-NLS-1$
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < value.length; i++) {
			if (i != 0) {
				sb.append(ELEMENT_SEPARATOR);
			}
			sb.append(value[i]);
		}
		return sb.toString();
	}

	public static String[] toStringArray(String value) {
		if (value == null) {
			return null;
		}
		StringTokenizer tk = new StringTokenizer(value, ELEMENT_SEPARATOR,
				false);
		String[] result = new String[tk.countTokens()];
		int i = 0;
		while (tk.hasMoreTokens()) {
			String token = tk.nextToken();
			result[i] = token.trim();
			i++;
		}
		return result;
	}

	/**
	 * 
	 */
	private PreferencesTools() {
		// tool class
	}
}
