package it.mice.voila.wizard.plugin;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.eclipse.acceleo.engine.service.AbstractAcceleoGenerator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWizard;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.ide.IDE;
import org.osgi.framework.Bundle;

import it.micegroup.voila2.cartridge.library.javaservice.FileCopyUtils;
import it.micegroup.voila2.cartridge.library.javaservice.GeneralUtils;

/**
 * This is a sample new wizard. Its role is to create a new file resource in the
 * provided container. If the container resource (a folder or a project) is
 * selected in the workspace when the wizard is opened, it will accept it as the
 * target container. The wizard creates one file with the extension "mpe". If a
 * sample multi-page editor (also available as a template) is registered for the
 * same extension, it will be able to open it.
 */

public class NewProjectWizard extends Wizard implements INewWizard {
	protected WizardPage stylePage;
	protected WizardPage projectPage;
	protected ISelection selection;

	private DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

	/**
	 * Constructor for SampleNewWizard.
	 */
	public NewProjectWizard() {
		super();
		setNeedsProgressMonitor(true);
		setHelpAvailable(true);
		setWindowTitle("MDD New Project Wizard");
	}

	public NewProjectWizardStackPage getProjectPage() {
		return (NewProjectWizardStackPage) projectPage;
	}

	public NewProjectWizardStylesPage getStylePage() {
		return (NewProjectWizardStylesPage) stylePage;
	}

	/**
	 * Adding the page to the wizard.
	 */
	public void addPages() {
		stylePage = new NewProjectWizardStylesPage();
		addPage(stylePage);
		projectPage = new NewProjectWizardStackPage(selection, "togetherAdfgenComplexConfigurationWithSecurity");
		addPage(projectPage);
	}

	/**
	 * We will accept the selection in the workbench to see if we can initialize
	 * from it.
	 * 
	 * @see IWorkbenchWizard#init(IWorkbench, IStructuredSelection)
	 */
	public void init(IWorkbench workbench, IStructuredSelection selection) {
		this.selection = selection;
	}

	public boolean canFinish() {
		return (super.canFinish() && getProjectPage().isCurrentPage());
	}

	/**
	 * This method is called when 'Finish' button is pressed in the wizard. We will
	 * create an operation and run it using wizard as execution context.
	 */
	public boolean performFinish() {
		/**
		 * Added toLowerCase to avoid wrong naming conventions Replace of "-" which is
		 * not an allowed char for package declarations
		 */

		final String projectId = (!getProjectPage().getProjectName().contains("-"))
				? getProjectPage().getProjectName().toLowerCase()
				: getProjectPage().getProjectName().toLowerCase().replace("-", ""); // lowercase x standard java package
																					// name
		final String projectName = (!getProjectPage().getRootProjectName().contains("-"))
				? getProjectPage().getRootProjectName().toLowerCase()
				: getProjectPage().getRootProjectName().toLowerCase().replace("-", "");// lowercase x accessibilità
																						// project name
		final String projectDescription = getProjectPage().getProjectDescription();
		final String basePackageName = (!getProjectPage().getBasePackageName().contains("-"))
				? getProjectPage().getBasePackageName()
				: getProjectPage().getBasePackageName().replace("-", "");
		// Added substring to sync the project name with the proper folder name
		final String targetFolder = getProjectPage().getRootTargetLocation().substring(0,
				getProjectPage().getRootTargetLocation().lastIndexOf("/") + 1) + projectId;
		final boolean isDefaulLocation = getProjectPage().isDefaultLocation();

		IRunnableWithProgress op = new IRunnableWithProgress() {
			public void run(IProgressMonitor monitor) throws InvocationTargetException {
				try {
					doFinish(projectId, projectName, projectDescription, basePackageName, targetFolder,
							isDefaulLocation, monitor);
				} catch (CoreException e) {
					throw new InvocationTargetException(e);
				} finally {
					monitor.done();
				}
			}
		};
		try {
			getContainer().run(true, false, op);
		} catch (InterruptedException e) {
			return false;
		} catch (InvocationTargetException e) {
			Throwable realException = e.getTargetException();
			MessageDialog.openError(getShell(), "Error", realException.getMessage()); //$NON-NLS-1$
			return false;
		}
		return true;
	}

	/**
	 * The worker method. It will find the container, create the file if missing or
	 * just replace its contents, and open the editor on the newly created file.
	 * 
	 * @param fileName2
	 * @param containerName2
	 * @param projectName
	 */

	private void doFinish(String projectId, String projectName, String projectDescription, String basePackageName,
			String targetFolder, boolean isDefaultLocation, IProgressMonitor monitor) throws CoreException {

		// create a sample project
		monitor.beginTask(Policy.bind("Message.GeneratingRootProject", projectName), IProgressMonitor.UNKNOWN); //$NON-NLS-1$
		try {
			generateRootProject(projectId, projectName, projectDescription, basePackageName, targetFolder, monitor);
		} catch (Exception e1) {
			throwCoreException(Policy.bind("Message.GenerationFailed", e1.getMessage())); //$NON-NLS-1$
		}

		monitor.setTaskName(Policy.bind("Message.CreatingProject", projectName)); //$NON-NLS-1$

		IProject currProject = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
		IProjectDescription newProjectDesc = ResourcesPlugin.getWorkspace()
				.newProjectDescription(currProject.getName());
		newProjectDesc.setLocation(new Path(targetFolder));
		currProject.create(newProjectDesc, monitor);
		currProject.open(monitor);

		currProject = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName + "-model");
		newProjectDesc = ResourcesPlugin.getWorkspace().newProjectDescription(currProject.getName());
		newProjectDesc.setLocation(new Path(targetFolder + "/modules/" + projectName + "-model/"));
		currProject.create(newProjectDesc, monitor);
		currProject.open(monitor);

		monitor.setTaskName(Policy.bind("Message.OpeningModel")); //$NON-NLS-1$
		getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				try {
					PlatformUI.getWorkbench().showPerspective("org.eclipse.papyrus.infra.core.perspective", //$NON-NLS-1$
							PlatformUI.getWorkbench().getActiveWorkbenchWindow());
				} catch (WorkbenchException e) {
					e.printStackTrace();
				}
			}
		});

		monitor.setTaskName(Policy.bind("Message.OpeningConfFile")); //$NON-NLS-1$
		// final IFile file = currProject.getFile(new
		// Path("src/workflow/workflow.properties")); //$NON-NLS-1$
		final IFile file = currProject.getFile(new Path("src/main/resources/model/" + projectId + "_model.di")); //$NON-NLS-1$
		if (!FileCopyUtils.exists(file)) {
			throwCoreException(Policy.bind("Message.VoilaPropDoNotExist", file.toString())); //$NON-NLS-1$
		}
		getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
				try {
					IDE.openEditor(page, file, true);
				} catch (PartInitException e) {
				}
			}
		});
	}

	private void generateRootProject(String projectId, String projectName, String projectDescription,
			String basePackageName, String targetFolder, IProgressMonitor monitor) throws Exception {
		ClassLoader backupClassLoader = Thread.currentThread().getContextClassLoader();

		try {
//    		System.setProperty("org.apache.commons.logging.LogFactory", "org.apache.commons.logging.impl.Log4JLogger");
			System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Log4JLogger");

			String stackBundleName = getProjectPage().getStackConfig().getDeclaringExtension().getContributor()
					.getName();
			String archetypeBundleName = getProjectPage().getArchetypeConfig().getDeclaringExtension().getContributor()
					.getName();
			String workflowName = getProjectPage().getArchetypeConfig().getAttribute("wizardConfigurationId");

			Bundle stackBundle = Platform.getBundle(stackBundleName);
			URL urlStackBundle = stackBundle.getEntry("/");
			String stackTargetDir = FileLocator.toFileURL(urlStackBundle).getPath().substring(1) + "target/";

			Bundle archetypeBundle = Platform.getBundle(archetypeBundleName);
			URL urlArchetypeBundle = archetypeBundle.getEntry("/");
			String archetypeTargetDir = FileLocator.toFileURL(urlArchetypeBundle).getPath().substring(1) + "target/";

			URL[] url = new URL[] { new URL(("file:///" + archetypeTargetDir)),
					new URL(("file:///" + stackTargetDir)) };
			ClassLoader myClassLoader = new URLClassLoader(url, backupClassLoader);
			Thread.currentThread().setContextClassLoader(myClassLoader);

			LogManager.resetConfiguration();
			PropertyConfigurator.configure(archetypeTargetDir + "voila.log4j.properties");
			// Utility.setupConsole(null);

			// AdfgenUtils.prepareExternalBundleSystemProperties(stackBundleName,
			// archetypeBundleName);

			Properties properties = new Properties();
			// Map<String, String> properties = new HashMap<String, String>();
			// Map<String, Object> slotContents = new HashMap<String, Object>();

			properties.put("basedir", targetFolder + "/"); //$NON-NLS-1$ //$NON-NLS-2$
			properties.put("projectId", projectId); //$NON-NLS-1$
			properties.put("projectDescription", projectDescription); //$NON-NLS-1$
			properties.put("base.package.name", basePackageName); //$NON-NLS-1$
			properties.put("baseGroupName", basePackageName.substring(0, basePackageName.lastIndexOf('.'))); //$NON-NLS-1$
			properties.put("basePackageAsFolder", basePackageName.replaceAll("\\.", "\\/")); //$NON-NLS-1$
			properties.put("projectDate", df.format(new Date())); //$NON-NLS-1$

			File tempFile = File.createTempFile("voila2", ".properties");
			properties.store(new FileOutputStream(tempFile), null);
			// String wfFile = archetypeTargetDir + archetypeBundleName.replaceAll("\\.",
			// "\\/") + "/workflow.oaw";
			// String wfFile = archetypeTargetDir + workflowName;
			// Logger log = Logger.getLogger(getClass());

			AbstractAcceleoGenerator generator = createMainGenerator(workflowName, targetFolder,
					tempFile.getAbsolutePath(), archetypeBundle);
			// generator.addPropertiesFile(tempFile.getAbsolutePath());
			GeneralUtils.setupProps(generator.getProperties(), targetFolder, ".");

			generator.doGenerate(new BasicMonitor());
			/*
			 * EMY WorkflowRunner theWorkflowRunner = new WorkflowRunner(); Issues result =
			 * theWorkflowRunner.runWorkflow(wfFile , new NullProgressMonitor(), properties,
			 * slotContents); theWorkflowRunner = null; if (result.hasErrors()) { throw new
			 * RuntimeException("Problem during generation, see log file for more information !"
			 * ); }
			 */
		} catch (Throwable e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} finally {
			Thread.currentThread().setContextClassLoader(backupClassLoader);
			// EMY GlobalConfigurationHolder.setConfigurationModel(new
			// GeneratorConfigurationModel(), new IssuesImpl(), true);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private AbstractAcceleoGenerator createMainGenerator(String fqName, String remoteDir, String arguments,
			Bundle archetypeBundle) throws IOException {
		try {
			File target = new File(remoteDir);
			Class clazz = archetypeBundle.loadClass(fqName);
			Constructor c = clazz.getConstructor(File.class, String.class);
			return (AbstractAcceleoGenerator) c.newInstance(target, arguments);
		} catch (Exception e) {
			// TODO: Raise a meaningful error diagnostic
			e.printStackTrace();
			return null; // new Main(model, f, arguments);
		}
	}

	private void throwCoreException(String message) throws CoreException {
		IStatus status = new Status(IStatus.ERROR, "it.mice.voila.wizard.plugin", IStatus.OK, message, null); //$NON-NLS-1$
		throw new CoreException(status);
	}

}