package it.micegroup.voila2.cartridge.library.javaservice;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.emf.mwe.core.resources.ResourceLoaderFactory;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;

import it.micegroup.voila2.cartridge.library.formatters.JavaImportBeautifierImpl;

public class JavaBeautifier {

	private static final Logger LOGGER = Logger.getLogger(JavaBeautifier.class.getName());

	static {
		LOGGER.setUseParentHandlers(false);

		ConsoleHandler handler = new ConsoleHandler();
		Formatter formatter = new LogFormatter();

		handler.setFormatter(formatter);

		LOGGER.addHandler(handler);
	}
	
	public JavaBeautifier() {
		super();

		configFile = GeneralUtils.getBaseDir() + "/.settings/org.eclipse.jdt.core.prefs";
		getCodeFormatter();
		
		importFormatter = new JavaImportBeautifierImpl();
		importFormatter.setFormat(false);
		importFormatter.setOrganizeImports(true);
	}

	/** Singleton code formatter instance. */
	private CodeFormatter codeFormatter;
	private JavaImportBeautifierImpl importFormatter;

	/**
	 * Path to the config file for the formatter. See
	 * http://www.peterfriese.de/index
	 * .php/2007/05/28/formatting-your-code-using-the-eclipse-code-formatter/ for
	 * more infor on creating the config file.
	 */
	private String configFile;

	private Properties options;

	/**
	 * Formats the file using Eclipse code formatter. The file must have the
	 * extension '.java'.
	 */
	public String format(final String input) {
//		CharacterSequence sequence = new CharacterSequence(input);
//		importFormatter.beautify(sequence); //organize imports

		String result = format(input, null); //Formatting the code;
		return result;
	}

	/**
	 * Formats the file using Eclipse code formatter. The file must have the
	 * extension '.java'.
	 */
	public String format(final String input, final String fileName) {
		//IFile file = ResourcesPlugin.getWorkspace().getRoot().getProject("gcerti").findMember(fileName.substring(6));
		IDocument doc = new Document(input);
		TextEdit edit = getCodeFormatter().format(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS,
				doc.get(), 0, doc.get().length(), 0, null);
		
		// check if text formatted successfully
		if (edit != null) {
			try {
				edit.apply(doc);
				/*
				ASTParser parser = ASTParser.newParser(AST.JLS11);
				// Parse the class as a compilation unit.
				parser.setKind(ASTParser.K_COMPILATION_UNIT);
				parser.setSource(doc.get().toCharArray());
				parser.setResolveBindings(true);
				// Return the compiled class as a compilation unit
				ICompilationUnit cu = (ICompilationUnit) parser.createAST(null);
				//cu = new BasicCompilationUnit(doc.get().toCharArray(), "it", "tmp.java", "utf-8");
				ImportRewrite ir = ImportRewrite.create(cu, true);
				TextEdit editOi = ir.rewriteImports(null);
				editOi.apply(doc);
				*/
				return doc.get();
			} catch (MalformedTreeException e) {
				LOGGER.log(Level.WARNING,
						"Error during code formatting. Illegal code edit tree (" + e.getMessage() + ").");
			} catch (BadLocationException e) {
				ConsoleUtils.consoleMessage("WARN",
						"Error during code formatting. Bad location (" + e.getMessage() + ").");
//			} catch (CoreException e) {
//				ConsoleUtils.consoleMessage("WARN",
//						"Error during code formatting. Bad location (" + e.getMessage() + ").");
			}
		} else {
			ConsoleUtils.consoleMessage("WARN", "File " + fileName
					+ " could not be formatted. Make sure your template produces legal Java code!");
		}
		return null;
	}

	/**
	 * Returns an instance of the Eclipse code formatter. If the user supplied the
	 * path to a config file, this file will be used to configure the code
	 * formatter. Otherwise we use the default options supplied with oAW.
	 *
	 * @return a preconfigured instance of the Eclipse code formatter.
	 */
	private CodeFormatter getCodeFormatter() {
		if (codeFormatter == null) {

			if (configFile == null) {
				loadDefaultFormatterOptions();
				LOGGER.log(Level.INFO,
						"no config file specified; using the default config file supplied with oAW: org.eclipse.jdt.core.formatterprefs");
			} else {
				options = readConfig(configFile);
			}

			// instantiate the formatter
			codeFormatter = ToolFactory.createCodeFormatter(options);
		}
		return codeFormatter;
	}

	private void loadDefaultFormatterOptions() {
		options = new Properties();
		options.put("org.eclipse.jdt.core.compiler.compliance", "1.5");
		options.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform", "1.5");
		options.put("org.eclipse.jdt.core.compiler.source", "1.5");
	}

	/**
	 * Return a Java Properties file representing the options that are in the
	 * specified config file.
	 */
	private Properties readConfig(String filename) {
		BufferedInputStream stream = null;
		BufferedReader reader = null;

		try {
			InputStream is = openStream(filename);
			final Properties formatterOptions = new Properties();
			if (filename.endsWith(".xml")) {
				Pattern pattern = Pattern.compile("<setting id=\"([^\"]*)\" value=\"([^\"]*)\"\\/>");
				reader = new BufferedReader(new InputStreamReader(is));
				for (String line = reader.readLine(); line != null; line = reader.readLine()) {
					Matcher matcher = pattern.matcher(line);
					if (matcher.matches()) {
						formatterOptions.put(matcher.group(1), matcher.group(2));
					}
				}
			} else {
				stream = new BufferedInputStream(is);
				formatterOptions.load(stream);
			}
			// add some settings for the compiler options
			// which are not included in the Eclipse code style settings
			// to make the code formatter working
			// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=222736
			if (formatterOptions.get("org.eclipse.jdt.core.compiler.compliance") == null) {
				formatterOptions.put("org.eclipse.jdt.core.compiler.compliance", "1.5");
			}
			if (formatterOptions.get("org.eclipse.jdt.core.compiler.codegen.targetPlatform") == null) {
				formatterOptions.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform", "1.5");
			}
			if (formatterOptions.get("org.eclipse.jdt.core.compiler.source") == null) {
				formatterOptions.put("org.eclipse.jdt.core.compiler.source", "1.5");
			}
			return formatterOptions;
		} catch (IOException e) {
			LOGGER.log(Level.WARNING, "Problem reading code formatter config file (" + e.getMessage() + ").");
			loadDefaultFormatterOptions();
		} finally {
			if (stream != null) {
				try {
					stream.close();
				} catch (IOException e) {
					/* ignore */
				}
			}
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					/* ignore */
				}
			}
		}
		return null;
	}

	/**
	 * Searches for the given filename as a ressource and returns a stream on it.
	 * Throws an IOException, if the file cannot be found.
	 *
	 * @param filename The name of the file to be searched in the ressources.
	 * @return InputStream for subsequent reading
	 * @throws IOException
	 */
	protected InputStream openStream(String filename) throws IOException {
		InputStream is = null;
		try {
			is = ResourceLoaderFactory.createResourceLoader().getResourceAsStream(filename);
		} catch (RuntimeException e) {
		}
		if (is == null) {
			is = new FileInputStream(filename);
		}
		return is;
	}

	/**
	 * @return the configuration file for the formatter
	 */
	public String getConfigFile() {
		return configFile;
	}

	/**
	 * @param configFile configuration file for the formatter
	 */
	public void setConfigFile(String configFile) {
		this.configFile = configFile;
	}

}
