// $Id: DateRange.java,v 1.3 2004/09/24 03:26:32 caron Exp $
/*
 * Copyright 2002-2004 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package thredds.datatype;

import ucar.nc2.units.*;
import java.util.Date;
import java.text.ParseException;

/**
 * Implements a range of dates, using DateType and/or TimeDuration.
 * You can use a DateType = "present" and a time duration to specify "real time" intervals, eg
 *   "last 3 days" uses endDate = "present" and duration = "3 days".
 *
 * @author john caron
 * @version $Revision: 1.3 $ $Date: 2004/09/24 03:26:32 $
 */

public class DateRange {
  private DateType start, end;
  private TimeDuration duration, resolution;
  boolean invalid;

  public DateRange(Date start, Date end) {
    this( new DateType(false, start), new DateType(false, end), null, null);
  }

  /**
   * Encapsolates a range of dates, using DateType and/or TimeDuration.
   *  A DateRange can be specified in any of the following ways:
    <ol>
     <li> a start date and end date
     <li> a start date and duration
     <li> an end date and duration
    </ol>
   *
   * @param start starting date
   * @param end ending date
   * @param duration time duration
   * @param resolution time resolution
   */
  public DateRange(DateType start, DateType end, TimeDuration duration, TimeDuration resolution) {
    this.start = start;
    this.end = end;
    this.duration = duration;
    try {
      this.resolution = (resolution == null) ? new TimeDuration("1 sec") : resolution;
    } catch (ParseException e) {
      // cant happen ha ha
    }

    boolean hasStart = (start != null) && !start.isBlank();
    boolean hasEnd = (end != null) && !end.isBlank();
    boolean hasDuration = (duration != null) && !duration.isBlank();

    invalid = true;
    if (hasStart && hasEnd) {
      invalid = false;
      recalcDuration();
    } else if (hasStart && hasDuration) {
      invalid = false;
      this.end = start.add( duration);
    } else if (hasEnd && hasDuration) {
      invalid = false;
      this.start = end.subtract( duration);
    }
    hashCode = 0;
  }

  private void recalcDuration() {

    long min = getStart().getDate().getTime();
    long max = getEnd().getDate().getTime();
    double secs = .001 * (max - min);
    if (secs < 0)
      secs = 0;

    TimeUnit rtu = resolution.getTimeUnit();
    double resSecs = rtu.getSeconds();

    // make it a multiple of resolution
    double closest = Math.round(secs / resSecs);
    secs = closest * resSecs;

    // how much in units of resolution ?
    try {
      TimeUnit t = rtu.newValue(secs);
      duration = new TimeDuration(t);

      // System.out.println("secs= "+secs+ " duration = "+duration.getTimeUnit().getValue());

    } catch (Exception e) {
      e.printStackTrace();
    }

    hashCode = 0;
  }

  /**
   * Determine if the given date is included in this date range.
   * The date range includes the start and end dates.
   */
  public boolean included( Date d) {
    if (invalid) return false;

    if (start.after( d)) return false;
    if (end.before( d)) return false;

    return true;
  }

  public DateType getStart() { return start; }
  public void setStart(DateType start) {
    this.start = start;
    recalcDuration();
    this.start = end.subtract( duration);
  }

  public DateType getEnd() { return end; }
  public void setEnd(DateType end) {
    this.end = end;
    recalcDuration();
    this.end = start.add( duration);
  }

  public TimeDuration getDuration() { return duration; }
  public void setDuration(TimeDuration duration) {
    this.duration = duration;
  }

  public TimeDuration getResolution() { return resolution; }
  public void setResolution(TimeDuration resolution) {
    this.resolution = resolution;
  }

  /**
   * Return true if start equals end date, so rdate range is a point.
   */
  public boolean isPoint() {
    return start.equals( end);
  }

  public String toString() { return "start= "+start +" end= "+end+ " duration= "+ duration
        + " resolution= "+ resolution; }

  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof DateRange)) return false;
    return o.hashCode() == this.hashCode();
  }

 /** Override Object.hashCode() to implement equals. */
  public int hashCode() {
    if (hashCode == 0) {
      int result = 17;
      if (start != null)
        result = 37*result + start.hashCode();
      if (end != null)
        result = 37*result + end.hashCode();
      if (duration != null)
        result = 37*result + duration.hashCode();
      if (resolution != null)
        result = 37*result + resolution.hashCode();
      hashCode = result;
    }
    return hashCode;
  }
  private volatile int hashCode = 0; // Bloch, item 8

}

/* Change History:
   $Log: DateRange.java,v $
   Revision 1.3  2004/09/24 03:26:32  caron
   merge nj22

*/