/* ====================================================================
 * 
 * The ObjectStyle Group Software License, Version 1.0 
 *
 * Copyright (c) 2002 - 2006 The ObjectStyle Group 
 * and individual authors of the software.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:  
 *       "This product includes software developed by the 
 *        ObjectStyle Group (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "ObjectStyle Group" and "Cayenne" 
 *    must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written 
 *    permission, please contact andrus@objectstyle.org.
 *
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    nor may "ObjectStyle" appear in their names without prior written
 *    permission of the ObjectStyle Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the ObjectStyle Group.  For more
 * information on the ObjectStyle Group, please see
 * <http://objectstyle.org/>.
 *
 */
package org.objectstyle.wolips.builder.internal;

import java.util.Map;

import org.eclipse.core.resources.IContainer;
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.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.objectstyle.wolips.baseforplugins.AbstractBaseActivator;
import org.objectstyle.wolips.builder.BuilderPlugin;
import org.objectstyle.wolips.core.resources.types.folder.IWoprojectAdapter;
import org.objectstyle.wolips.core.resources.types.project.ProjectAdapter;
import org.objectstyle.wolips.preferences.Preferences;
import org.objectstyle.wolips.templateengine.ProjectInput;
import org.objectstyle.wolips.templateengine.ProjectTemplate;
import org.objectstyle.wolips.variables.BuildProperties;

/**
 * @author Harald Niesche The incremental builder creates the
 *         build/ProjectName.woa or build/ProjectName.framework folder that
 *         contains an approximation of the structure needed to run a WebObjects
 *         application or use a framework
 */
public class WOIncrementalBuilder extends AbstractIncrementalProjectBuilder {
	private BuildVisitor _buildVisitor;

	private JarBuilder _jarBuilder;

	/**
	 * Constructor for WOProjectBuilder.
	 */
	public WOIncrementalBuilder() {
		super();
	}

	public boolean isEnabled() {
		return true;
	}

	/*
	 * this is duplicated from ProjectNaturePage, couldn't find a good place for
	 * now
	 */
	private String getArg(Map values, String key, String defVal) {
		String result = null;
		try {
			result = (String) values.get(key);
		} catch (Exception up) {
			getLogger().log(up);
		}
		if (null == result)
			result = defVal;
		return result;
	}

	public void invokeOldBuilder(int kind, Map args, IProgressMonitor progressMonitor, IResourceDelta resourceDelta) throws Exception {
		if (!Preferences.mockBundleEnabled()) {
			// just don't do anything here
			return;
		}

		IProgressMonitor subProgressMonitor = null;
		if (null == progressMonitor) {
			subProgressMonitor = new NullProgressMonitor();
		} else {
			subProgressMonitor = new SubProgressMonitor(progressMonitor, 100 * 1000);
		}
		IResourceDelta delta = resourceDelta;
		if (kind != IncrementalProjectBuilder.FULL_BUILD && kind != IncrementalProjectBuilder.CLEAN_BUILD && !projectNeedsAnUpdate(delta)) {
			subProgressMonitor.done();
			return;
		}
		getLogger().debug("<incremental build>");
		subProgressMonitor.beginTask("building WebObjects layout ...", 100);
		try {
			ProjectAdapter project = (ProjectAdapter) this.getProject().getAdapter(ProjectAdapter.class);
			boolean fullBuild = (kind == IncrementalProjectBuilder.FULL_BUILD || kind == IncrementalProjectBuilder.CLEAN_BUILD || patternsetDeltaVisitor().isFullBuildRequired());
			String oldPrincipalClass = getArg(args, BuilderPlugin.NS_PRINCIPAL_CLASS, "");
			if (oldPrincipalClass.length() == 0) {
				oldPrincipalClass = null;
			}
			BuildProperties buildProperties = project.getBuildProperties();
			String principalClass = buildProperties.getPrincipalClass(true);
			if (principalClass == null && oldPrincipalClass != null) {
				principalClass = oldPrincipalClass;
				buildProperties.setPrincipalClass(principalClass);
			}
			
			if (buildProperties.getWOVersion().isAtLeastVersion(5, 6)) {
				IContainer infoPListContainer = getProject().getFolder(IWoprojectAdapter.FOLDER_NAME);
				createInfoPlist(infoPListContainer);
			}
			else {
				if (_buildVisitor == null) {
					_buildVisitor = new BuildVisitor();
				}
				_buildVisitor.reinitForNextBuild(this.getProject());
				if (!fullBuild) {
					subProgressMonitor.subTask("checking directory structure ...");
					if (!_buildVisitor._checkDirs()) {
						delta = null;
						subProgressMonitor.worked(5);
					}
				} else {
					delta = null;
					long t0 = System.currentTimeMillis();
					IFolder buildFolder = getProject().getFolder("build");
					subProgressMonitor.subTask("scrubbing build folder ...");
					buildFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
					subProgressMonitor.worked(1);
					getLogger().debug("refresh build folder took: " + (System.currentTimeMillis() - t0) + " ms");
					t0 = System.currentTimeMillis();
					buildFolder.delete(true, false, null);
					subProgressMonitor.worked(2);
					getLogger().debug("scrubbing build folder took: " + (System.currentTimeMillis() - t0) + " ms");
					t0 = System.currentTimeMillis();
					buildFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
					subProgressMonitor.subTask("re-creating structure ...");
					_buildVisitor._checkDirs();
					subProgressMonitor.worked(2);
					getLogger().debug("re-creating build folder took: " + (System.currentTimeMillis() - t0) + " ms");
				}
				subProgressMonitor.subTask("creating Info.plist");
				IContainer infoPListContainer = getProject().getWorkspace().getRoot().getFolder(_buildVisitor.getInfoPath());
				createInfoPlist(infoPListContainer);
				subProgressMonitor.worked(1);
				if ((null != delta)) {
					getLogger().debug("<partial build>");
					subProgressMonitor.subTask("preparing partial build");
					long t0 = System.currentTimeMillis();
					_buildVisitor.resetCount();
					delta.accept(_buildVisitor, IResourceDelta.ALL_WITH_PHANTOMS);
					getLogger().debug("delta.accept with " + _buildVisitor.getCount() + " delta nodes took: " + (System.currentTimeMillis() - t0) + " ms");
					getLogger().debug("</partial build>");
					subProgressMonitor.worked(12);
				} else {
					getLogger().debug("<full build>");
					subProgressMonitor.subTask("preparing full build");
					long t0 = System.currentTimeMillis();
					t0 = System.currentTimeMillis();
					_buildVisitor.resetCount();
					getProject().accept(_buildVisitor);
					getLogger().debug("preparing with " + _buildVisitor.getCount() + " project nodes took: " + (System.currentTimeMillis() - t0) + " ms");
					getLogger().debug("</full build>");
					subProgressMonitor.worked(12);
				}
				long t0 = System.currentTimeMillis();
				_buildVisitor.executeTasks(subProgressMonitor);
				getLogger().debug("building structure took: " + (System.currentTimeMillis() - t0) + " ms");
				t0 = System.currentTimeMillis();
				subProgressMonitor.subTask("copying classes");
				jarBuild(delta, subProgressMonitor, getProject());
				getLogger().debug("copying classes took: " + (System.currentTimeMillis() - t0) + " ms");
				subProgressMonitor.done();
			}
		} catch (Exception up) {
			getLogger().log(up);
			throw up;
		}
		getLogger().debug("</incremental build>");
	}

	protected void createInfoPlist(IContainer targetContainer) throws Exception {
		ProjectTemplate infoPListTemplate;
		IProject project = getProject();
		ProjectAdapter projectAdapter = (ProjectAdapter) project.getAdapter(ProjectAdapter.class);
		if (projectAdapter.isFramework()) {
			infoPListTemplate = ProjectTemplate.loadProjectTemplateNamed("MiscTemplates", "FrameworkInfoPList");
		}
		else {
			infoPListTemplate = ProjectTemplate.loadProjectTemplateNamed("MiscTemplates", "ApplicationInfoPList");
		}
		infoPListTemplate.addInput(new ProjectInput("buildProperties", projectAdapter.getBuildProperties()));
		
		targetContainer.refreshLocal(IResource.DEPTH_ZERO, null);
		infoPListTemplate.createProjectContents(project, targetContainer, new NullProgressMonitor());
		IFile infoPList = targetContainer.getFile(new Path("Info.plist"));
		infoPList.refreshLocal(IResource.DEPTH_ZERO, null);
		infoPList.setDerived(true, null);
	}

	private AbstractBaseActivator getLogger() {
		return BuilderPlugin.getDefault();
	}

	private void jarBuild(IResourceDelta delta, IProgressMonitor monitor, IProject project) throws CoreException {
		getLogger().debug("<jar build>");
		if (_jarBuilder == null)
			_jarBuilder = new JarBuilder();
		_jarBuilder.reinitForNextBuild(project);
		long t0 = System.currentTimeMillis();
		if (null != delta) {
			delta.accept(_jarBuilder, IResourceDelta.ALL_WITH_PHANTOMS);
		} else {
			IPath outPath = getJavaProject().getOutputLocation();
			IContainer output = getProject();
			if (!outPath.segment(0).equals(getProject().getName())) {
				output = getProject().getParent().getFolder(outPath);
			}
			output.accept(_jarBuilder);
		}
		getLogger().debug("prepare jar copy took " + (System.currentTimeMillis() - t0) + " ms");
		monitor.worked(10);
		t0 = System.currentTimeMillis();
		_jarBuilder.executeTasks(monitor);
		getLogger().debug("executing jar copy took " + (System.currentTimeMillis() - t0) + " ms");
		getLogger().debug("</jar build>");
	}

	private IJavaProject getJavaProject() {
		try {
			return ((IJavaProject) (getProject().getNature(JavaCore.NATURE_ID)));
		} catch (CoreException up) {
			this.getLogger().log(up);
		}
		return null;
	}

	/**
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#startupOnInitialize()
	 */
	protected void startupOnInitialize() {
		// try {
		// IJavaProject javaProject = getJavaProject();
		// _getLogger().debug(javaProject.getOutputLocation());
		// } catch (Throwable up) {
		// }
		// super.startupOnInitialize();
	}
}