/*******************************************************************************
* Copyright (c) 2006 IONA Technologies PLC
* All rights reserved. This program and the accompanying materials
* are 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:
*     IONA Technologies PLC - initial API and implementation
*******************************************************************************/
package org.eclipse.stp.sc.common.workspace;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.ICommand;
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.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.stp.common.logging.LoggingProxy;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveRegistry;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;




public class WorkspaceManager {
    public static final String WSDL_FOLDER = "wsdl";
    public static final String SRC_FOLDER = "src";
    public static final String CLASSES_FOLDER = "classes";
    public static final String META_INF_DIR = "META-INF";
    public static final String META_INF_FILENAME = "MANIFEST.MF";
    public static final String BIN_DIR = "bin";
    
    public enum BuilderOrder {
    	FIRST,
    	LAST
    }
    private static final LoggingProxy LOG = LoggingProxy.getlogger(WorkspaceManager.class);
	
    protected WorkspaceManager() {
    }

    protected static void createFolder(IFolder folder, IProgressMonitor monitor)
        throws CoreException {
        if (folder.exists()) {
            //folder.delete(true, monitor);
            return;
        }

        folder.create(true, true, monitor);
    }
    
    public static boolean hasBuilder(IProject project, String builderID) throws CoreException {
        IProjectDescription desc = project.getDescription();
        ICommand[] commands = desc.getBuildSpec();

        for (int i = 0; i < commands.length; ++i) {
            if (commands[i].getBuilderName().equals(builderID)) {
                return true;
            }
        }
        return false;
    }

    public static void addBuilder(IProject project, String builderID, 
    		BuilderOrder order)throws CoreException {
    	IProjectDescription desc = project.getDescription();
        ICommand[] commands = desc.getBuildSpec();

        for (int i = 0; i < commands.length; ++i) {
            if (commands[i].getBuilderName().equals(builderID)) {
                return;
            }
        }

        ICommand[] newCommands = new ICommand[commands.length + 1];
        ICommand newCommand = desc.newCommand();
        newCommand.setBuilderName(builderID);
        
        int index = 0;
        if (order == BuilderOrder.FIRST) {
        	newCommands[index++] = newCommand;
        }
        
        System.arraycopy(commands, 0, newCommands, index, commands.length);
        if (order == BuilderOrder.LAST) {
        	newCommands[newCommands.length - 1] = newCommand;
        }
        desc.setBuildSpec(newCommands);
        project.setDescription(desc, null);
    }
    
    
    public static void removeBuilder(IProject project, String builderID) throws CoreException {
        IProjectDescription description = project.getDescription();
        ICommand[] commands = description.getBuildSpec();

        for (int i = 0; i < commands.length; ++i) {
            if (commands[i].getBuilderName().equals(builderID)) {
                ICommand[] newCommands = new ICommand[commands.length - 1];
                System.arraycopy(commands, 0, newCommands, 0, i);
                System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
                description.setBuildSpec(newCommands);
                break;
            }
        }
        project.setDescription(description, null);
    }
    
//    public static IFolder getWSDLFolder(IProject hostProject) {
//        return hostProject.getFolder(WSDL_FOLDER);
//    }
//
//
    
    public static IFolder getMetaInfFolder(IProject hostProject) {
    	return hostProject.getFolder(META_INF_DIR);
    }
    
    public static IFolder getBinFolder(IProject hostProject) {
    	return hostProject.getFolder(BIN_DIR);
    }
    
    public static IFile getManiInfFile(IProject hostProject) {
    	return getMetaInfFolder(hostProject).getFile(META_INF_FILENAME);
    }

    /**
     * recursively copy files within one original folders to destination folder recuris
     * @param originalFolder, the original folder to copy from
     * @param destFolder, the destination folder
     * @param extensions. only files with assigned extension will be copied. if this == null, all
     *        files will be copied
     * @throws CoreException
     * @throws IOException
     */
    public static void copyFilesInFolder(IContainer originalFolder,
        IFolder destFolder, HashSet<String> extensions, IProgressMonitor monitor)
        throws CoreException, IOException {
        IResource[] resources = originalFolder.members();        
        for (IResource res : resources) {
            if (res instanceof IFile) {
                if (extensions != null) {
                    if (!extensions.contains(res.getFileExtension())) {
                        continue;
                    }
                }

                copyFile((IFile)res, destFolder, monitor);
            } else if (res instanceof IFolder) {
                IFolder childFolder = (IFolder)res;
                IFolder childDestFolder = destFolder.getFolder(childFolder
                        .getName());

                if (childDestFolder.exists()) {
                    childDestFolder.delete(true, null);
                }

                childDestFolder.create(true, true, null);
                copyFilesInFolder(childFolder, childDestFolder, extensions,
                    monitor);
            }
        }
    }

    public static void copyFilesInFolder(String path, IFolder destFolder,
        String extension, boolean overWrite, IProgressMonitor monitor)
        throws CoreException, IOException {
        File dir = new File(path);

        if (!dir.exists() || !dir.isDirectory()) {
            LOG.error("error to copy files. the input dir doesn't exist:" + path);

            return;
        }

        File[] files = dir.listFiles();

        for (File f : files) {
            if (extension != null) {
                if (!f.getName().endsWith(extension)) {
                    continue;
                }
            }

            copyFile(f.getAbsolutePath(), destFolder.getFile(f.getName()),
                overWrite, monitor);
        }
    }

    /**
     * copy files to dest folder, if file with the same name exists, it will overwrite
     * @param originalFile, the original file to copy
     * @param destFolder, the destination folder
     * @throws CoreException,
     * @throws IOException
     */
    public static void copyFile(IFile originalFile, IFolder destFolder,
        IProgressMonitor monitor) throws CoreException, IOException {
        IFile file = destFolder.getFile(originalFile.getName());

        if (file.exists()) {
            file.delete(true, monitor);
        }

        FileInputStream fs = new FileInputStream(originalFile.getRawLocation()
                                                             .toOSString());
        file.create(fs, true, monitor);
        fs.close();
    }

    public static void copyFile(String filePath, IFile destFile,
        boolean overWrite, IProgressMonitor monitor)
        throws CoreException, IOException {
        if (!overWrite && destFile.exists()) {
            return;
        }

        if (destFile.exists()) {
            destFile.delete(true, monitor);
        }

        FileInputStream fs = new FileInputStream(filePath);
        destFile.create(fs, true, null);
        fs.close();
    }

    /**
     * retrieves the currently selected resource
     *
     * @return selected resource - null if none found
     */
    public static IResource getSelectedResource() {
        // find the current page
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        IResource selResource = getSelectedResource(page);

        return selResource;
    }

    /**
     * retrieves the currently selected resource
     *
     * @return selected resource - null if none found
     */
    public static IResource getSelectedResource(IWorkbenchPage page) {
        IResource selResource = null;
        //the current selection in the current page
        ISelection sel = page.getSelection();
        selResource = getSelectedResource(page, sel);
        return selResource;
    }

    public static IResource getSelectedResource(IWorkbenchPage page, ISelection sel) {
        IResource selResource = null;
        if (sel instanceof StructuredSelection) {
            StructuredSelection ssel = (StructuredSelection)sel;
            Object element = ssel.getFirstElement();            
            if (element instanceof IResource) {
                selResource = (IResource)element;
            } else if (element instanceof IAdaptable) {
                IAdaptable adaptable = (IAdaptable)element;                
                selResource = (IResource)adaptable.getAdapter(IResource.class);
                if (selResource == null) {
                    selResource = (IResource)page.getActiveEditor().getAdapter(IResource.class);
                }
            } 
        } else if (sel instanceof TextSelection) {
            // current selection is in an editor page
            IEditorPart editor = page.getActiveEditor();
            IEditorInput input = editor.getEditorInput();
            if (input instanceof IFileEditorInput) {
                selResource = ((IFileEditorInput)input).getFile();
            }
        }

        return selResource;
    }

    public static IFolder getFolderByPath(IPath path) {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();

        return workspace.getRoot().getFolder(path);
    }

    public static IFile getFileByPath(IPath path) {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();

        return workspace.getRoot().getFile(path);
    }

//    /**
//     * This method sets up the Java nature on a project.
//     *
//     * @param project
//     */
//    protected static void setupJavaNature(IProject project)
//        throws CoreException {
//        addProjectNature(project, JavaCore.NATURE_ID);
//
//        // create default src and bin folders
//        IFolder srcFolder = WorkspaceManager.getSrcFolder(project);
//
//        if (!srcFolder.exists()) {
//            srcFolder.create(false, true, null);
//        }
//
//        IFolder classesFolder = WorkspaceManager.getClassesFolder(project);
//
//        if (!classesFolder.exists()) {
//            classesFolder.create(false, true, null);
//        }
//
//        // add classpath to java project
//        IJavaProject javaProject = JavaCore.create(project);
//        setupJavaNatureClasspath(javaProject);
//        
//        // change default output location to classes folder
//        javaProject.setOutputLocation(WorkspaceManager.getClassesFolder(project).
//            getFullPath(), null);
//
//
//    }

    public static void addProjectNature(IProject project, String natureID)
        throws CoreException {
        if (!project.hasNature(natureID)) {
            IProjectDescription description = project.getDescription();
            String[] natures = description.getNatureIds();
            String[] newNatures = new String[natures.length + 1];
            System.arraycopy(natures, 0, newNatures, 0, natures.length);
            newNatures[natures.length] = natureID;
            description.setNatureIds(newNatures);
            project.setDescription(description, null);
        }
    }

    /**
     * This method sets up the classpath that is needed to build the java code
     * that is produced by the java generator.
     *
     * @param codeGenHelper
     * @param javaProj
     * @throws CfgException
     * @throws JavaModelException
     */
//    protected static void setupJavaNatureClasspath(IJavaProject javaProj)
//        throws JavaModelException {
//        // Initialize entries            
//        List<IClasspathEntry> libraryEntries = new ArrayList<IClasspathEntry>();
//
//        // Add system JRE 
//        IClasspathEntry jreEntry = JavaRuntime.getJREVariableEntry();
//        libraryEntries.add(jreEntry);
//
//        // Add default entry for src folder in project
//        IClasspathEntry srccpe = JavaCore.newSourceEntry(getSrcFolder(
//                    javaProj.getProject()).getFullPath());
//        libraryEntries.add(srccpe);
//
//        // Set projects classpath
//        javaProj.setRawClasspath(libraryEntries.toArray(
//                (IClasspathEntry[])new IClasspathEntry[0]), null);
//    }
    
    public static boolean isPerspectiveOpen(IWorkbench workBench, String pspId) {
        IWorkbenchWindow window = workBench.getActiveWorkbenchWindow();
        if (window != null) {
            IWorkbenchPage page = window.getActivePage();
            if (page != null) {
                IPerspectiveDescriptor currentDescriptor =
                    page.getPerspective();
                String activeId = currentDescriptor.getId();
                if (activeId.equals(pspId)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    public static void openPerspective(IWorkbench workBench, String pspId) {
        if (!isPerspectiveOpen(workBench, pspId)) {
            IPerspectiveRegistry reg = workBench.getPerspectiveRegistry();
            IPerspectiveDescriptor pd = reg.findPerspectiveWithId(pspId);
            IWorkbenchWindow workbenchWin = workBench.getActiveWorkbenchWindow();
            IWorkbenchPage workbenchPage = workbenchWin.getActivePage();
            workbenchPage.setPerspective(pd);
        }
        LOG.debug("Workbench " + workBench + " changed to perspective: " + pspId);
    }

    public static IProject getProject(String projectName) {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();

        return root.getProject(projectName);
    }

//	public static IFolder getWSDLGenFolder(IPath path, IProject project) throws Exception {
//
//		ICompilationUnit unit = JDTUtils.findUnitByFileName(JDTUtils.getJavaProjectByName(project.getName()), path.toString());
//    	IPackageDeclaration[] decls = unit.getPackageDeclarations();
//    	String pkgName = "";
//    	if (decls != null && decls.length > 0) {
//    		pkgName = decls[0].getElementName();
//    	}
//    	
//        IFolder wsdlRoot = WorkspaceManager.getWSDLFolder(project);
//        if (!wsdlRoot.exists()) {
//            wsdlRoot.create(false, true, null);
//        }
//
//        IFolder wsdlLoc = wsdlRoot;
//        StringTokenizer tokenizer = new StringTokenizer(pkgName, ".");
//        while (tokenizer.hasMoreTokens()) {
//        	String token = tokenizer.nextToken();
//        	wsdlLoc = wsdlLoc.getFolder(token);
//        	if (!wsdlLoc.exists()) {
//        		wsdlLoc.create(false, true, null);
//        	}
//        }
//        return wsdlLoc;
//	}

	
	public static void addEntriesToClassPath(IJavaProject project,
			List<IClasspathEntry> newEntries) throws JavaModelException {

		IClasspathEntry[] existingEntries = project.getRawClasspath();

		for (int inx = 0; inx < existingEntries.length; inx++) {
			if (newEntries.contains(existingEntries[inx])) {
				while (newEntries.remove(existingEntries[inx])) {
					LOG.info("remove duplicated classpatth entries '"
							+ existingEntries[inx]
							+ "' from the entries added to the project: "
							+ project.getProject().getName());
				}
			}
		}

		IClasspathEntry[] concatenatedEntries = new IClasspathEntry[existingEntries.length
				+ newEntries.size()];
		System.arraycopy(existingEntries, 0, concatenatedEntries, 0,
				existingEntries.length);
		int index = existingEntries.length;
		for (Iterator<IClasspathEntry> iter = newEntries.iterator(); iter
				.hasNext();) {
			concatenatedEntries[index++] = iter.next();
		}

		project.setRawClasspath(concatenatedEntries, null);
	}
	
	/**
     * remove all the class path entries from the given project that are prefixed by the specified VAR name
     * @param jProject
     * @param varName
     * @throws JavaModelException 
     */
    public static void removeEntriesFromClassPath(IJavaProject jProject, String varName) throws JavaModelException {
        IClasspathEntry[] existingEntries = jProject.getRawClasspath();
        List<IClasspathEntry> cleanedEntries = new ArrayList<IClasspathEntry>(existingEntries.length);
        
        for (int inx = 0; inx < existingEntries.length; inx++) {
            if (existingEntries[inx].getPath().toOSString().indexOf(varName) == -1) {
                cleanedEntries.add(existingEntries[inx]);
            }
        }

        jProject.setRawClasspath(cleanedEntries.toArray(new IClasspathEntry[cleanedEntries.size()]), null);
    }
    
    
    public static List<IClasspathEntry> convertFilesToPathEnries(
			List<IPath> libPaths, String pathVarName) throws JavaModelException {
		if (libPaths == null || libPaths.size() == 0) {
			return new ArrayList<IClasspathEntry>(0);
		}

		List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(libPaths
				.size());
		IPath rootPath = (new Path(pathVarName)).addTrailingSeparator();
		for (Iterator<IPath> iter = libPaths.iterator(); iter.hasNext();) {
			entries.add(JavaCore.newLibraryEntry(((IPath) rootPath.clone())
					.append(iter.next()), null, null, true));
		}
		return entries;
	}
    
    public static IEditorPart openEditor(IWorkbenchWindow window, IFile file) {
    	return openEditor(window, file, IDE.getDefaultEditor(file).getId());
    }
    
    public static IEditorPart openEditor(IWorkbenchWindow window, IFile file, String editorId) {
    	IEditorPart editor = null;
    	IWorkbenchPage page = window.getActivePage();
    	
    	try {
    		if(page != null) {
    			if (editorId != null) {
    				editor = IDE.openEditor(page, file, editorId, true);
    			} else {
    				editor = IDE.openEditor(page, file, true);
    			}
    		}
    	} catch (PartInitException e) {
    		e.printStackTrace();
    	}
    	return editor;
    }
    
    /**
     * enable/disable the autobuild of current workspace
     * @param autoBuild
     * @throws CoreException
     */
    public static void setWorkspaceAutoBuild(boolean autoBuild)
			throws CoreException {
		IWorkspace ws = ResourcesPlugin.getWorkspace();
		IWorkspaceDescription desc = ws.getDescription();
		desc.setAutoBuilding(autoBuild);
		ws.setDescription(desc);
	}
    
    public static boolean getWorkspaceAutoBuild() {
    	IWorkspace ws = ResourcesPlugin.getWorkspace();
		IWorkspaceDescription desc = ws.getDescription();
		return desc.isAutoBuilding();
    }
	
}
