package org.applause.lang.ui.wizard;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.applause.lang.ui.builder.MobilePlatform;
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.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
import org.eclipse.xtext.ui.util.PluginProjectFactory;
import org.eclipse.xtext.ui.util.ProjectFactory;
import org.eclipse.xtext.ui.wizard.AbstractProjectCreator;
import org.eclipse.xtext.util.StringInputStream;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.Provider;

public class CustomApplauseProjectCreator extends AbstractProjectCreator {

	private static final String MODEL_ROOT = "model";
	private static final List<String> ALL_FOLDERS = ImmutableList.of();
	
	@Inject
	private Provider<PluginProjectFactory> projectFactoryProvider;
	
	@Inject
	private Provider<ProjectFactory> platformProjectFactoryProvider;

	@Override
	protected ProjectFactory createProjectFactory() {
		return projectFactoryProvider.get();	
	}
	
	protected ProjectFactory createPlatformProjectFactory() {
		return platformProjectFactoryProvider.get();
	}

	@Override
	protected String getModelFolderName() {
		return MODEL_ROOT;
	}

	@Override
	protected List<String> getAllFolders() {
		return ALL_FOLDERS;
	}
	
	protected ApplauseDslProjectInfo getApplauseProjectInfo() {
		return (ApplauseDslProjectInfo)getProjectInfo();
	}
	
	@Override
	protected void execute(final IProgressMonitor monitor) throws CoreException, InvocationTargetException,
			InterruptedException {
		SubMonitor subMonitor = SubMonitor.convert(
				monitor, 
				getCreateModelProjectMessage(),
				getApplauseProjectInfo().numberOfPlatforms() + 1);

		IProject project = createModelProject(subMonitor.newChild(1));
		for(MobilePlatform platform : getApplauseProjectInfo().getSelectedPlatforms()) {
			createPlatformProject(platform, subMonitor.newChild(1));
		}

		IFile modelFile = getModelFile(project);
		setResult(modelFile);
	}

	private IProject createModelProject(SubMonitor monitor) throws CoreException {
		ProjectFactory factory = createProjectFactory();
		configureProjectFactory(factory);
		IProject modelProject = factory.createProject(monitor, null);
		createModel(modelProject, monitor);
		return modelProject;
	}	
	
	private void createModel(final IProject project, final SubMonitor monitor) throws CoreException {
		InputStream contents = this.getClass().getClassLoader().getResourceAsStream("/templateproject/project.zip");
		enhancePlatformProject(project, contents, monitor);
	}
	
	protected ProjectFactory configureProjectFactoryForPlatformProject(ProjectFactory factory, MobilePlatform platform) {
		factory.setProjectName(getProjectInfo().getProjectName() + platform.getProjectNameSuffix());
		factory.addReferencedProjects(getReferencedProjects());
		factory.addProjectNatures(getProjectNatures());
		factory.addBuilderIds(getBuilders());
		return factory;
	}
	
	
	private IProject createPlatformProject(MobilePlatform platform, SubMonitor monitor) {
		ProjectFactory factory = createPlatformProjectFactory();
		configureProjectFactoryForPlatformProject(factory, platform);
		IProject platformProject = factory.createProject(monitor, null);
		enhancePlatformProject(platformProject, platform, monitor);
		return platformProject;
	}
	private void enhancePlatformProject(IProject project, MobilePlatform platform, SubMonitor monitor) {
		ExtensibleURIConverterImpl uriConverter = new ExtensibleURIConverterImpl();
		try {
			InputStream stream = uriConverter.createInputStream(platform.getTemplateProjectURI());
			enhancePlatformProject(project, stream, monitor);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		
	}
	
	private void enhancePlatformProject(IProject project, InputStream stream, SubMonitor monitor) {
		try {
			ZipInputStream zipStream = new ZipInputStream(stream);
			ZipEntry entry;
			while ((entry = zipStream.getNextEntry()) != null) {
				String name = entry.getName();
				if(name.contains("__MACOSX/"))
					continue;
				
				name = replaceTemplate(name);
				if (entry.isDirectory()) {
					IFolder folder = project.getFolder(name);
					if (!folder.exists()) {
						folder.create(true, true, monitor);
					}
				} else {
					IFile file = project.getFile(name);
					// PFR: PNGs, GIfs and ZIPs will not be filtered. Poor man's binary detection :-/
					if (entry.getName().endsWith(".png") || entry.getName().endsWith(".gif") || entry.getName().endsWith(".jar")) {
						File destFile = file.getLocation().toFile();
						FileOutputStream output = FileUtils.openOutputStream(destFile);
						IOUtils.copy(zipStream, output);
					}
					else {
						String content;
						content = replaceTemplate(IOUtils.toString(zipStream, "UTF-8"));
						if (file.exists()) {
							file.delete(true, monitor);
						}
						file.create(new StringInputStream(content), true, monitor);
					}
				}
			}
			project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			IOUtils.closeQuietly(stream);
		}
	}
	
	private String replaceTemplate(String str) {
		String projectName = getProjectInfo().getProjectName();
		String capitalizedProjectName = projectName.equals("_template_") ? "_Template_" : StringUtils.capitalize(projectName);
		str = str.replace("itemisApp", projectName);
		str = str.replace("ItemisApp", capitalizedProjectName);
		str = str.replace("de.itemis.itemisapp.R", "de.itemis." + projectName.toLowerCase() + ".R");
		return str;
	}

}
