package jp.ac.kyutech.ai.ylab.shiva.nativecapable.plugin.exportwizard;

import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;

import jp.ac.kyutech.ai.ylab.shiva.nativecapable.plugin.NativeCapableJavaPlugin;
import jp.ac.kyutech.ai.ylab.shiva.nativecapable.plugin.Utils;
import jp.ac.kyutech.ai.ylab.shiva.nativecapable.plugin.properties.NativeCapableJavaProjectPropertyPage;

import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
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.ui.IExportWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.console.MessageConsoleStream;

public class NativeCapableExportWizard extends Wizard implements IExportWizard {

	private NativeCapableExportWizardCreationPage pageCreation;
	private NativeCapableExportWizardConfigPage pageConfig;
	private ISelection selection;

	private String[] libs = new String[0];

	public NativeCapableExportWizard() {
		super();
		setNeedsProgressMonitor(true);

	}

	@Override
	public void addPages() {
		pageConfig = new NativeCapableExportWizardConfigPage(selection);
		pageCreation = new NativeCapableExportWizardCreationPage(selection,
				pageConfig);

		addPage(pageCreation);
		addPage(pageConfig);
	}

	private void doFinish(String module, String[] targets, boolean delegate,
			boolean createApplet, boolean createApplication, String width,
			String height, final IProgressMonitor monitor) throws CoreException {

		IResource resource = (IResource) Utils.getSelectedResource(selection);
		resource.refreshLocal(100, monitor);
		IProject project = resource.getProject();
		IJavaProject javaProject = JavaCore.create(project);
		IType type = javaProject.findType(module);
		IPackageFragment pf = type.getPackageFragment();

		IFolder folder = project.getFolder("release");
		if (!folder.exists()) {
			folder.create(true, false, monitor);
		}

		folder = project.getFolder("build");
		if (!folder.exists()) {
			folder.create(true, false, monitor);
		}

		// create Applet
		if (createApplet) {
			String name = type.getElementName() + "Applet";

			StringBuilder buf = new StringBuilder();
			buf.append("public class ").append(name).append(
					" extends NativeCapableApplet {\n");
			buf.append("private static final long serialVersionUID = 1L;\n");
			buf.append("public void init() {\n");
			buf.append("System.setSecurityManager(null);\n");
			buf.append("NativeCapableModuleBuilder builder = ");
			buf.append("new NativeCapableModuleBuilder(this, \"").append(
					type.getFullyQualifiedName()).append("\");\n");
			buf.append("Collection<String> col = new HashSet<String>();\n");
			for (String string : targets) {
				buf.append("col.add(\"").append(string).append(".\");\n");
			}
			if (!delegate) {
				buf.append("col.add(\"").append(
						type.getFullyQualifiedName() + "Applet").append(
						"\");\n");
			}
			buf.append("builder.setTargetPackages(col);\n");
			buf.append("builder.setDelegateTargets(").append(delegate).append(
					");\n");
			buf.append("NativeCapableModule module = builder.newModule();\n");
			buf.append("module.initModule();\n");
			buf.append("add(module.getRootComponent());\n");
			buf.append("}\n");
			buf.append("}");

			ICompilationUnit compilationUnit = pf.createCompilationUnit(name
					+ ".java", buf.toString(), true, monitor);
			compilationUnit.createPackageDeclaration(pf.getElementName(),
					monitor);
			compilationUnit.createImport("java.util.Collection", null, monitor);
			compilationUnit.createImport("java.util.HashSet", null, monitor);
			compilationUnit.createImport(
					"jp.ac.kyutech.ai.ylab.shiva.utils.nativecapable.*", null,
					monitor);

			compilationUnit.save(monitor, true);
		}
		if (createApplication) {
			String name = type.getElementName() + "Application";

			StringBuilder buf = new StringBuilder();
			buf.append("public class ").append(name).append(" {\n");
			buf.append("public static void main(String[] args) {\n");
			buf.append("System.setSecurityManager(null);\n");
			buf
					.append("NativeCapableApplication nca = new NativeCapableApplication();");
			buf.append("NativeCapableModuleBuilder builder = ");
			buf.append("new NativeCapableModuleBuilder(nca, \"").append(
					type.getFullyQualifiedName()).append("\");\n");
			buf.append("Collection<String> col = new HashSet<String>();\n");
			for (String string : targets) {
				buf.append("col.add(\"").append(string).append(".\");\n");
			}
			buf.append("builder.setTargetPackages(col);\n");
			buf.append("builder.setDelegateTargets(").append(delegate).append(
					");\n");
			buf
					.append("final NativeCapableModule module = builder.newModule();\n");
			buf.append("module.initModule();\n");
			buf.append("nca.add(module.getRootComponent());\n");
			buf
					.append("nca.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);\n");
			buf.append("nca.addWindowListener(new WindowAdapter() {\n");
			buf.append("public void windowClosing(WindowEvent e) {\n");
			buf.append("module.destroyModule();\n");
			buf.append("}\n");
			buf.append("});\n");
			buf.append("nca.setBounds(100, 100, " + width + ", " + height
					+ ");\n");
			buf.append("nca.setVisible(true);\n");
			buf.append("module.startModule();\n");
			buf.append("}\n");
			buf.append("}");

			ICompilationUnit compilationUnit = pf.createCompilationUnit(name
					+ ".java", buf.toString(), true, monitor);
			compilationUnit.createPackageDeclaration(pf.getElementName(),
					monitor);
			compilationUnit.createImport("java.util.Collection", null, monitor);
			compilationUnit.createImport("java.util.HashSet", null, monitor);
			compilationUnit.createImport("java.awt.event.WindowAdapter", null,
					monitor);
			compilationUnit.createImport("java.awt.event.WindowEvent", null,
					monitor);
			compilationUnit.createImport("javax.swing.WindowConstants", null,
					monitor);

			compilationUnit.createImport(
					"jp.ac.kyutech.ai.ylab.shiva.utils.nativecapable.*", null,
					monitor);

			compilationUnit.save(monitor, true);
		}

	}

	/**
	 * called at the end of doFinish
	 * 
	 * @param module
	 * @param targets
	 * @param delegate
	 * @param createApplet
	 * @param createApplication
	 * @param monitor
	 * @throws CoreException
	 */
	private void doFinishAnt(String module, String[] targets, boolean delegate,
			boolean createApplet, boolean createApplication,
			final IProgressMonitor monitor) throws CoreException {

		// create ant file
		IResource resource = (IResource) Utils.getSelectedResource(selection);
		IProject project = resource.getProject();
		IJavaProject javaProject = JavaCore.create(project);
		String target = javaProject.getOption(
				JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);
		// System.out.println("Target = " + target);

		String srcDirPath = project.getPersistentProperty(new QualifiedName("",
				NativeCapableJavaProjectPropertyPage.SRC_DIR_PROPERTY));
		String libDirPath = project.getPersistentProperty(new QualifiedName("",
				NativeCapableJavaProjectPropertyPage.LIB_DIR_PROPERTY));
		String binDirPath = project.getPersistentProperty(new QualifiedName("",
				NativeCapableJavaProjectPropertyPage.BIN_DIR_PROPERTY));

		File f = null;
		try {
			URL url = getClass().getResource("/resources");
			url = FileLocator.toFileURL(url);
			f = new File(url.getFile());
		} catch (Exception e) {
			throw new CoreException(new Status(IStatus.ERROR,
					NativeCapableJavaPlugin.PLUGIN_ID, "Error", e));
		}

		Properties p = new Properties();
		try {
			p.setProperty("file.resource.loader.path", f.getAbsolutePath()
					+ "/");
		} catch (Throwable t) {
			throw new CoreException(new Status(IStatus.ERROR,
					NativeCapableJavaPlugin.PLUGIN_ID, "Error", t));
		}

		try {
			Velocity.init(p);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// lets make a Context and put data into it
		VelocityContext context = new VelocityContext();
		// context.put("groupName", "GROUP NAME");
		context.put("version", "1.0.0");
		context.put("javacTarget", target);
		context.put("srcDir", srcDirPath);
		context.put("binDir", binDirPath);
		context.put("libDir", libDirPath);
		context.put("genkeyStorepass", "password");

		StringBuilder sb = new StringBuilder();
		IFolder libDir = project.getFolder(libDirPath);
		IResource[] resouces = libDir.members();
		Collection<String> col = new HashSet<String>();
		for (IResource r : resouces) {
			if (r.getName().endsWith(".jar")) {
				col.add(r.getName());
				sb.append(r.getName()).append(" ");
			}
		}
		sb.deleteCharAt(sb.length() - 1);
		libs = new String[col.size()];
		libs = col.toArray(libs);
		context.put("classPath", sb);
		context.put("mainClass", module + "Application");

		File dest = null;
		IFile iFile = null;
		try {
			// monitor.beginTask("creating build file", 1);
			iFile = project.getFile("build.xml");
			dest = File.createTempFile("tmp", "");
			BufferedWriter writer = new BufferedWriter(new FileWriter(dest));
			Velocity.mergeTemplate("build.xml", "UTF-8", context, writer);
			writer.flush();
			writer.close();

			// monitor.worked(1);
			if (iFile.exists()) {
				iFile.delete(true, monitor);
			}
			iFile.create(new FileInputStream(dest), true, monitor);
		} catch (Exception e) {
			throw new CoreException(new Status(IStatus.ERROR,
					NativeCapableJavaPlugin.PLUGIN_ID, "Error", e));
		}

		// URL[] urls;
		// try {
		// HashSet<URL> urlSet = new HashSet<URL>();
		// for (IResource r : resouces) {
		// if (r.getName().endsWith(".jar") && r instanceof IFile) {
		// System.out.println(r.getLocation().toFile()
		// .getAbsolutePath());
		// URL url = r.getLocation().toFile().toURI().toURL();
		// urlSet.add(url);
		// }
		// }
		// urls = new URL[urlSet.size()];
		// urlSet.toArray(urls);
		// } catch (Exception e) {
		// throw new CoreException(new Status(IStatus.ERROR,
		// NativeCapableJavaPlugin.PLUGIN_ID, "Error", e));
		// }

		// AntRunner runner = new AntRunner();
		// runner.setBuildFileLocation(iFile.getLocation().toFile()
		// .getAbsolutePath());
		// // runner.setCustomClasspath(urls);
		// runner.setArguments("-Dmessage=Building -verbose");
		// runner.run(monitor);

		File buildFile = new File(iFile.getLocation().toFile()
				.getAbsolutePath());
		Project antProject = new Project();
		antProject.init();

		ProjectHelper.getProjectHelper().parse(antProject, buildFile);

		final MessageConsoleStream stream = NativeCapableJavaPlugin
				.getConsoleStream();
		antProject.addBuildListener(new BuildListener() {

			@Override
			public void buildFinished(BuildEvent event) {
				// stream.println(event.getMessage());

			}

			@Override
			public void buildStarted(BuildEvent event) {
				// stream.println(event.getMessage());

			}

			@Override
			public void messageLogged(BuildEvent event) {
				if (event.getPriority() <= Project.MSG_INFO) {
					stream.println("\t" + event.getMessage());
				}

			}

			@Override
			public void targetFinished(BuildEvent event) {
				// stream.println("[/" + event.getTarget().getName() + "]\n");
			}

			@Override
			public void targetStarted(BuildEvent event) {
				stream.println("[" + event.getTarget().getName() + "]");

			}

			@Override
			public void taskFinished(BuildEvent event) {
			}

			@Override
			public void taskStarted(BuildEvent event) {
				// stream.println("[" + event.getTask().getTaskName() + "]");

			}
		});

		antProject.executeTarget("release");

	}

	/**
	 * called at the end of doFinish
	 * 
	 * write out HTML for APPLET.
	 * 
	 * @param module
	 * @param targets
	 * @param delegate
	 * @param createApplet
	 * @param createApplication
	 * @param monitor
	 * @throws CoreException
	 */
	private void doFinishApplet(String module, String[] targets,
			boolean delegate, boolean createApplet, boolean createApplication,
			String width, String height, final IProgressMonitor monitor)
			throws CoreException {

		if (!createApplet) {
			return;
		}

		IResource resource = (IResource) Utils.getSelectedResource(selection);
		IProject project = resource.getProject();

		// create applet.htm
		{
			File f = null;
			try {
				URL url = getClass().getResource("/resources/applet.htm");
				url = FileLocator.toFileURL(url);
				f = new File(url.getFile());
			} catch (Exception e) {
				e.printStackTrace();
			}

			Properties p = new Properties();
			p.setProperty("file.resource.loader.path", f.getParentFile()
					.getAbsolutePath()
					+ "/");
			try {
				Velocity.init(p);
			} catch (Exception e) {
				e.printStackTrace();
			}
			// lets make a Context and put data into it
			VelocityContext context = new VelocityContext();
			context.put("width", width);
			context.put("height", height);

			StringBuilder sb = new StringBuilder();
			for (String r : libs) {
				sb.append(r).append(",");
			}
			sb.append("nativecapable-application.jar");
			context.put("archive", sb);
			context.put("code", module + "Applet.class");

			File dest = null;
			IFile iFile = null;
			try {
				// monitor.beginTask("creating build file", 1);
				iFile = project.getFile("release/applet.htm");
				dest = File.createTempFile("tmp", "");
				BufferedWriter writer = new BufferedWriter(new FileWriter(dest));
				Velocity.mergeTemplate("applet.htm", "UTF-8", context, writer);
				writer.flush();
				writer.close();

				// monitor.worked(1);
				if (iFile.exists()) {
					iFile.delete(true, monitor);
				}
				iFile.create(new FileInputStream(dest), true, monitor);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * called at the end of doFinish
	 * 
	 * write out JNLP for JWS.
	 * 
	 * @param module
	 * @param targets
	 * @param delegate
	 * @param createApplet
	 * @param createApplication
	 * @param monitor
	 * @throws CoreException
	 */
	private void doFinishJNLP(String module, String[] targets,
			boolean delegate, boolean createApplet, boolean createApplication,
			String width, String height, String codebase,
			final IProgressMonitor monitor) throws CoreException {

		if (!createApplication) {
			return;
		}

		IResource resource = (IResource) Utils.getSelectedResource(selection);
		IProject project = resource.getProject();
		IJavaProject javaProject = JavaCore.create(project);
		String target = javaProject.getOption(
				JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true);

		// create applet.htm
		{
			File f = null;
			try {
				URL url = getClass().getResource("/resources/jws.jnlp");
				url = FileLocator.toFileURL(url);
				f = new File(url.getFile());
			} catch (Exception e) {
				e.printStackTrace();
			}

			Properties p = new Properties();
			p.setProperty("file.resource.loader.path", f.getParentFile()
					.getAbsolutePath()
					+ "/");
			try {
				Velocity.init(p);
			} catch (Exception e) {
				e.printStackTrace();
			}
			// lets make a Context and put data into it
			VelocityContext context = new VelocityContext();
			context.put("version", target + "+");
			context.put("codebase", codebase);

			StringBuilder sb = new StringBuilder();
			sb.append("<jar       href=\"nativecapable-application.jar\"/>")
					.append("\n");
			for (String r : libs) {

				sb.append("<jar       href=\"" + r + "\"/>").append("\n");
			}
			context.put("jars", sb);
			context.put("mainClass", module + "Application");

			File dest = null;
			IFile iFile = null;
			try {
				// monitor.beginTask("creating build file", 1);

				iFile = project.getFile("release/jws.jnlp");
				dest = File.createTempFile("tmp", "");
				BufferedWriter writer = new BufferedWriter(new FileWriter(dest));
				Velocity.mergeTemplate("jws.jnlp", "UTF-8", context, writer);
				writer.flush();
				writer.close();

				// monitor.worked(1);
				if (iFile.exists()) {
					iFile.delete(true, monitor);
				}
				iFile.create(new FileInputStream(dest), true, monitor);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * called at the end of doFinish
	 * 
	 * do end proccess
	 * 
	 * @param module
	 * @param targets
	 * @param delegate
	 * @param createApplet
	 * @param createApplication
	 * @param monitor
	 * @throws CoreException
	 */
	private void doFinishEnd(String module, String[] targets, boolean delegate,
			boolean createApplet, boolean createApplication,
			final IProgressMonitor monitor) throws CoreException {

		IResource resource = (IResource) Utils.getSelectedResource(selection);
		IProject project = resource.getProject();

		// copy index.htm
		{

			StringBuilder buf = new StringBuilder();

			buf.append("<html>");
			buf
					.append("<head><title>Native Capable Application Sample</title></head>");
			buf.append("<body><center>");
			buf.append("<h2>Native Capable Application Sample</h2>");
			if (createApplet) {
				buf.append("<p><a href=\"applet.htm\">Applet</a></p>");
			}
			if (createApplication) {
				buf.append("<p><a href=\"jws.jnlp\">Java Web Start</a></p>");
			}
			buf.append("</center></body>");

			ByteArrayInputStream bis = null;
			try {
				bis = new ByteArrayInputStream(buf.toString().getBytes("sjis"));
			} catch (UnsupportedEncodingException e1) {
				e1.printStackTrace();
				return;
			}

			// File f = null;
			// try {
			// URL url = getClass().getResource("/resources/index.htm");
			// url = FileLocator.toFileURL(url);
			// f = new File(url.toURI());
			// } catch (Exception e) {
			// e.printStackTrace();
			// }

			IFile iFile = null;
			try {
				// monitor.beginTask("creating build file", 1);
				iFile = project.getFile("release/index.htm");

				// monitor.worked(1);
				if (iFile.exists()) {
					iFile.delete(true, monitor);
				}
				iFile.create(bis, true, monitor);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		// copy .htaccess
		{
			File f = null;
			try {
				URL url = getClass().getResource("/resources/htaccess");
				url = FileLocator.toFileURL(url);
				f = new File(url.getFile());
			} catch (Exception e) {
				e.printStackTrace();
			}

			IFile iFile = null;
			try {
				// monitor.beginTask("creating build file", 1);
				iFile = project.getFile("release/.htaccess");

				// monitor.worked(1);
				if (iFile.exists()) {
					iFile.delete(true, monitor);
				}
				iFile.create(new FileInputStream(f), true, monitor);
				iFile.getResourceAttributes().setHidden(false);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		project.refreshLocal(2, monitor);

	}

	@Override
	public boolean performFinish() {
		IRunnableWithProgress op = new IRunnableWithProgress() {

			final boolean del = pageCreation.isDelegate();
			final String[] targets = pageCreation.getTargetList();
			final String module = pageCreation.getTargetModule();
			final boolean cApplication = pageCreation.isCreateApplication();
			final boolean cApplet = pageCreation.isCreateApplet();

			final String appletWidth = pageConfig.getAppletWidth();
			final String appletHeight = pageConfig.getAppletHeight();
			final String jwsWidth = pageConfig.getJwsWidth();
			final String jwsHeight = pageConfig.getJwsHeight();
			final String codebase = pageConfig.getCodebase();

			public void run(IProgressMonitor monitor)
					throws InvocationTargetException {
				try {
					doFinish(module, targets, del, cApplet, cApplication,
							jwsWidth, jwsHeight, monitor);
					doFinishAnt(module, targets, del, cApplet, cApplication,
							monitor);
					doFinishApplet(module, targets, del, cApplet, cApplication,
							appletWidth, appletHeight, monitor);
					doFinishJNLP(module, targets, del, cApplet, cApplication,
							jwsWidth, jwsHeight, codebase, monitor);
					doFinishEnd(module, targets, del, cApplet, cApplication,
							monitor);
				} catch (Throwable e) {
					ILog logger = NativeCapableJavaPlugin.getDefault().getLog();
					IStatus status = new Status(IStatus.WARNING,
							NativeCapableJavaPlugin.PLUGIN_ID, IStatus.OK, e
									.toString(), e);
					logger.log(status);
					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());
			return false;
		}
		return true;
	}

	public void check(final String str) {
		check(str, null);
	}

	public void check(final String str, Throwable t) {

		ILog logger = NativeCapableJavaPlugin.getDefault().getLog();
		IStatus status = new Status(IStatus.INFO,
				NativeCapableJavaPlugin.PLUGIN_ID, IStatus.OK, str, t);
		logger.log(status);

	}

	@Override
	public void init(IWorkbench workbench, IStructuredSelection selection) {
		this.selection = selection;
	}

}
