/**
 * Copyright wro4j@2011
 */
package ro.isdc.wro.model.resource.processor.decorator;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import ro.isdc.wro.model.group.processor.Minimize;
import ro.isdc.wro.model.resource.ResourceType;
import ro.isdc.wro.model.resource.SupportedResourceType;
import ro.isdc.wro.model.resource.processor.Destroyable;
import ro.isdc.wro.model.resource.processor.ImportAware;
import ro.isdc.wro.model.resource.processor.MinimizeAware;
import ro.isdc.wro.model.resource.processor.ResourceProcessorAware;
import ro.isdc.wro.model.resource.processor.SupportAware;
import ro.isdc.wro.model.resource.processor.SupportedResourceTypeAware;
import ro.isdc.wro.util.AbstractDecorator;


/**
 * Hides details common to all processors decorators, like ability to identify if a processor is minimize aware
 *
 * @author Alex Objelean
 * @created 11 Apr 2012
 * @since 1.4.6
 */
public abstract class AbstractProcessorDecoratorSupport<T>
    extends AbstractDecorator<T>
    implements ResourceProcessorAware {
  /**
   * @param the
   *          decorated processor. The type of the returned object is {@link Object} because we don't really care and we
   *          need it only to check if the processor is minimize aware and get its supported type. This "hack" will e
   *          removed in 1.5.0.
   */
  public AbstractProcessorDecoratorSupport(final T decorated) {
    super(decorated);
  }

  /**
   * This method is final, because it intends to preserve the getSupportedResourceType flag of the decorated processor.
   * You still can override this behavior by implementing
   * {@link AbstractProcessorDecoratorSupport#getSupportedResourceTypeInternal()} on your own risk.
   *
   * @return the {@link SupportedResourceType} annotation of the decorated processor if one exist.
   */
  public final SupportedResourceType getSupportedResourceType() {
    return getSupportedResourceTypeInternal();
  }

  /**
   * Allow subclass override the way getSupportedResourceType is used.
   */
  protected SupportedResourceType getSupportedResourceTypeInternal() {
    return getSupportedResourceTypeForProcessor(getDecoratedObject());
  }

  /**
   * Computes {@link SupportedResourceType} for provided processor.
   */
  final SupportedResourceType getSupportedResourceTypeForProcessor(final Object processor) {
    SupportedResourceType supportedType = processor.getClass().getAnnotation(SupportedResourceType.class);
    /**
     * This is a special case for processors which implement {@link SupportedResourceTypeProvider} interface. This is
     * useful for decorator processors which needs to "inherit" the {@link SupportedResourceType} of the decorated
     * processor.
     */
    if (processor instanceof SupportedResourceTypeAware) {
      supportedType = ((SupportedResourceTypeAware) processor).getSupportedResourceType();
    }
    return supportedType;
  }

  /**
   * This method is final, because it intends to preserve the minimize flag of the decorated processor. You still can
   * override this behavior by implementing {@link AbstractProcessorDecoratorSupport#isMinimizeInternal()} on your own
   * risk.
   */
  public final boolean isMinimize() {
    return isMinimizeInternal();
  }

  /**
   * Allow subclass override the way isMinimized is used.
   */
  protected boolean isMinimizeInternal() {
    return isMinimizeForProcessor(getDecoratedObject());
  }

  /**
   * {@inheritDoc}
   */
  public boolean isSupported() {
    return getDecoratedObject() instanceof SupportAware ? ((SupportAware) getDecoratedObject()).isSupported() : true;
  }

  /**
   * {@inheritDoc}
   */
  public boolean isImportAware() {
    return getDecoratedObject() instanceof ImportAware ? ((ImportAware) getDecoratedObject()).isImportAware() : false;
  }

  /**
   * {@inheritDoc}
   */
  public void destroy()
      throws Exception {
    if (getDecoratedObject() instanceof Destroyable) {
      ((Destroyable) getDecoratedObject()).destroy();
    }
  }

  /**
   * @return true if passed processor is minimize aware.
   */
  final boolean isMinimizeForProcessor(final Object processor) {
    if (processor instanceof MinimizeAware) {
      return ((MinimizeAware) processor).isMinimize();
    }
    return processor.getClass().isAnnotationPresent(Minimize.class);
  }

  /**
   * @return the array of supported resources the processor can process.
   */
  public final ResourceType[] getSupportedResourceTypes() {
    final SupportedResourceType supportedType = getSupportedResourceType();
    return supportedType == null ? ResourceType.values() : new ResourceType[] {
      supportedType.value()
    };
  }

  /**
   * {@inheritDoc}
   */
  public final void process(final Reader reader, final Writer writer)
      throws IOException {
    process(null, reader, writer);
  }
}
