/**
 * Copyright (c) 2012 Eclipse contributors and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 */
package org.eclipse.emf.ecore.xcore.ui;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
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.Platform;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xcore.interpreter.IClassLoaderProvider;
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.xtext.common.types.xtext.ui.XtextResourceSetBasedProjectProvider;
import org.osgi.framework.Bundle;

public class XcoreJavaProjectProvider extends XtextResourceSetBasedProjectProvider implements IClassLoaderProvider
{
  public List<IJavaProject> getJavaProjects(Resource resource)
  {
    List<IJavaProject> result = new UniqueEList<IJavaProject>();
    GenModel genModel = (GenModel)EcoreUtil.getObjectByType(resource.getContents(), GenModelPackage.Literals.GEN_MODEL);
    if (genModel != null)
    {
      result.add(getJavaProject(genModel.getModelDirectory()));
      result.add(getJavaProject(genModel.getEditDirectory()));
      result.add(getJavaProject(genModel.getEditorDirectory()));
      result.add(getJavaProject(genModel.getTestsDirectory()));
    }
    else
    {
      result.add(getJavaProject(resource.getResourceSet()));
    }
    result.remove(null);
    return result;
  }

  protected IJavaProject getJavaProject(String directory)
  {
    if (directory != null)
    {
      URI directoryURI = URI.createURI(directory);
      if (directoryURI.segmentCount() > 0)
      {
        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(directoryURI.segment(0));
        if (project.exists())
        {
          try
          {
            if (project.hasNature(JavaCore.NATURE_ID))
            {
              return JavaCore.create(project);
            }
          }
          catch (CoreException exception)
          {
            // Ignore.
          }
        } 
      }
    }
    return null;
  }

  @Override
  public IJavaProject getJavaProject(ResourceSet resourceSet)
  {
    IJavaProject result = super.getJavaProject(resourceSet);
    if (result == null)
    {
      for (Resource resource : resourceSet.getResources())
      {
        URI uri = resource.getURI();
        if (uri.isPlatformResource())
        {
          IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(uri.segment(1));
          if (project.exists())
          {
            try
            {
              if (project.hasNature(JavaCore.NATURE_ID))
              {
                result = JavaCore.create(project);
                break;
              }
            }
            catch (CoreException exception)
            {
              // Ignore.
            }
          }
        }
      }
    }
    return result;
  }

  public ClassLoader getClassLoader(ResourceSet resourceSet)
  {
    IJavaProject project = getJavaProject(resourceSet);
    if (project != null)
    {
      IProject iProject = project.getProject();
      IWorkspaceRoot workspaceRoot = iProject.getWorkspace().getRoot();
      List<URL> libraryURLs = new UniqueEList<URL>();
      try
      {
        getAllReferencedProjects(libraryURLs, new IProject[] { iProject });
        IClasspathEntry [] classpath = project.getResolvedClasspath(true);
        if (classpath != null)
        {
          String projectName = iProject.getName();
          for (int i = 0; i < classpath.length; ++i)
          {
            IClasspathEntry classpathEntry =  classpath[i];
            switch (classpathEntry.getEntryKind())
            {
              case IClasspathEntry.CPE_LIBRARY:
              case IClasspathEntry.CPE_CONTAINER:
              {
                IPath path = classpathEntry.getPath();
                if (path.segment(0).equals(projectName))
                {
                  path = iProject.getLocation().append(path.removeFirstSegments(1));
                }
                libraryURLs.add(new URL(URI.createFileURI(path.toString()).toString()));
                break;
              }
              case IClasspathEntry.CPE_PROJECT:
              {
                IPath path = classpathEntry.getPath();
                IProject referencedProject = workspaceRoot.getProject(path.segment(0));
                IJavaProject referencedJavaProject = JavaCore.create(referencedProject);
                IContainer container = workspaceRoot.getFolder(referencedJavaProject.getOutputLocation());
                libraryURLs.add(new URL(URI.createFileURI(container.getLocation().toString() + "/").toString()));

                IProjectDescription description = referencedProject.getDescription();
                getAllReferencedProjects(libraryURLs, description.getReferencedProjects());
                getAllReferencedProjects(libraryURLs, description.getDynamicReferences());
                break;
              }
              case IClasspathEntry.CPE_SOURCE:
              case IClasspathEntry.CPE_VARIABLE:
              default:
              {
                break;
              }
            }
          }
        }

        return new URLClassLoader(libraryURLs.toArray(new URL [libraryURLs.size()]),  getClass().getClassLoader());
      }
      catch (MalformedURLException exception)
      {
        exception.printStackTrace();
      }
      catch (JavaModelException exception)
      {
        exception.printStackTrace();
      }
      catch (CoreException exception)
      {
        exception.printStackTrace();
      }
    }
    for (Resource resource : resourceSet.getResources())
    {
      URI uri = resource.getURI();
      if (uri.isPlatformPlugin())
      {
        final Bundle bundle = Platform.getBundle(uri.segments()[1]);
        return
          new ClassLoader()
          {
            @Override
            public Enumeration<URL> findResources(String name) throws IOException
            {
              return bundle.getResources(name);
            }

            @Override
            public URL findResource(String name)
            {
              return bundle.getResource(name);
            }


            @Override
            public URL getResource(String name)
            {
              return findResource(name);
            }

            @Override
            public Class<?> findClass(String name) throws ClassNotFoundException
            {
              return bundle.loadClass(name);
            }

            @Override
            protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
            {
              Class<?> clazz = findClass(name);
              if (resolve)
              {
                resolveClass(clazz);
              }
              return clazz;
            }
          };
      }
    }
    return null;
  }

  protected void getAllReferencedProjects(Collection<URL> libraryURLs, IProject [] projects) throws CoreException, MalformedURLException
  {
    for (int i = 0; i < projects.length; ++i)
    {
      IProject project = projects[i];
      if (project.exists() && project.isOpen())
      {
        IJavaProject referencedJavaProject = JavaCore.create(project);
        IContainer container = project.getWorkspace().getRoot().getFolder(referencedJavaProject.getOutputLocation());

        if (libraryURLs.add(new URL(URI.createFileURI(container.getLocation().toString() + "/").toString())))
        {
          IProjectDescription description = project.getDescription();
          getAllReferencedProjects(libraryURLs, description.getReferencedProjects());
          getAllReferencedProjects(libraryURLs, description.getDynamicReferences());
        }
      }
    }
  }
}
