package com.treelight.krnl;

import java.util.*;

/**
 * A node that encapsulates version-identification information.
 *
 * @version 0.1
 * @author Eric Armstrong
 * @see ../NodesAndLists.html
 */
public class VersionStamp implements NodeTypes, Comparable {

  // Note that these three values together define a globally unique node ID
  private UserID userID; // Identifies the author of the current version
  private SystemID systemID; // Identifies the system the version was created on
  private long timeStamp;  

  public String getUserID() { return userID; }
  public String getSystemID() { return systemID; }
  /** Returns time stamp -- MAY BE NULL. */
  public String getTimeStamp() { return timeStamp; }
  /** Sets time stamp. */
  public void setTime(Long timeStamp) {
    this.timeStamp = timeStamp;
  }

  /**
   * A link to the node that this version identifies. Used when 
   * the using system is processing a list of changes in order to
   * "undo" them.
   */
  private AbstractNode node;

  /**
   * Tests for node equality. This test will usually occur after
   * nodes created on one system are delivered to another. The
   * test therefore proceeds in the order most likely to produce
   * a difference: the systemID, then timesStamp, then userID.
   */
  public boolean equals(Object obj) 
  throws ClassCastException, UnstampedVersionException {
    VersionStamp targetStamp = (VersionStamp) obj;
    if (!this.systemID.equals(target.getSystemID())) return false;
    if (!this.timeStamp.equals(target.getTimeStamp())) return false;
    if (!this.userID.equals(target.getUserID())) return false;
    return true;
  }

  /**
   * The comparable interface is used in by the java.util 
   * list sorting mechanisms. Generally, the matter is
   * decided by the timestamp. When the timestamps happen
   * to be equal, we can do one of two things:
   * <ul>
   * <li>Identify the versions as equal, for the purposes
   *     of comparison. That defines a partial ordering that
   *     has a different definition for "equals" than the
   *     one above. That can be problematic. (See the
   *     java.util.Comparable interface.)<br>
   *     &nbsp;
   * <li>Return an ordering based on userID and systemID.
   *     That means that different systems will always have
   *     the same ordering of version lists (which could
   *     be handy), and that the equality operation is
   *     identical to the one defined in equals().
   * </ul>
   * Of the two possiblities, the latter makes the most
   * sense for compare(). For timeStamp-only comparisons,
   * use {@link #timeStampCompare}.
   */
  public boolean compare(Object obj) 
  throws ClassCastException, UnstampedVersionException {
    VersionStamp targetStamp = (VersionStamp) obj;
    int result = this.timeStamp.compare(target.getTimeStamp());
    if (result == -1) return -1; // less
    if (result == 1) return 1; // more

    // result = 0, so the timeStamps are equal
    if (!this.systemID.equals(target.getSystemID())) return false;
    if (!this.userID.equals(target.getUserID())) return false;
    return true;
  }

  /**
   * Returns -1 if this node's timeStamp is earlier than the
   * target node, +1 if later, and 0 if equal.
   */
  public int timeStampCompare(VersionStamp targetStamp)
  throws UnstampedVersionException { 
    TimeStamp targetTime = targetStamp.getTimeStamp();
    return this.timeStamp.compare(targetTime);
  }

  public long getTimeStamp()
  throws UnstampedVersionException { 
    if (time == null) throw new UnstampedVersionException()
    return timeStamp;
  }

  /**
   * Create a new version stamp that identifies the author of the
   * the new version. After creating the stamp, call (@link #setTime()}
   * to get the time value from the currently configured time server.
   *
   * Node: The node argument exists, so that a list of
   * unstamped versions can be processed serially, as a way of implementing
   * UNDO in the outer system. (For versioning, the KRNL needs a list of
   * nodes that have changed, where the set of changes constitutes the
   * new version. But for undo, the outer system needs a chronological
   * list of changes.
   */
  public void VersionStamp(AbstractNode node) {
    this.userID = VersionControl.getUserID;
    this.systemID = VersionControl.getSystemID;
    this.node = node;
  }

}//VersionStamp
