/* 
 * @author Andrea Ligios
 * */

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

class WordsCapitalizer {
    
	public static String capitalizeEveryWord(String source, List<Delimiter> delimiters, Locale locale) {
		char[] chars; 
		
		if (delimiters == null || delimiters.size() == 0)
			delimiters = getDefaultDelimiters();				
		
		// If Locale specified, i18n toLowerCase is executed, to handle specific behaviors (eg. Turkish dotted and dotless 'i')
		if (locale!=null)
			chars = source.toLowerCase(locale).toCharArray();
		else 
			chars = source.toLowerCase().toCharArray();
				
		// First charachter ALWAYS capitalized, if it is a Letter.
		if (chars.length>0 && Character.isLetter(chars[0]) && !isSurrogate(chars[0])){
			chars[0] = Character.toUpperCase(chars[0]);
		}
		
		for (int i = 0; i < chars.length; i++) {
			if (!isSurrogate(chars[i]) && !Character.isLetter(chars[i])) {
				// Current char is not a Letter; gonna check if it is a delimitrer.
				for (Delimiter delimiter : delimiters){
					if (delimiter.getDelimiter()==chars[i]){
						// Delimiter found, applying rules...						
						if (delimiter.capitalizeBefore() && i>0 
							&& Character.isLetter(chars[i-1]) && !isSurrogate(chars[i-1]))
						{   // previous character is a Letter and I have to capitalize it
							chars[i-1] = Character.toUpperCase(chars[i-1]);
						}
						if (delimiter.capitalizeAfter() && i<chars.length-1 
							&& Character.isLetter(chars[i+1]) && !isSurrogate(chars[i+1]))
						{   // next character is a Letter and I have to capitalize it
							chars[i+1] = Character.toUpperCase(chars[i+1]);
						}
						break;
					}
				} 
			}
		}
		return String.valueOf(chars);
	}
	
	public static String capitalizeEveryWord(String source, Locale locale) {
		return capitalizeEveryWord(source,null,locale);
	}

	public static String capitalizeEveryWord(String source) {
		return capitalizeEveryWord(source,null,null);
	}

	private static boolean isSurrogate(char chr){
		// Check if the current character is part of an UTF-16 Surrogate Pair.	
		// Note: not validating the pair, just used to bypass (any found part of) it.
		return (Character.isHighSurrogate(chr) || Character.isLowSurrogate(chr));
	}		
	
	private static List<Delimiter> getDefaultDelimiters(){
		// If no delimiter specified, "Capitalize after space" rule is set by default. 
		List<Delimiter> delimiters = new ArrayList<Delimiter>();
		delimiters.add(new Delimiter(Behavior.CAPITALIZE_AFTER_MARKER, ' '));
		return delimiters;
	} 

	static class Delimiter {
		private Behavior behavior;
		private char delimiter;

		private Delimiter(Behavior behavior, char delimiter) {
			super();
			this.behavior = behavior;
			this.delimiter = delimiter;
		}
		
		public boolean capitalizeBefore(){
			return (behavior.equals(Behavior.CAPITALIZE_BEFORE_MARKER)
					|| behavior.equals(Behavior.CAPITALIZE_BEFORE_AND_AFTER_MARKER));
		}

		public boolean capitalizeAfter(){
			return (behavior.equals(Behavior.CAPITALIZE_AFTER_MARKER)
					|| behavior.equals(Behavior.CAPITALIZE_BEFORE_AND_AFTER_MARKER));
		}

		public char getDelimiter() {
			return delimiter;
		}
	}
	
	static enum Behavior {
		CAPITALIZE_AFTER_MARKER(0),
		CAPITALIZE_BEFORE_MARKER(1),
		CAPITALIZE_BEFORE_AND_AFTER_MARKER(2);						
		
		private int value;			
		
		private Behavior(int value) {
			this.value = value;
		}
		
		public int getValue() {
			return value;
		}			
	} 
	
	
	

	public static void main(String[] args) throws Exception {			
		String testString;
		List <Delimiter> delimiters;
				
		Long startTime = new Date().getTime();
		System.out.println("=== WordsCapitalizer Live Demo ===");
		
		// ==============================================================
		// SIMPLE USAGE		
		// ==============================================================
		
		testString = "cApItAlIzE this string after WHITE SPACES";
		
		System.out.println("\n====================================\n SIMPLE USAGE\n====================================");
		System.out.println("Source: " + testString);
		System.out.println("Output: " + WordsCapitalizer.capitalizeEveryWord(testString));		
		// ==============================================================
		
		
				
		// ==============================================================
		// SINGLE CUSTOM-DELIMITER USAGE :
		// ==============================================================
		testString = "capitalize this string ONLY before'and''after'''APEX";		
		delimiters = new ArrayList<Delimiter>();
		delimiters.add(new Delimiter(Behavior.CAPITALIZE_BEFORE_AND_AFTER_MARKER, '\''));		
		
		System.out.println("\n====================================\n SINGLE CUSTOM-DELIMITER USAGE\n====================================");
		System.out.println("Source: " + testString);
		System.out.println("Output: " + WordsCapitalizer.capitalizeEveryWord(testString,delimiters,null));
		// ==============================================================
		
		
		
		// ==============================================================
		// MULTIPLE CUSTOM-DELIMITER USAGE :
		// ==============================================================
		testString = "capitalize this string AFTER SPACES, BEFORE'APEX, " + 
		             "and #AFTER AND BEFORE# NUMBER SIGN (#)";
		delimiters = new ArrayList<Delimiter>();
		delimiters.add(new Delimiter(Behavior.CAPITALIZE_AFTER_MARKER, ' '));
		delimiters.add(new Delimiter(Behavior.CAPITALIZE_BEFORE_MARKER, '\''));
		delimiters.add(new Delimiter(Behavior.CAPITALIZE_BEFORE_AND_AFTER_MARKER, '#'));
		
		System.out.println("\n====================================\n MULTIPLE CUSTOM-DELIMITER USAGE\n====================================");
		System.out.println("Source: " + testString);
		System.out.println("Output: " + WordsCapitalizer.capitalizeEveryWord(testString,delimiters,null));
		// ==============================================================
				
				
		// ==============================================================
		//  SIMPLE USAGE WITH CUSTOM LOCALE :
		// ==============================================================
		// Reference for this i18n problem: http://e...content-available-to-author-only...a.org/wiki/Dotted_and_dotless_I		
		testString = "Uniforming the first and last vowels (different kind of 'i's) of the Turkish word D[\u0130]YARBAK[\u0049]R (D\u0130YARBAK\u0049R) ";
		
		System.out.println("\n====================================\n SIMPLE USAGE WITH CUSTOM LOCALE\n====================================");
		System.out.println("Source: " + testString);
		System.out.println("Output: " + WordsCapitalizer.capitalizeEveryWord(testString,Locale.ENGLISH));
		

		// ==============================================================
		//  SIMPLE USAGE WITH A SURROGATE PAIR INSIDE THE STRING:
		// ==============================================================
				
		byte[] data = 
		{
			0, 0x61, // a
			0, 0x62, // b
			0, 0x20, // space
			// Surrogate Pair Begins here
			(byte) 0xD8, 1, // High surrogate
			(byte) 0xDC, 2, // Low surrogate
			// Surrogate Pair Ends here
			0, 0x63, // c
			0, 0x20, // space
			0, 0x64, // d
			0, 0x65, // e
			0, 0x20, // space
			0,(byte) 0xE0, // à
		};

		testString = new String(data, "UTF-16");
		System.out.println("\n====================================\n SIMPLE USAGE WITH A SURROGATE PAIR \n====================================");
		System.out.println("Source: " + testString);
		System.out.println("Output: " + WordsCapitalizer.capitalizeEveryWord(testString));
		
		
		
		Long endTime = new Date().getTime();						
		System.out.println("\nTotal Execution time (in milliseconds): [" + (endTime - startTime)+"]");		
		        
	}

}