package helper;


import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;


public class TextUi {
	private static final String EMPTY = "Empty";
	private static final String RESTART = "RESTART";
	private static final String DONE = "DONE";
	private static final String ADD_ALL = "ADD ALL";
	private static final Logger LOGGER = Logger.getLogger("kraken.textui");
	private static final String SEPARATOR =
			"------------------------------------------------------------------";
	private static boolean writeDebugMessage = true;
	public static final int CHOICE_YES = 1;
	public static final int CHOICE_NO = 2;
	public static final int CHOICE_ABORT = 3;
	static InputStreamReader isr = new InputStreamReader(System.in);
	static BufferedReader reader = new BufferedReader(TextUi.isr);
	static PrintStream writer = System.out;
	static PrintStream errorWriter = System.err;

	private static void printLabeledSeparator(String label) {
		if (label.equals("-")) {
			TextUi.println(TextUi.SEPARATOR);
			return;
		}
		final String realLabel = " " + label.substring(1) + " ";
		final int pads = TextUi.SEPARATOR.length() - realLabel.length();
		if (pads < 0) {
			TextUi.println("---" + realLabel + "---");
			return;
		}
		int i = 0;
		for (i = 0; i < pads / 2; i++) {
			TextUi.print("-");
		}
		TextUi.print(realLabel);
		for (; i < pads; i++) {
			TextUi.print("-");
		}
		TextUi.println("");
	}

	/**
	 * Creates an interactive menu with the specified menuitems and prompt.
	 * Simply pressing enter, will return the defaultItem
	 * 
	 * @param menuitems
	 * @param defaultItem
	 * @param prompt
	 * @return
	 */
	public static String menu(List<String> menuitems, String defaultItem,
			String prompt) {
		String thePrompt = null;
		String value = null;
		final HashMap<Integer, String> map = new HashMap<Integer, String>();
		int i = 0;
		if (prompt != null) {
			thePrompt = prompt;
		} else {
			thePrompt = "Enter your choice: ";
		}
		while (true) {
			i = 0;
			TextUi.writer.println("\n" + TextUi.SEPARATOR);
			for (final String item : menuitems) {
				if (item.startsWith("-")) {
					TextUi.printLabeledSeparator(item);
				} else {
					i++;
					if (item.equals(defaultItem)) {
						TextUi.println("[" + i + "]\t" + item);
					} else {
						TextUi.println(" " + i + " \t" + item);
					}
					map.put(new Integer(i), item);
				}
			}
			TextUi.writer.println(TextUi.SEPARATOR);
			try {
				TextUi.print(thePrompt);
				value = TextUi.reader.readLine();
				if (value.equals("")) {
					if (defaultItem != null) {
						return defaultItem;
					} else {
						continue;
					}
				}
				final Integer val = Integer.valueOf(value);
				final String retvalue = (String) map.get(val);
				if (retvalue == null) {
					TextUi.writer.println("Invalid value, try again!");
					continue;
				}
				return retvalue;
			} catch (final IOException ioex) {
				return "ERROR";
			} catch (final NumberFormatException nfex) {
				String selectedItem = null;
				int countmatch = 0;
				for (final String menuitem : menuitems) {
					if (menuitem.toLowerCase().indexOf(value.toLowerCase()) != -1) {
						selectedItem = menuitem;
						countmatch++;
					}
				}
				if (countmatch == 1) {
					return selectedItem;
				}
				if (countmatch > 1) {
					TextUi
							.println(value
									+ " matches more than one menu items!");
					continue;
				}
				if (countmatch == 0) {
					TextUi.println(value
							+ " does not match any of the menu items!");
					continue;
				}
			}
		}
	}

	/**
	 * @param string
	 * @param oldValue
	 */
	public static String inputString(String prompt) {
		return TextUi.inputString(prompt, null);
	}

	/**
	 * @param prompt
	 * @param oldValue
	 * @return
	 */
	public static String inputString(String prompt, String oldValue) {
		prompt = TextUi.processPrompt(prompt);
		if (oldValue != null) {
			TextUi.println(prompt + oldValue);
			TextUi.println("Press <ENTER> to keep");
		}
		TextUi.writer.print(prompt);
		try {
			String value = TextUi.reader.readLine();
			if (value.equals("") && oldValue != null) {
				value = oldValue;
			}
			if (value.startsWith("+")) {
				value = oldValue + value.substring(1);
			}
			return value;
		} catch (final Exception ex) {
			// should never happen
			TextUi.LOGGER.severe(ex.toString());
			return null;
		}
	}

	/**
	 * @param string
	 * @param oldValue
	 */
	public static Integer inputInteger(String prompt) {
		return TextUi.inputInteger(prompt, null);
	}

	public static Integer inputInteger(String prompt, Integer oldValue) {
		prompt = TextUi.processPrompt(prompt);
		if (oldValue != null) {
			TextUi.println(prompt + oldValue);
			TextUi.println("<ENTER> to keep the old value");
		}
		while (true) {
			try {
				TextUi.print(prompt);
				final String value = TextUi.reader.readLine();
				if (value.equals("") && oldValue != null) {
					return oldValue;
				}
				return Integer.valueOf(value);
			} catch (final NumberFormatException nfex) {
				TextUi.writer.print("Invalid value, enter an integer number");
			} catch (final Exception ex) {
				// should never happen
				TextUi.LOGGER.severe(ex.toString());
				return null;
			}
		}
	}

	/**
	 * @param string
	 * @param oldValue
	 */
	public static Date inputDate(String prompt) {
		return TextUi.inputDate(prompt, null);
	}

	public static Date inputDate(String prompt, Date oldValue) {
		prompt = TextUi.processPrompt(prompt);
		if (oldValue != null) {
			TextUi.println(prompt + oldValue);
			TextUi.println("<ENTER> to keep the old value");
		}
		while (true) {
			try {
				TextUi.writer.print(prompt);
				final String propertyValue = TextUi.reader.readLine();
				if (propertyValue.equals("") && oldValue != null) {
					return oldValue;
				}
				// return DataTypeParserHelper.parseStringToDate(propertyValue);
			} catch (final Exception ex) {
				// should never happen
				TextUi.LOGGER.severe(ex.toString());
				return null;
			}
		}
	}

	/**
	 * @param string
	 * @param oldValue
	 */
	public static List<String> inputList(String prompt) {
		return TextUi.inputList(prompt, null);
	}

	/**
	 * Helper function for inputList
	 * 
	 * @param prompt
	 * @param value
	 */
	private static void printListWithPrompt(String prompt, List<String> value) {
		TextUi.println(prompt);
		int i = 0;
		for (final String element : value) {
			TextUi.println("" + i + "\t" + element);
			i++;
		}
		TextUi
				.println("<ENTER> to keep the old value, \n+newvalue to add new value, \n-number to remove one of the elements\nor comma separated list for direct entry");
	}

	/**
	 * Dynamically edits a list of strings
	 * 
	 * @param prompt
	 * @param oldValue
	 * @return
	 */
	public static List<String> inputList(String prompt, List<String> oldValue) {
		prompt = TextUi.processPrompt(prompt);
		if (oldValue == null) {
			oldValue = new ArrayList<String>();
		}
		while (true) {
			try {
				TextUi.printListWithPrompt(prompt, oldValue);
				final String propertyValue = TextUi.reader.readLine();
				if (propertyValue.equals("") && oldValue != null) {
					return oldValue;
				}
				if (propertyValue.startsWith("+")) {
					TextUi.LOGGER.warning("Adding new value");
					// List newList = DataTypeParserHelper
					// .parseStringToList(propertyValue.substring(1));
					oldValue.add(propertyValue.substring(1));
					continue;
				}
				if (propertyValue.startsWith("-")) {
					final String number = propertyValue.substring(1);
					final int num = Integer.parseInt(number);
					TextUi.LOGGER.warning("Removing field " + num);
					oldValue.remove(num);
					continue;
				}
				// parse a new one
				// oldValue =
				// DataTypeParserHelper.parseStringToList(propertyValue);
			} catch (final Exception ex) {
				// should never happen
				TextUi.LOGGER.severe(ex.toString());
				return null;
			}
		}
	}

	/**
	 * Customized for source files - files from which you want to read
	 * 
	 * @param prompt
	 * @return
	 */
	public static File inputExistingFile(String prompt) {
		return TextUi.inputFile(prompt, true);
	}

	/**
	 * Allows to input a file name, doing various checks
	 * 
	 * @param prompt
	 * @param checkForExistence -
	 *            if true, it checks for the existence of the file
	 * @return
	 */
	public static File inputFile(String prompt, boolean checkForExistence) {
		prompt = TextUi.processPrompt(prompt);
		while (true) {
			try {
				TextUi.writer.print(prompt);
				final String propertyValue = TextUi.reader.readLine();
				if (propertyValue.equals("")) {
					return null;
				}
				final File theFile = new File(propertyValue);
				if (!checkForExistence || theFile.exists()) {
					return theFile;
				}
				TextUi.println("Unexistent file, check the spelling!");
			} catch (final IOException ex) {
				// should never happen
				TextUi.LOGGER.severe(ex.toString());
				return null;
			}
		}
	}

	/**
	 * Inputs a directory through iterative navigation
	 * 
	 * @param prompt
	 * @return
	 */
	public static File inputDirectory(String prompt) {
		File currentDir = new File(".");
		currentDir = currentDir.getAbsoluteFile();
		while (true) {
			final List<String> menuItems = new ArrayList<String>();
			final File dirs[] = currentDir.listFiles();
			for (int i = 0; i != dirs.length; i++) {
				if (dirs[i].isDirectory()) {
					menuItems.add(dirs[i].getName());
				}
			}
			menuItems.add("-");
			menuItems.add("UP");
			menuItems.add("HOME");
			menuItems.add("CANCEL");
			menuItems.add("DIRECT ENTRY");
			menuItems.add("ACCEPT");
			final String value =
					TextUi.menu(menuItems, null, prompt + "\nCurrent dir is:"
							+ currentDir + "\nNavigate directory: ");
			if (value.equals("UP")) {
				currentDir = currentDir.getParentFile();
				continue;
			}
			if (value.equals("HOME")) {
				currentDir =
						new File(java.lang.System.getProperty("user.home"));
				continue;
			}
			if (value.equals("ACCEPT")) {
				return currentDir;
			}
			if (value.equals("CANCEL")) {
				return null;
			}
			if (value.equals("DIRECT ENTRY")) {
				try {
					TextUi.writer.print("Type in directory: ");
					final String propertyValue = TextUi.reader.readLine();
					final File newDir = new File(propertyValue);
					if (newDir.isDirectory()) {
						currentDir = newDir;
					}
					TextUi.println("Not a directory: " + newDir);
				} catch (final IOException ex) {
					// should never happen
					TextUi.LOGGER.severe(ex.toString());
					return null;
				}
			}
			currentDir = new File(currentDir, value);
		}
	}

	/**
	 * 
	 * FIXME: if the startdir does not exist, it crashes!!! Inputs a file with
	 * navigation
	 * 
	 * @return
	 */
	public static File inputFileWithNavigation(String startDir, String pattern) {
		File currentDir = new File(startDir);
		if (!currentDir.exists()) {
			TextUi.errorPrint("The specified directory " + startDir
					+ " does not exist, starting in current dir");
			currentDir = new File(".");
		}
		currentDir = currentDir.getAbsoluteFile();
		while (true) {
			final List<String> menuItems = new ArrayList<String>();
			final File dirs[] = currentDir.listFiles();
			for (int i = 0; i != dirs.length; i++) {
				if (dirs[i].isDirectory()) {
					menuItems.add(dirs[i].getName());
				}
			}
			menuItems.add("-");
			for (int i = 0; i != dirs.length; i++) {
				if (!dirs[i].isDirectory()
						& Glob.match(pattern, dirs[i].getName())) {
					menuItems.add(dirs[i].getName());
				}
			}
			menuItems.add("-");
			menuItems.add("UP");
			menuItems.add("HOME");
			menuItems.add("CANCEL");
			final String value =
					TextUi.menu(menuItems, null, "Current dir is:" + currentDir
							+ "\nNavigate directory or select the file: ");
			if (value.equals("HOME")) {
				currentDir =
						new File(java.lang.System.getProperty("user.home"));
				continue;
			}
			if (value.equals("UP")) {
				currentDir = currentDir.getParentFile();
				continue;
			}
			if (value.equals("CANCEL")) {
				return null;
			}
			currentDir = new File(currentDir, value);
			if (currentDir.isFile()) {
				return currentDir;
			}
		}
	}

	/**
	 * @param parentDirectory
	 * @param oldValues
	 * @return
	 */
	public static List<String> inputListOfFileNames(File parentDirectory,
			List<String> oldValues) {
		List<String> newValues = new ArrayList<String>(oldValues);
		final List<String> possibleValues = new ArrayList<String>();
		if (!parentDirectory.isDirectory()) {
			throw new Error("Call this method with an existing directory!");
		}
		// fill the Vector with the (top level) files
		final File content[] = parentDirectory.listFiles();
		for (int i = 0; i != content.length; i++) {
			if (content[i].isDirectory()) {
				continue;
			}
			possibleValues.add(content[i].getName());
		}
		// create a menu with the possible values
		while (true) {
			final ArrayList<String> menuItems = new ArrayList<String>();
			menuItems.addAll(possibleValues);
			// remove the ones already in the collection
			for (final Iterator<String> it = newValues.iterator(); it.hasNext();) {
				if (menuItems.contains(it.next())) {
					menuItems.remove(it.next());
				}
			}
			menuItems.add("-");
			menuItems.add(TextUi.RESTART);
			menuItems.add(TextUi.EMPTY);
			menuItems.add(TextUi.DONE);
			final String result = TextUi.menu(menuItems, null, null);
			if (result.equals(TextUi.RESTART)) {
				newValues = new ArrayList<String>(oldValues);
			}
			if (result.equals(TextUi.DONE)) {
				return newValues;
			}
			if (result.equals(TextUi.EMPTY)) {
				newValues = new ArrayList<String>();
			}
		}
	}

	private static List<String> prepareChoices(List<String> currentList,
			List<String> choices) {
		final List<String> currentChoices = new ArrayList<String>();
		for (final Iterator<String> it = choices.iterator(); it.hasNext();) {
			final String item = (String) it.next();
			if (!currentList.contains(item)) {
				currentChoices.add(item);
			}
		}
		currentChoices.add("-");
		currentChoices.add(TextUi.RESTART);
		currentChoices.add(TextUi.ADD_ALL);
		currentChoices.add(TextUi.DONE);
		return currentChoices;
	}

	public static List<String> selectFromChoices(List<String> oldList,
			List<String> choices) {
		List<String> currentList = new ArrayList<String>(oldList);
		while (true) {
			final List<String> currentChoices =
					TextUi.prepareChoices(currentList, choices);
			final String prompt =
					"Current elements: " + currentList + "\nChoice: ";
			final String choice =
					TextUi.menu(currentChoices, TextUi.DONE, prompt);
			if (choice == TextUi.DONE) {
				return currentList;
			}
			if (choice == TextUi.RESTART) {
				currentList = new ArrayList<String>();
				continue;
			}
			if (choice == TextUi.ADD_ALL) {
				currentList = new ArrayList<String>(choices);
				continue;
			}
			currentList.add(choice);
		}
	}

	public static void println(Object o) {
		TextUi.writer.println(o.toString());
	}

	public static void println(String text) {
		TextUi.writer.println(text);
	}

	public static void print(String text) {
		TextUi.writer.print(text);
	}

	public static void printHeader(String text) {
		TextUi.drawLine();
		final int whiteSpaces = (TextUi.SEPARATOR.length() - text.length()) / 2;
		if (whiteSpaces > 0) {
			final StringBuilder sb = new StringBuilder();
			for (int i = 0; i != whiteSpaces; i++) {
				sb.append(' ');
			}
			TextUi.print(sb.toString());
		}
		TextUi.println(text);
		TextUi.drawLine();
	}

	public static String shiftText(int pads, String padding, String text) {
		String paddingString = "";
		for (int i = 0; i != pads; i++) {
			paddingString = paddingString + padding;
		}
		final String preparedText =
				paddingString + text.replaceAll("\n", "\n" + paddingString);
		return preparedText;
	}

	public static String shiftTextWithSpaces(int pads, String text) {
		return TextUi.shiftText(pads, " ", text);
	}

	/**
	 * @param string
	 * @return
	 */
	public static boolean inputBoolean(String prompt) {
		prompt = TextUi.processPrompt(prompt);
		TextUi.writer.print(prompt);
		try {
			final String value = TextUi.reader.readLine();
			if (value.equals("y") || value.equals("Y")) {
				return true;
			} else {
				return false;
			}
		} catch (final Exception ex) {
			// should never happen
			TextUi.LOGGER.severe(ex.toString());
			return false;
		}
	}

	public static int tripleChoice(String prompt, boolean defaultChoice) {
		prompt = TextUi.processQuestionPrompt(prompt);
		while (true) {
			if (defaultChoice) {
				TextUi.writer.print(prompt + " ([y]/n/a): ");
			} else {
				TextUi.writer.print(prompt + " (y/[n]/a): ");
			}
			try {
				final String value = TextUi.reader.readLine();
				if (value.equals("")) {
					if (defaultChoice) {
						return TextUi.CHOICE_YES;
					} else {
						return TextUi.CHOICE_NO;
					}
				}
				if (value.equals("y") || value.equals("Y")) {
					return TextUi.CHOICE_YES;
				}
				if (value.equals("n") || value.equals("N")) {
					return TextUi.CHOICE_NO;
				}
				if (value.equals("a") || value.equals("A")) {
					return TextUi.CHOICE_ABORT;
				}
				TextUi.println("Incorrect value entered, use y, n or a!");
			} catch (final Exception ex) {
				// should never happen
				TextUi.LOGGER.severe(ex.toString());
				return TextUi.CHOICE_NO;
			}
		}
	}

	/**
	 * @param string
	 * @return
	 */
	public static boolean confirm(String prompt, boolean defaultChoice) {
		prompt = TextUi.processQuestionPrompt(prompt);
		while (true) {
			if (defaultChoice) {
				TextUi.writer.print(prompt + " ([y]/n): ");
			} else {
				TextUi.writer.print(prompt + " (y/[n]): ");
			}
			try {
				final String value = TextUi.reader.readLine();
				if (value.equals("")) {
					return defaultChoice;
				}
				if (value.equals("y") || value.equals("Y")) {
					return true;
				}
				if (value.equals("n") || value.equals("N")) {
					return false;
				}
				TextUi.println("Incorrect value entered, use y or n!");
			} catch (final Exception ex) {
				// should never happen
				TextUi.LOGGER.severe(ex.toString());
				return false;
			}
		}
	}

	private static String processPrompt(String prompt) {
		if (prompt.endsWith("=") || prompt.endsWith(":")
				|| prompt.endsWith("= ")) {
			return prompt;
		}
		return prompt + " = ";
	}

	private static String processQuestionPrompt(String prompt) {
		if (prompt.endsWith("?") || prompt.endsWith("? ")) {
			return prompt;
		}
		return prompt + "?";
	}

	/**
	 * @param string
	 */
	public static void errorPrint(String string) {
		TextUi.errorWriter.println(string);
	}

	/**
	 * @param string
	 * @return
	 */
	public static File inputDestinationFile(String prompt) {
		return TextUi.inputFile(prompt, false);
	}

	/**
	 * @param string
	 * @return
	 */
	public static String inputPassword(String string) {
		return TextUi.inputString(string);
	}

	/**
	 * 
	 */
	public static void drawLine() {
		TextUi
				.println("--------------------------------------------------------------");
	}

	/**
	 * Writes a debug message to the error output. It can be turned on and off
	 * with setDebugMessages
	 * 
	 * @param message
	 */
	public static void debug(String message) {
		if (TextUi.writeDebugMessage) {
			TextUi.errorPrint("DEBUG:" + message);
		}
	}

	/**
	 * Toggles the writing of the debug messages.
	 * 
	 * @param wdm
	 */
	public static void setDebugMessages(boolean wdm) {
		TextUi.writeDebugMessage = wdm;
	}
}
