package com.marinilli.b2.c12;
import java.util.*;

/**
 * Chapter 12 - VersionIds representations
 *
 * A VersionId as defined in the JNLP specs. from Sun.
 * An example is represented by the string '1.2.2-ea'.
 *
 * @author Mauro Marinilli
 * @version 1.0
 */

public class VersionId extends Versionable implements Comparable {
  public final static char DOT_CHAR = '.';
  public final static String SEPARATORS = DOT_CHAR+"-_";//see JNLP specs. Appendix A
  private String[] singleValues;//see JNLP specs. Appendix A

  /**
   * Creates a new Version Id.
   * @param String s the string representing the version
   */
  protected VersionId(String s) {
    if (s==null)
      return;
    ArrayList arraylist = new ArrayList();
    int i = 0;
    for(int j = 0; j < s.length(); j++)
      if(SEPARATORS.indexOf(s.charAt(j)) !=-1) {
        if(i < j) {
          String s1 = s.substring(i, j);
          arraylist.add(s1);
        }
        i = j + 1;
      }

    if(i < s.length())
        arraylist.add(s.substring(i, s.length()));
    singleValues = new String[arraylist.size()];
    arraylist.toArray(singleValues);
  }

  /**
   * The exact match between two VersionIds
   */
  public boolean match(Versionable v) {
    if(v == null)
      return false;
    // to be refined
    if (v instanceof StarVersion)
      return ((StarVersion)v).contains(this);
    if (v instanceof PlusVersion)
      return ((PlusVersion)v).contains(this);
    if(v instanceof UnspecifiedVersion)
      return false;
    if(v instanceof VersionString) {
      return ((VersionString)v).contains(this);
    }
    if(!(v instanceof VersionId))
      return false;
    VersionId vid = (VersionId)v;
    return (compareToVersionId(vid)==0);
  }

  /**
   * compare a VersionId against another
   */
  public int compareToVersionId(VersionId vid) {
    if (vid instanceof UnspecifiedVersion )
      return 0;
    // normalize one against the other
    String as[] = normalize(singleValues, vid.getSingleValues().length);
    String as1[] = normalize(vid.getSingleValues(), singleValues.length);

    // then scan each single value against the respective one
    for(int i = 0; i < as.length; i++) {
      if(!as[i].equals(as1[i]))
        if (isNumber(as[i]) && isNumber(as1[i])) {
          int v1 = Integer.parseInt(as[i]);
          int v2 = Integer.parseInt(as1[i]);
          if (v1!=v2)
            return v1 - v2;
        } else {
          // we have at least a non-number, so compare them lexicographically
          // see JNLP specs, Appendix A
          return as[i].compareTo(as1[i]);
        }
      }//-for
      // if we arrive here the two versionIds are logically equal
      return 0;
  }

  public static boolean isNumber(String s){
    try {
        Integer.valueOf(s);
        return true;
    }
    catch(NumberFormatException _ex) {
      //don't care
    }
    return false;
  }

  /**
   * Implements the superclass method
   */
  public int compareTo(Object obj) {
    Versionable value = null;
    if (obj instanceof String)
      value = createVersionable((String)obj);
    if (obj instanceof Versionable)
      value = (Versionable)obj;
    if (value==null) {
      throw new ClassCastException("VersionId.compareTo() Cannot cast to Versionable or String classes");
    }

    //begins comparation
    //  remember that
    // <0 : this < value
    // =0 : this = value
    // >0 : this > value

    if (value instanceof VersionId)
      return compareToVersionId((VersionId) value);
    if (value instanceof StarVersion)
      return -1;

    return 0;
  }

  /**
   * An utility method when comparing VersionIds.
   * See JNLP  Specs. Appendix A.
   */
  protected String[] normalize(String pieces[], int maxLen) {
    if (pieces.length >= maxLen)
      return pieces;
    String normalized[] = new String[maxLen];
    System.arraycopy(pieces, 0, normalized, 0, pieces.length);
    Arrays.fill(normalized, pieces.length, maxLen, "0");
    return normalized;
  }

  public String[] getSingleValues(){
    return singleValues;
  }

  /**
   * Returns a human-readable representation of the versionId
   */
  public String toString() {
    StringBuffer stringbuffer = new StringBuffer();
    for(int i = 0; i < singleValues.length - 1; i++) {
      stringbuffer.append(singleValues[i]);
      stringbuffer.append(DOT_CHAR);
    }
    if(singleValues.length > 0)
      stringbuffer.append(singleValues[singleValues.length - 1]);
    return stringbuffer.toString();
  }

  /**
   * By now VersionIds are considered equals when they have the same
   * string representation. THIS IS NOT ALWAYS TRUE !
   *
   */
  public boolean equals(Object object) {
    /** @todo refine it better: THIS IS BUGGY!!*/
    if (!(object instanceof VersionId))
      return false;
    if (object instanceof UnspecifiedVersion)
      return false;
    VersionId other = (VersionId) object;
    return toString().equals(other.toString());
  }
}