/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package java.sql;

/**
 * An implementation of java.sql.Timestame. Derived from
 * http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Timestamp.html. This is
 * basically just regular Date decorated with a nanoseconds field.
 */
public class Timestamp extends java.util.Date {

  public static Timestamp valueOf(String s) {
    String[] components = s.split(" ");
    if (components.length != 2) {
      throw new IllegalArgumentException("Invalid escape format: " + s);
    }

    String[] timeComponents = components[1].split("\\.");
    if (timeComponents.length != 2) {
      throw new IllegalArgumentException("Invalid escape format: " + s);
    } else if (timeComponents[1].length() != 9) {
      throw new IllegalArgumentException("Invalid escape format: " + s);
    }

    Date d = Date.valueOf(components[0]);
    Time t = Time.valueOf(timeComponents[0]);
    int nanos;
    try {
      nanos = Integer.valueOf(timeComponents[1]);
    } catch (NumberFormatException e) {
      throw new IllegalArgumentException("Invalid escape format: " + s);
    }

    return new Timestamp(d.getYear(), d.getMonth(), d.getDate(), t.getHours(),
        t.getMinutes(), t.getSeconds(), nanos);
  }

  private static String padNine(int value) {
    StringBuffer toReturn = new StringBuffer("000000000");
    assert toReturn.length() == 9;

    String asString = String.valueOf(value);
    toReturn = toReturn.replace(9 - asString.length(), 9, asString);

    assert toReturn.length() == 9;
    return toReturn.toString();
  }

  /**
   * Stores the nanosecond resolution of the timestamp; must be kept in sync
   * with the sub-second part of Date.millis.
   */
  private int nanos;

  @Deprecated
  public Timestamp(int year, int month, int date, int hour, int minute,
      int second, int nano) {
    super(year, month, date, hour, minute, second);
    setNanos(nano);
  }

  public Timestamp(long time) {
    super(time);

    // Seed the milliseconds in nanos
    nanos = (((int) (time % 1000)) * 1000000);
  }

  public boolean after(Timestamp ts) {
    return (getTime() > ts.getTime())
        || (getTime() == ts.getTime() && getNanos() > ts.getNanos());
  }

  public boolean before(Timestamp ts) {
    return (getTime() < ts.getTime())
        || (getTime() == ts.getTime() && getNanos() < ts.getNanos());
  }

  public int compareTo(java.util.Date o) {
    // JavaDoc says a ClassCastException is correct behavior
    return compareTo((Timestamp) o);
  }

  public int compareTo(Timestamp o) {
    int delta = (int) (getTime() - o.getTime());
    return delta == 0 ? getNanos() - o.getNanos() : delta;
  }

  public boolean equals(Object ts) {
    // Timestamps can't be compared to java.util.Date
    return ts instanceof Timestamp ? equals((Timestamp) ts) : false;
  }

  public boolean equals(Timestamp ts) {
    return (getTime() == ts.getTime() && getNanos() == ts.getNanos());
  }

  public int getNanos() {
    return nanos;
  }

  public long getTime() {
    return super.getTime();
  }

  public int hashCode() {
    // This is correct, per the Javadoc
    return super.hashCode();
  }

  public void setNanos(int n) {
    if (n < 0 || n > 999999999) {
      throw new IllegalArgumentException("nanos out of range " + n);
    }

    nanos = n;
    // Clear the sub-second part of the date; replace with new nanos.
    super.setTime((getTime() / 1000) * 1000 + (nanos / 1000000));
  }

  public void setTime(long time) {
    super.setTime(time);
    nanos = (((int) (time % 1000)) * 1000000);
  }

  public String toString() {
    return String.valueOf(1900 + getYear()) + "-" + padTwo(1 + getMonth())
        + "-" + padTwo(getDate()) + " " + padTwo(getHours()) + ":"
        + padTwo(getMinutes()) + ":" + padTwo(getSeconds()) + "."
        + padNine(getNanos());
  }
}
