/* ====================================================================
 * 
 * The ObjectStyle Group Software License, Version 1.0 
 *
 * Copyright (c) 2002 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.io.File;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.internal.resources.ResourceException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.objectstyle.wolips.baseforplugins.util.FileUtilities;

/**
 * @author Harald Niesche
 * 
 */
public class ResourceUtilities {

	/**
	 * create a Folder recursively
	 * 
	 * @param f
	 *            the Folder to be created
	 * @param m
	 *            a ProgressMonitor
	 * @throws CoreException
	 */
	public static void createFolder(IFolder f, IProgressMonitor m) throws CoreException {
		if (f.exists()) {
			return;
		}
		IContainer parent = f.getParent();
		if (!f.getParent().exists()) {
			if (parent instanceof IFolder) {
				createFolder((IFolder) parent, m);
			}
		}
		f.create(true, true, m);
	}

	/**
	 * check if a folder exists under a path, create it if necessary
	 * 
	 * @param path
	 *            the path to the folder to be created (relative to the
	 *            workspace root or absolute)
	 * @param m
	 *            a ProgressMonitor
	 * @return true, iff the folder already existed, false, if it had to be
	 *         created
	 * @throws CoreException
	 */
	public static boolean checkDir(IPath path, IProgressMonitor m) throws CoreException {
		return checkDir(path, m, false);
	}

	/**
	 * @param path
	 * @param m
	 * @return
	 * @throws CoreException
	 */
	public static boolean checkDerivedDir(IPath path, IProgressMonitor m) throws CoreException {
		return checkDir(path, m, true);
	}

	public static boolean checkDir(IPath path, IProgressMonitor m, boolean derived) throws CoreException {
		boolean result = true;

		IFolder f = getWorkspaceRoot().getFolder(path);
		if (!f.exists()) {
			createFolder(f, m);
			if (derived) {
				f.setDerived(true);
			}
			result = false;
		}

		return (result);
	}

	/**
	 * checks if a path is fit to be used as destination for a copy operation if
	 * not, the destination is prepared to be used as destination (i.e.,
	 * existing files and folders are deleted, the parent path is created, if
	 * necessary)
	 * 
	 * @param path
	 *            the candidate destination path
	 * @param m
	 *            a ProgressMonitor
	 * @return
	 * @throws CoreException
	 */
	public static IResource checkDestination(IPath path, IProgressMonitor m, boolean deleteIfExists) throws CoreException {
		return checkDestination(path, m, false, deleteIfExists);
	}

	public static IResource checkDerivedDestination(IPath path, IProgressMonitor m, boolean deleteIfExists) throws CoreException {
		return checkDestination(path, m, true, deleteIfExists);
	}

	public static IResource checkDestination(IPath path, IProgressMonitor m, boolean derived, boolean deleteIfExists) throws CoreException {
		if (checkDir(path.removeLastSegments(1), m, derived)) {
			IResource res = getWorkspaceRoot().findMember(path);
			if (null != res && res.exists()) {
				if (deleteIfExists) {
					try {
						File f = res.getLocation().toFile();
						if (f.exists()) {
							FileUtilities.deleteRecursively(f);
						}
						else {
							res.delete(true, m);
						}
						// } catch
						// (org.eclipse.core.internal.resources.ResourceException e)
						// {
					} catch (CoreException ce) {
						ce.printStackTrace();
						// NOTE AK: this code was commented out, I re-instated it as
						// I think it's
						// better to have the non-deletable files in one place,
						// instead of messing the build directory
						IPath trashFolder = res.getProject().getFullPath().append("build/.trash");
						IPath trashPath = trashFolder.append(res.getName() + (_getUniqifier()));
						checkDir(trashFolder, m);
						res.move(trashPath, true, null);
						/*
						 * IPath newName = res.getLocation().removeLastSegments(1);
						 * newName = newName.append(res.getName()+_getUniqifier());
						 * File resFile = res.getLocation().toFile(); if
						 * (!resFile.renameTo(newName.toFile())) { throw ce; }
						 */
					}
				}
				res.refreshLocal(IResource.DEPTH_ONE, m);
			}
			return res;
		}
		return null;
	}

	private static synchronized int _getUniqifier() {
		return _uniqifier++;
	}

	/**
	 * @param res
	 * @param dest
	 * @param m
	 * @throws CoreException
	 */
	public static void copyDerivedOld(IResource res, IPath dest, IProgressMonitor m) throws CoreException {
		if (res.isTeamPrivateMember() || res.getName().equals(".svn"))
			return;

		IResource rdest = checkDestination(dest, m, true);
		res.copy(dest, true, null);
		if (null != rdest) {
			rdest.setDerived(true);
		}
	}

	/**
	 * @param res
	 *            the Resource to copy
	 * @param dest
	 *            where it should be copied to
	 * @param m
	 *            ProgressMonitor to show what's going on
	 * 
	 * @throws CoreException
	 *             if something goes wrong (and we notice)
	 */
	public static void copyDerived(IResource res, IPath dest, IProgressMonitor m) throws CoreException {
		if (res.isTeamPrivateMember() || res.getName().equals(".svn"))
			return;

		if (res instanceof IFolder) {
			IResource[] members = ((IFolder) res).members();
			// MS: This is super goofy, but apparently Nonlocalized.lproj folders get fiddled with
			// during the build and end up going to build/Whatever.framework/Resources directly,
			// which makes the Resource folder get deleted, potentially tossing files you just built.
			boolean deleteDestination = !res.getName().equals("Nonlocalized.lproj"); 
			IResource rdest = checkDerivedDestination(dest, m, deleteDestination);

			for (int i = 0; i < members.length; i++) {
				IResource thisOne = members[i];
				if (!members[i].isTeamPrivateMember()) {
					IPath thisDest = dest.append(thisOne.getName());
					copyDerived(thisOne, thisDest, m);
				}
			}
			if (null != rdest) {
				rdest.setDerived(true);
			}
		} else {
			// it's a file, let's just copy it
			IResource rdest = checkDestination(dest, m, true);
			res.copy(dest, true, m);
			if (null != rdest) {
				rdest.setDerived(true);
			}
		}
	}

	/**
	 * @return
	 */
	public static IWorkspace getWorkspace() {
		return ResourcesPlugin.getWorkspace();
	}

	/**
	 * @return
	 */
	public static IWorkspaceRoot getWorkspaceRoot() {
		return ResourcesPlugin.getWorkspace().getRoot();
	}

	protected ResourceUtilities() {
		super();
	}

	private static int _uniqifier = 1;

	/**
	 * @param res
	 * @param markerId
	 * @throws CoreException
	 */
	public static void unmarkResource(IResource res, String markerId) throws CoreException {
		if (res.exists()) {
			res.deleteMarkers(markerId, true, 0);
		}
	}

	/**
	 * @param res
	 * @param markerId
	 * @param severity
	 * @param message
	 * @param location
	 * @return
	 * @throws CoreException
	 */
	public static IMarker markResource(IResource res, String markerId, int severity, String message, String location) throws CoreException {
		IMarker newMarker;
		if (!res.exists()) {
			newMarker = null;
		}
		else {
			try {
			IMarker[] marker = res.findMarkers(markerId, true, 0);
			if (marker.length != 1) {
				if (marker.length > 1) {
					res.deleteMarkers(markerId, false, 0);
				}
				marker = new IMarker[1];
				marker[0] = res.createMarker(markerId);
			}
	
			if (!marker[0].exists()) {
				marker[0] = res.createMarker(markerId);
			}
			Map attr = new HashMap();
	
			attr.put(IMarker.PRIORITY, Integer.valueOf(IMarker.PRIORITY_HIGH));
			attr.put(IMarker.SEVERITY, Integer.valueOf(severity));
			attr.put(IMarker.MESSAGE, message);
			attr.put(IMarker.LOCATION, location);
	
			marker[0].setAttributes(attr);
			newMarker = marker[0];
			}
			catch (ResourceException e) {
				// MS: This happens a lot and it's not actually a problem.  This is almost always a deleted file.
				e.printStackTrace();
				newMarker = null;
			}
		}
		return newMarker;
	}
}
