/*
 * Copyright (c) 2008. All rights reserved.
 */
package ro.isdc.wro.model.resource;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;


/**
 * Encapsulates information about a resource. This class is not final because we need to mock it in unit tests.
 *
 * @author Alex Objelean
 * @created Created on Oct 30, 2008
 */
public class Resource {
  /**
   * The type of resource.
   */
  private ResourceType type;
  /**
   * Resource identifier.
   */
  private String uri;
  /**
   * Used to skip minimization during pre processing. Useful when you know that the resource is already minimized and no
   * minimization is needed. Default value is true.
   */
  private boolean minimize = true;

  /**
   * Empty constructor. Used by json deserializer.
   */
  public Resource() {
  }

  /**
   * Constructor.
   *
   * @param uri of the resource.
   * @param type of the resource.
   */
  private Resource(final String uri, final ResourceType type) {
    Validate.notNull(uri, "URI cannot be null!");
    Validate.notNull(type, "ResourceType cannot be null!");
    this.uri = cleanUri(uri);
    this.type = type;
  }


  /**
   * Factory method for Resource creation. A factory method is preferred instead of public constructor, in order to
   * avoid possibilities for clients to extend Resource class.
   *
   * @return an instance of {@link Resource} object.
   */
  public static Resource create(final String uri, final ResourceType type) {
    return new Resource(uri, type);
  }


  /**
   * Creates a resource and set the correct {@link ResourceType} based on uri extension. If resourceType cannot be
   * identified an exception is thrown.
   *
   * @param uri of the resource to create.
   * @return Resource with the provided uri and correct type.
   */
  public static Resource create(final String uri) {
    final String resourceExtension = FilenameUtils.getExtension(uri);
    final ResourceType type = ResourceType.get(resourceExtension);
    return new Resource(uri, type);
  }

  /**
   * Perform a cleaning of the uri by trimming it and removing last '/' character if exists.
   *
   * @param uri to clean.
   * @return cleaned uri.
   */
  private static String cleanUri(final String uri) {
    String result = uri.trim();
    //handle empty uri
    if (!StringUtils.isEmpty(uri)) {
      final int endIndex = result.length() - 1;
      if (result.lastIndexOf('/') == endIndex) {
        result = result.substring(0, endIndex);
      }
    }
    return result;
  }

  /**
   * @param type the type to set
   */
  public void setType(final ResourceType type) {
    Validate.notNull(type, "ResourceType cannot be null!");
    this.type = type;
  }


  /**
   * @param uri the uri to set
   */
  public void setUri(final String uri) {
    Validate.notNull(uri, "Uri cannot be null!");
    this.uri = cleanUri(uri);
  }


  /**
   * @return the type
   */
  public ResourceType getType() {
    return type;
  }


  /**
   * @return the uri associated with this resource.
   */
  public String getUri() {
    return uri;
  }


  /**
   * @return the minimize
   */
  public boolean isMinimize() {
    return this.minimize;
  }


  /**
   * @param minimize the minimize to set
   */
  public void setMinimize(final boolean minimize) {
    this.minimize = minimize;
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public boolean equals(final Object obj) {
    if (obj instanceof Resource) {
      final Resource resource = (Resource)obj;
      return getUri().equals(resource.getUri()) && getType().equals(resource.getType());
    }
    return false;
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public int hashCode() {
    int hash = 7;
    //handle null, it can happen when empty constructor is used.
    hash = 31 * hash + (this.type != null ? this.type.hashCode() : 0);
    hash = 31 * hash + (this.uri != null ? this.uri.hashCode() : 0);
    return hash;
  }


  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.NO_FIELD_NAMES_STYLE);
  }
}
