/**
 * Copyright (c) 2007-2012 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.ecore.resource;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

import org.eclipse.core.runtime.content.IContentDescriber;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.impl.ContentHandlerRegistryImpl;

/**
 * A handler for describing the contents of URIs.
 * <p>
 * A content handler is used primarily by a {@link URIConverter URI converter}
 * which provides support for {@link URIConverter#contentDescription(URI, Map) describing} the contents of a URI
 * by virtue of having a {@link URIConverter#getContentHandlers() list} of content handlers
 * that it consults to determine whether the  handler {@link #canHandle(URI) can handle} the given URI and if so
 * that it uses as a delegate for computing the {@link #contentDescription(URI, InputStream, Map, Map) content description}.
 * </p>
 * @see URIHandler
 * @see URIConverter
 * @since 2.4
 */
public interface ContentHandler
{
  /**
   * A registry of content handlers based on a priority order; lower values indicate have higher priority.
   */
  interface Registry extends SortedMap<Integer, List<ContentHandler>>
  {
    /**
     * A constant indicating a very high priority content handler.
     */
    int VERY_HIGH_PRIORITY = -10000;

    /**
     * A constant indicating a high priority content handler.
     */
    int HIGH_PRIORITY = -1000;

    /**
     * A constant indicating a normal priority content handler.
     */
    int NORMAL_PRIORITY = 0;

    /**
     * A constant indicating a low priority content handler.
     */
    int LOW_PRIORITY = 1000;

    /**
     * A constant indicating a very low priority content handler.
     */
    int VERY_LOW_PRIORITY = 10000;

    /**
     * Adds an additional content handler with the given priority to the map.
     * If there is already a list for the given priority in the map, the handler is added to that list.
     * Otherwise, a new list containing the handler is created and put into the map.
     * @param priority the priority of the handler.
     * @param contentHandler the new handler to add.
     */
    void put(int priority, ContentHandler contentHandler);

    /**
     * Returns a read only list of all the content handlers in the map in priority order.
     * @return a read only list of all the content handlers in the map in priority order.
     */
    List<ContentHandler> contentHandlers();

    /**
     * The global static content handler registry instance.
     */
    Registry INSTANCE = new ContentHandlerRegistryImpl();
  }

  /**
   * Returns whether this handler can describe the contents for the given URI.
   * @param uri the URI to consider.
   * @return whether this handler can describe the contents for the given URI.
   */
  boolean canHandle(URI uri);

  /**
   * An option used to specify the {@link Set} of properties being requested when computing a {@link #contentDescription(URI, InputStream, Map, Map) content description}.
   * @see #VALIDITY_PROPERTY
   * @see #CONTENT_TYPE_PROPERTY
   * @see #BYTE_ORDER_MARK_PROPERTY
   * @see #CHARSET_PROPERTY
   * @see #LINE_DELIMITER_PROPERTY
   */
  String OPTION_REQUESTED_PROPERTIES = "REQUESTED_PROPERTIES";

  /**
   * A {@link #contentDescription(URI, InputStream, Map, Map) content description} property indicating the {@link Validity validity} of the content.
   * The value will be one of {@link Validity#INVALID}, {@link Validity#INDETERMINATE}, or {@link Validity#VALID}.
   * This property will always be present in a {@link #contentDescription(URI, InputStream, Map, Map) content description}.
   * @see Validity
   * @see IContentDescriber#INVALID
   * @see IContentDescriber#INDETERMINATE
   * @see IContentDescriber#VALID
   */
  String VALIDITY_PROPERTY = "org.eclipse.emf.ecore:validity";

  /**
   * A value specifying the validity of a {@link ContentHandler#contentDescription(URI, InputStream, Map, Map) content description}.
   * The {@link ContentHandler#VALIDITY_PROPERTY validity property} will have a value of this type,
   * i.., either {@link Validity#INVALID}, {@link Validity#INDETERMINATE}, or {@link Validity#VALID}.
   * @see IContentDescriber#INVALID
   * @see IContentDescriber#INDETERMINATE
   * @see IContentDescriber#VALID
   */
  enum Validity
  {
    /**
     * @see IContentDescriber#INVALID
     */
    INVALID,
    /**
     * @see IContentDescriber#INDETERMINATE
     */
    INDETERMINATE,
    /**
     * @see IContentDescriber#VALID
     */
    VALID
  }

  /**
   *  A {@link #contentDescription(URI, InputStream, Map, Map) content description} property describing the content's type identity.
   *  The value will be an arbitrary string.
   *  The content type is often used to {@link Resource.Factory.Registry#getContentTypeToFactoryMap() determine} an appropriate resource factory
   *  for processing the contents of a URI.
   *  @see IContentType#getId()
   *  @see Resource.Factory.Registry#getContentTypeToFactoryMap()
   *  @see Resource.Factory.Registry#getFactory(URI, String)
   */
  String CONTENT_TYPE_PROPERTY = "org.eclipse.emf.ecore:contentType";

  /**
   * A constant used to indicate that a {@link #CONTENT_TYPE_PROPERTY content type} needs to be computed.
   * @see ResourceSet#createResource(URI, String)
   * @see Resource.Factory.Registry#getFactory(URI, String)
   */
  String UNSPECIFIED_CONTENT_TYPE = "";

  /**
   * A {@link #contentDescription(URI, InputStream, Map, Map) content description} property describing the character set encoding used by the bytes of the content.
   * The value will be a string denoting a character set.
   * @see IContentDescription#CHARSET
   */
  String CHARSET_PROPERTY = "org.eclipse.core.runtime:charset";

  /**
   * A {@link #contentDescription(URI, InputStream, Map, Map) content description} property describing the line delimiter used by the characters of the content, 
   * as determined from the bytes of the content interpreted using {@link #CHARSET_PROPERTY appropriate character set}.
   * The value will be a string that can be used as a line delimiter.
   * @see IContentDescription#CHARSET
   * @since 2.9
   */
  String LINE_DELIMITER_PROPERTY = "org.eclipse.emf.ecore:lineDelimiter";

  /**
   * A {@link #contentDescription(URI, InputStream, Map, Map) content description} property describing the byte order mark at the beginning of the contents.
   * The value will be of type {@link ByteOrderMark}.
   * @see IContentDescription#BYTE_ORDER_MARK
   */
  String BYTE_ORDER_MARK_PROPERTY = "org.eclipse.core.runtime:bom";

  /**
   * A value specifying the byte order mark of a {@link ContentHandler#contentDescription(URI, InputStream, Map, Map) content description}.
   * The {@link ContentHandler#BYTE_ORDER_MARK_PROPERTY byte order mark property} will have a value of this type,
   * i.., either {@link #UTF_8}, {@link #UTF_16BE}, or {@link #UTF_16LE}.
   * @see IContentDescription#BYTE_ORDER_MARK
   * @see IContentDescription#BOM_UTF_8
   * @see IContentDescription#BOM_UTF_16BE
   * @see IContentDescription#BOM_UTF_16LE
   */
  enum ByteOrderMark
  {
    /**
     * A byte order mark indicating a UTF-8 encoding.
     * @see IContentDescription#BOM_UTF_8
     */
    UTF_8
      {
        @Override
        public byte[] bytes()
        {
          return UTF_8_BYTES;
        }
      },
    /**
     * A byte order mark indicating a UTF-16 big endian encoding.
     * @see IContentDescription#BOM_UTF_16BE
     */
    UTF_16BE
      {
        @Override
        public byte[] bytes()
        {
          return UTF_16BE_BYTES;
        }
      },
    /**
     * A byte order mark indicating a UTF-16 little endian encoding.
     * @see IContentDescription#BOM_UTF_16LE
     */
    UTF_16LE
      {
        @Override
        public byte[] bytes()
        {
          return UTF_16LE_BYTES;
        }
      };

    private static final byte [] UTF_8_BYTES;
    private static final byte [] UTF_16BE_BYTES;
    private static final byte [] UTF_16LE_BYTES;

    static
    {
      byte [] utf8Bytes;
      byte [] utf16BEBytes;
      byte [] utf16LEBytes;
      try
      {
        utf8Bytes = org.eclipse.core.runtime.content.IContentDescription.BOM_UTF_8;
        utf16BEBytes = org.eclipse.core.runtime.content.IContentDescription.BOM_UTF_16BE;
        utf16LEBytes = org.eclipse.core.runtime.content.IContentDescription.BOM_UTF_16LE;
      }
      catch (Throwable throwable)
      {
        utf8Bytes = new byte [] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
        utf16BEBytes = new byte [] {(byte) 0xFE, (byte) 0xFF};
        utf16LEBytes =  new byte [] {(byte) 0xFF, (byte) 0xFE};
      }
      UTF_8_BYTES = utf8Bytes;
      UTF_16BE_BYTES = utf16BEBytes;
      UTF_16LE_BYTES = utf16LEBytes;
    }

    /**
     * Returns the bytes associated with this byte order mark.
     * This value will be identical to the corresponding constant for {@link IContentDescription#BYTE_ORDER_MARK}.
     * @see IContentDescription#BYTE_ORDER_MARK
     * @see IContentDescription#BOM_UTF_8
     * @see IContentDescription#BOM_UTF_16BE
     * @see IContentDescription#BOM_UTF_16LE
     * @return the bytes associated with this byte order mark.
     */
    public abstract byte [] bytes();

    /**
     * Returns the byte order mark at the start of the input stream, or <code>null</code> if there isn't one.
     * @param inputStream the input stream to scan.
     * @return the byte order mark at the start of the input stream, or <code>null</code> if there isn't one.
     * @throws IOException if there is a problem reading from the input stream.
     */
    public static ByteOrderMark read(InputStream inputStream) throws IOException
    {
      int first = inputStream.read();
      if (first == 0xEF)
      {
        if (inputStream.read() == 0xBB && inputStream.read() == 0xBF)
        {
          return UTF_8;
        }
      }
      else if (first == 0xFE)
      {
        if (inputStream.read() == 0xFF)
        {
          return UTF_16BE;
        }
      }
      else if (first == 0xFF)
      {
        if (inputStream.read() == 0xFE)
        {
          return UTF_16LE;
        }
      }

      return null;
    }
  }

  /**
   * An unmodifiable {@link #contentDescription(URI, InputStream, Map, Map) content description} indicating that the content is invalid.
   */
  Map<String, Object> INVALID_CONTENT_DESCRIPTION =
    Collections.unmodifiableMap(org.eclipse.emf.ecore.resource.impl.ContentHandlerImpl.createContentDescription(Validity.INVALID));

  /**
   * Returns a map of properties that describe the content of the given URI's corresponding input stream.
   * The {@link #VALIDITY_PROPERTY validity property} will always be present to indicate the status.
   * The {@link #CONTENT_TYPE_PROPERTY content type property} too will always be present,
   * except when the validity property is {@link Validity#INVALID}.
   * The option {@link #OPTION_REQUESTED_PROPERTIES} can be used to specify the set of additional properties that should appear in the result.
   * If this option is not present, all properties this handler can compute will be returned.
   * The context map is used to cache results that can be shared between content handler implementations.
   * For example, once a content handler has computed the {@link #BYTE_ORDER_MARK_PROPERTY byte order mark property},
   * the result can be cached so that it is not recomputed repeatedly.
   * Similarly, content handlers for XML content might cache the {@link #CONTENT_TYPE_PROPERTY character set property},
   * and might even share a parsed XML representation so that each handle can analyze to determine whether that XML is of the expected form for the content type.
   *
   * @param uri the URI for which to determine the content description.
   * @param inputStream the input stream associated with the given URI.
   * @param options a map of options to direct what kind of description is needed.
   * @param context a map of contextual information that content handlers use to store partially computed results.
   * @return a map of properties that describe the content of the given URI's corresponding input stream.
   * @throws IOException if there is a problem reading the stream.
   */
  Map<String, ?> contentDescription(URI uri, InputStream inputStream, Map<?, ?> options, Map<Object, Object> context) throws IOException;
}
