/*
 * ====================================================================
 * 
 * The ObjectStyle Group Software License, Version 1.0
 * 
 * Copyright (c) 2006 The ObjectStyle Group and individual authors of the
 * software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * 2. 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.
 * 
 * 3. The end-user documentation included with the redistribution, if any, must
 * include the following acknowlegement: "This product includes software
 * developed by the ObjectStyle Group (http://objectstyle.org/)." Alternately,
 * this acknowlegement may appear in the software itself, if and wherever such
 * third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse or
 * promote products derived from this software without prior written permission.
 * For written permission, please contact andrus@objectstyle.org.
 * 
 * 5. Products derived from this software may not be called "ObjectStyle" nor
 * may "ObjectStyle" appear in their names without prior written permission of
 * the ObjectStyle Group.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
 * OBJECTSTYLE GROUP OR ITS 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.
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by many individuals on
 * behalf of the ObjectStyle Group. For more information on the ObjectStyle
 * Group, please see <http://objectstyle.org/>.
 *  
 */
package org.objectstyle.wolips.baseforplugins.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class StringUtils {
	public static String toShortPrettyClassName(String name) {
		if (name == null) {
			return null;
		}
		String shortName;
		int dotIndex = name.lastIndexOf('.');
		if (dotIndex == -1) {
			shortName = name;
		}
		else {
			shortName = name.substring(dotIndex + 1);
		}
		if (shortName.startsWith("EO")) {
			shortName = shortName.substring(2);
		}
		StringBuffer nameBuffer = new StringBuffer(shortName);
		for (int i = 0; i < nameBuffer.length(); i ++) {
			char ch = nameBuffer.charAt(i);
			if (Character.isUpperCase(ch) && i != 0) {
				nameBuffer.insert(i, ' ');
				i ++;
			}
		}
		return nameBuffer.toString();
	}
	
	public static int firstLetterIndex(String _name) {
		int letterIndex = -1;
		if (_name != null) {
			int length = _name.length();
			boolean found = false;
			for (int index = 0; !found && index < length; index++) {
				char ch = _name.charAt(index);
				if (Character.isLetter(ch)) {
					letterIndex = index;
					found = true;
				} else if (ch != '_') {
					found = true;
				}
			}
		}
		return letterIndex;
	}

	public static boolean isUppercaseFirstLetter(String _name) {
		int firstLetterIndex = StringUtils.firstLetterIndex(_name);
		return (firstLetterIndex != -1 && Character.isUpperCase(_name.charAt(firstLetterIndex)));
	}

	public static boolean isLowercaseFirstLetter(String _name) {
		int firstLetterIndex = StringUtils.firstLetterIndex(_name);
		return (firstLetterIndex != -1 && Character.isLowerCase(_name.charAt(firstLetterIndex)));
	}

	public static String toLowercaseFirstLetter(String value) {
    String capital = null;
    if (value != null) {
            int length = value.length();
            if (length > 0) {
                StringBuffer buffer = new StringBuffer(value);
                for (int i = 0; i < length; i ++) {
                    char ch = value.charAt(i);
                    if (i == 0 || i == length - 1 || (i < length - 1 && Character.isUpperCase(value.charAt(i + 1)))) {
                        buffer.setCharAt(i, Character.toLowerCase(ch));
                    }
                    else {
                            break;
                    }
                }
                capital = buffer.toString();
            }
    }
    return capital != null ? capital : value;
	}

	public static String toUppercaseFirstLetter(String _name) {
		int firstLetterIndex = StringUtils.firstLetterIndex(_name);
		String name;
		if (firstLetterIndex == -1) {
			name = _name;
		} else {
			StringBuffer sb = new StringBuffer();
			if (firstLetterIndex > 0) {
				sb.append(_name.substring(0, firstLetterIndex));
			}
			sb.append(Character.toUpperCase(_name.charAt(firstLetterIndex)));
			sb.append(_name.substring(firstLetterIndex + 1));
			name = sb.toString();
		}
		return name;
	}

	public static boolean isSelectorNameEqual(String _expectedName, String _possibleName) {
		return _expectedName.equals(_possibleName) || (_expectedName + ":").equals(_possibleName);
	}
    
    /**
     * Converts ThisIsATest to this_is_a_test
     * @param camelString the StringWithCaps
     * @return the string_with_underscores
     */
    public static String camelCaseToUnderscore(String camelString) {
    	return StringUtils.camelCaseToUnderscore(camelString, true);
    }
    
    /**
     * Converts ThisIsATest to this_is_a_test
     * @param camelString the StringWithCaps
     * @param lowercase if true, the uppercase characters at the separators are lowercased
     * @return the string_with_underscores
     */
    public static String camelCaseToUnderscore(String camelString, boolean lowercase) {
    	StringBuffer underscore = new StringBuffer();
    	boolean lastCharacterWasWordBreak = false;
    	boolean lastCharacterWasCapital = false;
    	int length = camelString.length();
    	for (int i = 0; i < length; i ++) {
    		char ch = camelString.charAt(i);
    		if (Character.isUpperCase(ch)) {
    			boolean isLastCharacter = (i == length - 1); 
    			boolean nextCharacterIsCapital =  (!isLastCharacter && Character.isUpperCase(camelString.charAt(i + 1)));
    			if (i > 0 && ((!lastCharacterWasWordBreak && !lastCharacterWasCapital) || (!nextCharacterIsCapital && !isLastCharacter))) {
    				underscore.append("_");
    				lastCharacterWasWordBreak = true;
    			}
    			else {
    				lastCharacterWasWordBreak = false;
    			}
    			lastCharacterWasCapital = true;
    		}
    		else if (ch == '_') {
    			lastCharacterWasWordBreak = true;
    			lastCharacterWasCapital = false;
    		}
    		else {
    			lastCharacterWasWordBreak = false;
    			lastCharacterWasCapital = false;
    		}
    		if (lowercase) {
    			underscore.append(Character.toLowerCase(ch));
    		}
    		else {
    			underscore.append(ch);
    		}
    	}
    	return underscore.toString();
    }
    
	public static String toPlural(String _str) {
		String plural;
		if (_str != null && _str.length() > 0) {
			char ch = _str.charAt(_str.length() - 1);
			StringBuffer pluralBuffer = new StringBuffer(_str);
			if (ch == 's' || ch == 'x') {
				pluralBuffer.append("es");
			} else if (ch == 'y') {
				pluralBuffer.setLength(pluralBuffer.length() - 1);
				pluralBuffer.append("ies");
			} else {
				pluralBuffer.append("s");
			}
			plural = pluralBuffer.toString();
		} else {
			plural = _str;
		}
		return plural;
	}
	
	public static boolean isKeyPath(String str) {
		return str != null && str.matches("^[^.][a-zA-Z0-9_.]+$");
	}

	public static String getErrorMessage(Throwable _t) {
		return StringUtils.getErrorMessage(null, _t);
	}
	
	public static String getErrorMessage(String initialMessage, Throwable _t) {
		StringBuffer messageBuffer = new StringBuffer();
		if (initialMessage != null) {
			messageBuffer.append(initialMessage);
			if (_t != null) {
				messageBuffer.append(" ");
			}
		}
		
		Throwable t = _t;
		while (t != null) {
			String message = t.getMessage();
			if (message == null && !(t instanceof InvocationTargetException)) {
				String name = t.getClass().getName();
				int lastDotIndex = name.lastIndexOf('.');
				name = name.substring(lastDotIndex + 1);
				message = name;
			}

			if (message != null) {
				message = message.trim();
				messageBuffer.append(message);
				if (!message.endsWith(".")) { //$NON-NLS-1$
					messageBuffer.append(".  "); //$NON-NLS-1$
				}
				else if (!message.endsWith("  ")) {
					messageBuffer.append("  ");
				}
			}

			Throwable cause = t.getCause();
			if (t == cause) {
				t = null;
			} else {
				t = cause;
			}
		}
		return messageBuffer.toString();
	}
	
	public static String findUnusedName(String newName, Object obj, String getMethodName) {
		try {
			String safeNewName = newName;
			if (safeNewName == null) {
				safeNewName = "MISSING";
			}
			Method getMethod = obj.getClass().getMethod(getMethodName, String.class);
			boolean unusedNameFound = (getMethod.invoke(obj, safeNewName) == null);
			String unusedName = safeNewName;
			if (!unusedNameFound) {
				int cutoffLength;
				for (cutoffLength = safeNewName.length(); cutoffLength > 0; cutoffLength --) {
					if (!Character.isDigit(safeNewName.charAt(cutoffLength - 1))) {
						break;
					}
				}
				String newWithoutTrailingNumber = safeNewName.substring(0, cutoffLength);
				unusedNameFound = (getMethod.invoke(obj, newWithoutTrailingNumber) == null);
				unusedName = newWithoutTrailingNumber;
				for (int dupeNameNum = 1; !unusedNameFound; dupeNameNum++) {
					unusedName = newWithoutTrailingNumber + dupeNameNum;
					Object existingObject = getMethod.invoke(obj, unusedName);
					unusedNameFound = (existingObject == null);
				}
			}
			return unusedName;
		} catch (Throwable t) {
			throw new RuntimeException("Failed to find unused name for '" + newName + "' with method '" + getMethodName + "'.", t);
		}
	}

}
