 /*******************************************************************************
  * Copyright (c) 2008 Red Hat, Inc.
  * Distributed under license by Red Hat, Inc. All rights reserved.
  * This program is made available under the terms of the
  * Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  *     Red Hat, Inc. - initial API and implementation
  ******************************************************************************/
package org.jboss.tools.seam.internal.core.project.facet;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;
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.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
import org.eclipse.wst.common.frameworks.datamodel.IDataModel;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.format.FormatProcessorXML;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.core.project.facet.SeamVersion;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * @author Alexey Kazakov
 * This class helps New Seam Wizard Page to create EJB, EAR and test projects for seam 2.* WAR project.
 */
public class Seam2ProjectCreator extends SeamProjectCreator {

	private static final String STRICT = "strict";
	private static final String MODULE_ORDER = "module-order";
	
	// test/*.jar are duplicated here since the filtering seem to be assymetric when matching 
	private static AntCopyUtils.FileSet JBOSS_TEST_LIB_FILESET = new AntCopyUtils.FileSet()
	    .include("testng.*\\.jar") //$NON-NLS-1$
		.include("test/hibernate-all\\.jar") //$NON-NLS-1$
		.include("hibernate-all\\.jar") //$NON-NLS-1$
		.include("test/jboss-embedded-all.jar") //$NON-NLS-1$
		.include("jboss-embedded-all.jar") //$NON-NLS-1$
		.include("test/jboss-embedded-api.jar") //$NON-NLS-1$
		.include("jboss-embedded-api.jar") //$NON-NLS-1$
		.include("test/jboss-deployers.jar") //$NON-NLS-1$		
		.include("jboss-deployers.jar") //$NON-NLS-1$
		.include("test/thirdparty-all\\.jar") //$NON-NLS-1$
		.include("thirdparty-all\\.jar") //$NON-NLS-1$
		.include("core.*\\.jar") //$NON-NLS-1$
		.include("ecj.*\\.jar") //$NON-NLS-1$
		.exclude(".*/CVS") //$NON-NLS-1$
		.exclude(".*/\\.svn"); //$NON-NLS-1$

	/**
	 * @param model Seam facet data model
	 * @param seamWebProject Seam web project
	 */
	public Seam2ProjectCreator(IDataModel model, IProject seamWebProject, SeamLibFileSetProvider seamLibFileSetProvider) {
		super(model, seamWebProject, seamLibFileSetProvider);
		viewFilterSetCollection.addFilterSet(SeamFacetFilterSetFactory.createHibernateDialectFilterSet(model));
		droolsLibFolder = new File(seamHomePath, Seam2FacetInstallDelegate.DROOLS_LIB_SEAM_RELATED_PATH);
	}

	/*
	 * (non-Javadoc)
	 * @see org.jboss.tools.seam.internal.core.project.facet.SeamProjectCreator#createEarProject()
	 */
	@Override
	protected void createEarProject() {
		super.createEarProject();
		if (!SeamCorePlugin.getDefault().hasM2Facet(seamWebProject) && shouldCopyLibrariesAndTemplates(model) && shouldCopyLibraries(model)) {
			File earContentsFolder = new File(earProjectFolder, "EarContent"); //$NON-NLS-1$
			File earLibFolder = new File(earContentsFolder, "lib"); //$NON-NLS-1$
			AntCopyUtils.copyFiles(seamLibFolder, earLibFolder, new AntCopyUtils.FileSetFileFilter(new AntCopyUtils.FileSet(seamLibFileSetProvider.getEarLibFileSet()).dir(seamLibFolder)));
		}
	}

	@Override
	protected boolean createTestProject() {
		if(!(Boolean)model.getProperty(ISeamFacetDataModelProperties.TEST_PROJECT_CREATING) || !shouldCopyLibrariesAndTemplates(model))
			return false;
		
		File testProjectDir = new File(seamWebProject.getLocation().removeLastSegments(1).toFile(), testProjectName); //$NON-NLS-1$
		testProjectDir.mkdir();

		IVirtualComponent component = ComponentCore.createComponent(seamWebProject);
		IVirtualFolder webRootVirtFolder = component.getRootFolder().getFolder(new Path("/")); //$NON-NLS-1$

		File testLibDir = new File(testProjectDir,"lib"); //$NON-NLS-1$
		File embededEjbDir = new File(testProjectDir,"bootstrap"); //$NON-NLS-1$
		File testSrcDir = new File(testProjectDir,"test-src"); //$NON-NLS-1$
		String seamGenResFolder = seamRuntime.getResourceTemplatesDir();
		File dataSourceFile = new File(seamGenResFolder, "datasource-ds.xml");
		File seamPropertiesFile = new File(seamGenResFolder, "seam.properties");
		//File jbossBeansFile = new File(seamGenResFolder ,"META-INF/jboss-beans.xml"); //$NON-NLS-1$
		FilterSet filterSet = new FilterSet();
		filterSet.addFilter("projectName", seamWebProject.getName()); //$NON-NLS-1$
		filterSet.addFilter("earProjectName", earProjectName); //$NON-NLS-1$
		filterSet.addFilter("ejbProjectName", ejbProjectName); //$NON-NLS-1$
		filterSet.addFilter("testProjectName", testProjectName); //$NON-NLS-1$

		filterSet.addFilter("runtimeName", WtpUtils.getServerRuntimeName(seamWebProject)); //$NON-NLS-1$
		filterSet.addFilter("webRootFolder",webRootVirtFolder.getUnderlyingFolder().getFullPath().removeFirstSegments(1).toString()); //$NON-NLS-1$

		FilterSet jdbcFilterSet = SeamFacetFilterSetFactory.createJdbcFilterSet(model);
		// TODO: why are these filters not shared!?
		filterSet.addConfiguredFilterSet(SeamFacetFilterSetFactory.createHibernateDialectFilterSet(model));

		AntCopyUtils.FileSet includeLibs = new AntCopyUtils.FileSet(getJBossTestLibFileset()).dir(new File(seamRuntime.getHomeDir(),"lib")); //$NON-NLS-1$
		AntCopyUtils.FileSet secondSetincludeLibs = new AntCopyUtils.FileSet(getJBossTestLibFileset()).dir(new File(seamRuntime.getHomeDir(),"lib/test")); //$NON-NLS-1$

		File[] firstlibs = includeLibs.getDir().listFiles(new AntCopyUtils.FileSetFileFilter(includeLibs));
		File[] secondLibs = secondSetincludeLibs.getDir().listFiles(new AntCopyUtils.FileSetFileFilter(secondSetincludeLibs));
		Set<String> allLibs = new HashSet<String>(); // HACK: needed to be unique because some jboss-*.jars are duplicated
		if(firstlibs != null)
			for(File f : firstlibs) {
				allLibs.add(f.getName());
			}
		if(secondLibs != null)
			for(File f : secondLibs) {
				allLibs.add(f.getName());
			}

		StringBuffer testLibraries = new StringBuffer();

		if (!SeamCorePlugin.getDefault().hasM2Facet(seamWebProject)) {
			for (String file : allLibs) {
				testLibraries.append("\t<classpathentry kind=\"lib\" path=\"lib/" + file + "\"/>\n"); //$NON-NLS-1$ //$NON-NLS-2$
			}			
		}

		StringBuffer requiredProjects = new StringBuffer();
		requiredProjects.append(
				"\t<classpathentry combineaccessrules=\"false\" kind=\"src\" path=\"/" + seamWebProject.getName() + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
		if(!SeamFacetAbstractInstallDelegate.isWarConfiguration(model)) {
			requiredProjects.append("\n\t<classpathentry combineaccessrules=\"false\" kind=\"src\" path=\"/" + ejbProjectName + "\"/>"); //$NON-NLS-1$ //$NON-NLS-2$
		} 
		filterSet.addFilter("testLibraries",testLibraries.toString()); //$NON-NLS-1$
		filterSet.addFilter("requiredProjects",requiredProjects.toString()); //$NON-NLS-1$
		File testTemplateDir = null;
		try {
			testTemplateDir = new File(SeamFacetInstallDataModelProvider.getTemplatesFolder(), "test-seam2"); //$NON-NLS-1$
		} catch (IOException e) {
			SeamCorePlugin.getPluginLog().logError(e);
			return false;
		}
		AntCopyUtils.FileSet excludeCvsSvn 
				 = new AntCopyUtils.FileSet(SeamFacetAbstractInstallDelegate.CVS_SVN).dir(testTemplateDir);

		AntCopyUtils.copyFilesAndFolders(
				testTemplateDir,
				testProjectDir,
				new AntCopyUtils.FileSetFileFilter(excludeCvsSvn),
				new FilterSetCollection(filterSet), true);

		excludeCvsSvn.dir(new File(seamRuntime.getHomeDir(), "bootstrap")); //$NON-NLS-1$
		AntCopyUtils.copyFilesAndFolders(
				new File(seamRuntime.getHomeDir(), "bootstrap"), //$NON-NLS-1$
				embededEjbDir,
				new AntCopyUtils.FileSetFileFilter(excludeCvsSvn), new FilterSetCollection(), true);

//			AntCopyUtils.copyFileToFile(
//					persistenceFile,
//					new File(testProjectDir,"test-src/META-INF/persistence.xml"), //$NON-NLS-1$
//					new FilterSetCollection(filterSet), true);

		FilterSetCollection f = new FilterSetCollection();
		f.addFilterSet(filterSet);
		f.addFilterSet(jdbcFilterSet);

		AntCopyUtils.copyFileToFile(
				dataSourceFile,
				new File(testProjectDir, "test-src/META-INF/"+seamWebProject.getName() + "-test-ds.xml"), //$NON-NLS-1$
				f, true);

		AntCopyUtils.copyFileToFolder(
				seamPropertiesFile,
				testSrcDir, //$NON-NLS-1$
				new FilterSetCollection(filterSet), true);

		// Add "org.jboss.seam.core.init.debug=false" for Seam 2.1/2.2/2.3
		// to seam.properties file to avoid https://jira.jboss.org/jira/browse/JBIDE-3623
		if(getVersion() == SeamVersion.SEAM_2_1 || getVersion() == SeamVersion.SEAM_2_2 || getVersion() == SeamVersion.SEAM_2_3) {
			Properties seamProperties = new Properties();
			File testSeamPropertiesFile = new File(testSrcDir, "seam.properties");
			FileInputStream inStream = null;
			FileOutputStream out = null;
			try {
				inStream = new FileInputStream(testSeamPropertiesFile);
				seamProperties.load(inStream);
				seamProperties.setProperty("org.jboss.seam.core.init.debug", "false"); //$NON-NLS-1$ //$NON-NLS-2$
				
				out = new FileOutputStream(testSeamPropertiesFile);
				seamProperties.store(out, "debug is explicitly disabled in test to avoid JBIDE-3623");
			} catch (FileNotFoundException e) {
				SeamCorePlugin.getPluginLog().logError(e);
			} catch (IOException e) {
				SeamCorePlugin.getPluginLog().logError(e);
			} finally {
				if(inStream!=null) {
					try { inStream.close();	} catch (IOException e) { /**ignore*/ }					
					}									
				if(out!=null) {
					try { out.close();	} catch (IOException e) { /**ignore*/ }
				}
			}
		}

		if (!SeamCorePlugin.getDefault().hasM2Facet(seamWebProject) && shouldCopyLibraries(model)) {
			AntCopyUtils.copyFiles(new File(seamRuntime.getHomeDir(), "lib"), //$NON-NLS-1$
							testLibDir, new AntCopyUtils.FileSetFileFilter(includeLibs));

			// seam2 has a lib/test
			AntCopyUtils.copyFiles(new File(seamRuntime.getHomeDir(), "lib/test"), //$NON-NLS-1$
							testLibDir, new AntCopyUtils.FileSetFileFilter(includeLibs));
		}

		SeamFacetAbstractInstallDelegate.createComponentsProperties(testSrcDir, "", true); //$NON-NLS-1$
		return true;
	}

	@Override
	protected void createEjbProject() {
		super.createEjbProject();
		// Copy security.drl to source folder
		if(shouldCopyLibrariesAndTemplates(model))
			AntCopyUtils.copyFileToFolder(new File(seamGenResFolder, "security.drl"), new File(ejbProjectFolder, "ejbModule/"), true); //$NON-NLS-1$ //$NON-NLS-2$
	}

	@Override
	protected void configureJBossAppXml() {
		IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(earProjectName);
		IVirtualComponent component = ComponentCore.createComponent(project);
		IVirtualFolder folder = component.getRootFolder();
		IFolder rootFolder = (IFolder) folder.getUnderlyingFolder();
		IResource jbossAppXml = rootFolder.findMember("META-INF/jboss-app.xml");
		if(jbossAppXml==null || !(jbossAppXml instanceof IFile) || !jbossAppXml.exists()) {
			return;
		}
		
		IModelManager manager = StructuredModelManager.getModelManager();
		if(manager == null) {
			return;
		}
		IStructuredModel model = null;		
		try {
			model = manager.getModelForEdit((IFile)jbossAppXml);
			if (model instanceof IDOMModel) {
				IDOMModel domModel = (IDOMModel) model;
				IDOMDocument document = domModel.getDocument();
				Element root = document.getDocumentElement();
				if(root==null) {
					return;
				}
				NodeList children = root.getChildNodes();
				boolean strictAdded = false;
				Node firstChild = null;
				for(int i=0; i<children.getLength(); i++) {
					Node currentNode = children.item(i);
					if(Node.ELEMENT_NODE == currentNode.getNodeType() && firstChild == null) {
						firstChild = currentNode;
					}
					if(Node.ELEMENT_NODE == currentNode.getNodeType() && MODULE_ORDER.equals(currentNode.getNodeName())) {
						setValue(document,currentNode,STRICT);
						strictAdded = true;
					}
				}
				if (!strictAdded) {
					Element moduleOrder = document.createElement(MODULE_ORDER);
					setValue(document,moduleOrder,STRICT);
					if (firstChild != null) {
						root.insertBefore(moduleOrder, firstChild);
					} else {
						root.appendChild(moduleOrder);
					}
				}
				model.save();
			}
		} catch (CoreException e) {
			SeamCorePlugin.getDefault().logError(e);
        } catch (IOException e) {
        	SeamCorePlugin.getDefault().logError(e);
		} finally {
			if (model != null) {
				model.releaseFromEdit();
			}
		}
		try {
			new FormatProcessorXML().formatFile((IFile) jbossAppXml);
		} catch (IOException e) {
			SeamCorePlugin.getDefault().logError(e);
		} catch (CoreException e) {
			SeamCorePlugin.getDefault().logError(e);
		}
	}

	private void setValue(Document document, Node node, String value) {
		Text text = document.createTextNode(value);
		node.appendChild(text);
	}

	protected AntCopyUtils.FileSet getJBossTestLibFileset() {
		return JBOSS_TEST_LIB_FILESET;
	}

	protected AntCopyUtils.FileSet getJbossEarContent() {
		return Seam2FacetInstallDelegate.JBOSS_EAR_CONTENT;
	}
}