/**
 * Copyright (c) 2005-2007 IBM Corporation 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
 * 
 * Contributors: 
 *   IBM - Initial API and implementation
 */

package org.eclipse.emf.edit.ui.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.PlatformUI;

import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.edit.ui.EMFEditUIPlugin;

/**
 * @since 2.2.0
 */
public class EditUIUtil
{
  public static URI getURI(IEditorInput editorInput)
  {
    URI result = null;
    if (editorInput instanceof URIEditorInput)
    {
      result = ((URIEditorInput)editorInput).getURI().trimFragment();
    }
    else
    {
      result = URI.createURI(editorInput.getName());
    }
    return result;
  }
  
  /**
   * Returns the default editor for a given file name. This method is like {@link IEditorRegistry#getDefaultEditor(String)},
   * but it will not return <code>null</code> unless all applicable content types have no associated editor.
   * 
   * @param fileName the file name in the system
   * @return the descriptor of the default editor, or <code>null</code> if not found
   * @since 2.4
   */
  public static IEditorDescriptor getDefaultEditor(String fileName)
  {
    return fileName != null && fileName.length() != 0 ?
      getDefaultEditor(fileName, Platform.getContentTypeManager().findContentTypesFor(fileName)) :
      null;
  }

  /**
   * Returns the default editor for a given contents and file name.
   * <p>
   * If a file name is not provided, the entire content type registry will be queried. For performance reasons, it is
   * highly recommended to provide a file name if available.
   *
   * @param contents an input stream
   * @param fileName the file name associated to the contents, or <code>null</code> 
   * @return the descriptor of the default editor, or <code>null</code> if not found
   * @since 2.4
   */ 
  public static IEditorDescriptor getDefaultEditor(InputStream contents, String fileName)
  {
    if (contents != null)
    {
      try
      {
        return getDefaultEditor(fileName, Platform.getContentTypeManager().findContentTypesFor(contents, fileName));
      }
      catch (IOException e)
      {
        EMFEditUIPlugin.INSTANCE.log(e);
      }
    }
    return getDefaultEditor(fileName);
  }

  private static IEditorDescriptor getDefaultEditor(String fileName, IContentType[] contentTypes)
  {
    IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry();

    if (contentTypes.length == 0)
    {
      return editorRegistry.getDefaultEditor(fileName, null);
    }

    IEditorDescriptor result = null;
    for (int i = 0; result == null && i < contentTypes.length; i++)
    {
      result = editorRegistry.getDefaultEditor(fileName, contentTypes[i]);
    }
    return result;
  }

  /**
   * Returns the default editor for a given URI. This method actually attempts to open an input stream for the URI and
   * uses its contents, along with the filename (the URI's last segment), to obtain appropriate content types.
   * <p>
   * If a URI converter is specified, it is used to open the stream. Otherwise, the global default
   * {@link URIConverter#INSTANCE instance} is used.
   *
   * @param uri a URI
   * @param uriConverter URI converter from which to obtain an input stream, or <code>null</code>
   * @return the descriptor of the default editor, or <code>null</code> if not found
   * @since 2.4
   */ 
  public static IEditorDescriptor getDefaultEditor(URI uri, URIConverter uriConverter)
  {
    String fileName = URI.decode(uri.lastSegment());
    if (uriConverter == null)
    {
      uriConverter = URIConverter.INSTANCE;
    }
    InputStream stream = null;

    try
    {
      stream = uriConverter.createInputStream(uri);
      return getDefaultEditor(stream, fileName);
    }
    catch (IOException e)
    {
      EMFEditUIPlugin.INSTANCE.log(e);
      return getDefaultEditor(fileName);
    }
    finally
    {
      close(stream);
    }
  }

  private static void close(InputStream stream)
  {
    if (stream != null)
    {
      try
      {
        stream.close();
      }
      catch (IOException e) { EMFEditUIPlugin.INSTANCE.log(e); }
    }
  }

  /**
   * Returns the editors for a given file name. This method is like {@link IEditorRegistry#getEditors(String)},
   * but its result will include editors for all applicable content types.
   * 
   * @param fileName the file name in the system
   * @param defaultsOnly if <code>true</code>, only the default editor for each content type will be included in the result 
   * @return the descriptors of the editors
   * @since 2.4
   */
  public static IEditorDescriptor[] getEditors(String fileName, boolean defaultsOnly)
  {
    return fileName != null && fileName.length() != 0 ?
      getEditors(fileName, Platform.getContentTypeManager().findContentTypesFor(fileName), defaultsOnly) :
      new IEditorDescriptor[0];
  }

  /**
   * Returns the editors for a given contents and file name.
   * <p>
   * If a file name is not provided, the entire content type registry will be queried. For performance reasons, it is
   * highly recommended to provide a file name if available.
   * 
   * @param contents an input stream
   * @param fileName the file name associated to the contents, or <code>null</code> 
   * @param defaultsOnly if <code>true</code>, only the default editor for each content type will be included in the result 
   * @return the descriptors of the editors
   * @since 2.4
   */
  public static IEditorDescriptor[] getEditors(InputStream contents, String fileName, boolean defaultsOnly)
  {
    if (contents != null)
    {
      try
      {
        return getEditors(fileName, Platform.getContentTypeManager().findContentTypesFor(contents, fileName), defaultsOnly);
      }
      catch (IOException e)
      {
        EMFEditUIPlugin.INSTANCE.log(e);
      }
    }    
    return getEditors(fileName, defaultsOnly);
  }

  private static IEditorDescriptor[] getEditors(String fileName, IContentType[] contentTypes, boolean defaultsOnly)
  {
    IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry();

    if (contentTypes.length == 0)
    {
      return editorRegistry.getEditors(fileName, null);
    }

    List<IEditorDescriptor> result = new UniqueEList<IEditorDescriptor>();
    for (IContentType contentType : contentTypes)
    {
      if (defaultsOnly)
      {
        IEditorDescriptor editor = editorRegistry.getDefaultEditor(fileName, contentType);
        if (editor != null)
        {
          result.add(editor);
        }
      }
      else
      {
        result.addAll(Arrays.asList(editorRegistry.getEditors(fileName, contentType)));        
      }
    }
    return result.toArray(new IEditorDescriptor[result.size()]);
  }

  /**
   * Returns the editors for a given URI. This method actually attempts to open an input stream for the URI and
   * uses its contents, along with the filename (the URI's last segment), to obtain appropriate content types.
   * <p>
   * If a URI converter is specified, it is used to open the stream. Otherwise, the global default
   * {@link URIConverter#INSTANCE instance} is used.
   *
   * @param uri a URI
   * @param uriConverter a URI converter from which to obtain an input stream, or <code>null</code>
   * @param defaultsOnly if <code>true</code>, only the default editor for each content type will be included in the result 
   * @return the descriptors of the editors
   * @since 2.4
   */ 
  public static IEditorDescriptor[] getEditors(URI uri, URIConverter uriConverter, boolean defaultsOnly)
  {
    String fileName = URI.decode(uri.lastSegment());
    if (uriConverter == null)
    {
      uriConverter = URIConverter.INSTANCE;
    }
    InputStream stream = null;

    try
    {
      stream = uriConverter.createInputStream(uri);
      return getEditors(stream, fileName, defaultsOnly);
    }
    catch (IOException e)
    {
      EMFEditUIPlugin.INSTANCE.log(e);
      return getEditors(fileName, defaultsOnly);
    }
    finally
    {
      close(stream);
    }
  }
}
