package org.etsi.mts.tdl.importers.ui.handlers;

import java.util.Arrays;
import java.util.LinkedHashMap;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.ui.resource.IResourceSetProvider;
import org.etsi.mts.tdl.Package;
import org.etsi.mts.tdl.asn2tdl.ASN2TDLTranslator;
import org.etsi.mts.tdl.helper.TDLHelper;
import org.etsi.mts.tdl.json2tdl.JSON2TDLTranslator;
import org.etsi.mts.tdl.openapi2tdl.next.OpenAPI2TDLTranslatorNext;
import org.etsi.mts.tdl.to2tdl.TO2TDLTranslator;
import org.etsi.mts.tdl.transform.AbstractTranslator;
import org.etsi.mts.tdl.yang2tdl.Yang2TDLTranslator;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;

/**
 * Our sample handler extends AbstractHandler, an IHandler base class.
 * @see org.eclipse.core.commands.IHandler
 * @see org.eclipse.core.commands.AbstractHandler
 */
public class TranslationHandler extends AbstractHandler {
	@Inject Injector injector;
	@Inject Provider<ResourceSet> rsp;
	@Inject Provider<XtextResourceSet> xrsp;

	@Inject
	IResourceSetProvider resourceSetProvider;
	LinkedHashMap<String, String> targetFormats = new LinkedHashMap<>();

	public static String translationTarget = "TDL XF (Part 3, XMI)";
	
	private IWorkbenchWindow window;

	/**
	 * The constructor.
	 */
	public TranslationHandler() {
		init();
	}

	private void init() {
		//TODO: split into multiple importers or make independent from openAPI, ASN and others, similar to different target syntaxes
		//TODO: update rt.ui as well
		//TODO: do not reload after first init
		if (!targetFormats.isEmpty()) {
//			return;
		}
		targetFormats.clear();
		targetFormats.put("TDL XF (Part 3, XMI)", "tdl");
		loadTargetFormat("org.etsi.mts.tdl.tx", "TDL TX (Part 8, Braces)", "tdltx");
		loadTargetFormat("org.etsi.mts.tdl.txi", "TDL TX (Part 8, Indentation)", "tdltxi");
		loadTargetFormat("org.etsi.mts.tdl.TDLan2", "TDLan2 (Part 1, Annex B)", "tdlan2");
		loadTargetFormat("org.etsi.mts.tdl.TPLan2", "TPLan2 (Part 4, Annex B)", "tplan2");
	}

	private void loadTargetFormat(String bundleName, String label, String extension) {
		if (Platform.getBundle(bundleName) != null) {
			targetFormats.put(label, extension);
		}
	}

	/**
	 * the command has been executed, so extract extract the needed information
	 * from the application context.
	 */
	public Object execute(ExecutionEvent event) throws ExecutionException {
		init();
		ISelection selection = HandlerUtil.getCurrentSelection(event);
		IEditorInput input = HandlerUtil.getActiveEditorInput(event);
		IFile file = null;
		IContainer container = null; 
		if (input != null && input instanceof FileEditorInput) {
			file = ((FileEditorInput) input).getFile();
		} else if (selection !=null && selection instanceof IStructuredSelection) {
			IStructuredSelection structuredSelection = (IStructuredSelection) selection;
			Object firstElement = structuredSelection.getFirstElement();
			if (firstElement instanceof IFile) {
				file = (IFile) firstElement;
			}
			if (firstElement instanceof IContainer) {
				container = (IContainer) firstElement;
			}
		}
		
		if (file !=null || container != null) {
			//TODO: handle directories as well..
			URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
			ResourceSet rs = new ResourceSetImpl();
			
			String filepath = file.getLocation().toFile().getAbsolutePath();
			String type = file.getFileExtension();
			ElementListSelectionDialog dialog = new ElementListSelectionDialog(Display.getDefault().getActiveShell(), new LabelProvider());
			dialog.setTitle("Import configuration");
			dialog.setMessage("Importing "+type+" from "+file.getName()
								+"\n\nSelect the target format");
			dialog.setElements(targetFormats.keySet().toArray());
			
			dialog.setInitialElementSelections(Arrays.asList(new String[] {TranslationHandler.translationTarget}));

			// user pressed cancel
			if (dialog.open() != Window.OK) {
				return false;
			} else {
				injector = Guice.createInjector();
				Object[] result = dialog.getResult();
				String selected = (String)result[0];
				TranslationHandler.translationTarget = selected;
				String extension = targetFormats.get(selected);
				URI targetURI = URI.createURI(uri.toString()+"-generated."+extension);
				XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
				Resource tr = resourceSet.createResource(targetURI);
				AbstractTranslator translator = null;
				if (type.contains("asn")) {
					translator = new ASN2TDLTranslator();
				}
				if (type.contains("json")) {
					translator = new JSON2TDLTranslator();
				}
				if (type.contains("yaml")) {
					translator = new OpenAPI2TDLTranslatorNext();
				}
				if (type.contains("yang")) {
					translator = new Yang2TDLTranslator();
					//determine limit for looking for related module definitions 
					//also if linked folders are used
					if (container == null) {
						container = file.getParent();
					}
					while (container.getParent() != null 
							&& !container.isLinked()
							&& container.getType() == IResource.FOLDER
							) {
						container = container.getParent();
					}
					String limit = container.getLocation().toFile().getAbsolutePath();
					((Yang2TDLTranslator) translator).setLimit(limit);
				}
				if (translator != null) {
					translator.setTargetResource(tr);
					translator.initTargetResource(translator.getCleanName(file.getName()));
					try {
						translator.translate(filepath);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						showErrorDialog(e);
					}
				}
				
				if (type.contains("tplan2")) { //TODO: also new syntax
					//TODO: this does not work at present
					//TODO: add option to simply translate rather than transform
					//TODO: update names to distinguish between translation and transformation
					//TODO: cleanup wizards, there should be only one clear way how to do everything 
					Resource r = rs.getResource(uri, true);
					Package p = (Package) r.getContents().get(0);
					translator = new TO2TDLTranslator();
					translator.setTargetResource(tr);
					translator.initTargetResource("generated_from_"+p.getName());
					translator.addImports(p);
					((TO2TDLTranslator)translator).transform(p);
				}
				//tr.getContents().addAll(EcoreUtil.copyAll(r.getContents()));

				try {
					TDLHelper.store(tr, true);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					showErrorDialog(e);
				}
			}
		}
		//TODO: throw proper execution exceptions?
		return null;
	}

	private void showErrorDialog(Exception e) {
		String message = e.getMessage();
		Throwable cause = e;
		while ((cause = cause.getCause()) != null) {
			message+="\n"+"Caused by: \n\t"+cause.getMessage();
		}
		MessageDialog errorDialog = new MessageDialog(
				Display.getDefault().getActiveShell(), 
				"Translation error...", 
				null, 
				"An error occurred during the translation. "
				+ "It may be due to problems occurring while processing the input, "
				+ "e.g if the input could not be processed. "
				+ "See details for a more technical description:\n\n"
				+ message
				,		
				MessageDialog.ERROR, 
				0, 
				new String[] {"OK"});
		errorDialog.open();
	}
	
	public void init(IWorkbenchWindow window) {
		this.window = window;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

}
