package sale;

/**
  * This class is an implementation of the {@link Time Time} interface.
  *
  * <p>The time is represented in the form of a simple date. The format is &quot;dd.mm.yy&quot;.</p>
  *
  * <p><strong>Note:</strong> This is a very simple implementation and it is <b>not</b> Y2K safe!</p>
  *
  * @author Sven Matznick
  * @version 2.0 15/06/1999
  * @since v2.0
  */
public class Date extends Object implements Time {

  /**
    * The current date.
    *
    * @serial
    */
  private String m_sDate;

  /**
    * The default interval for Dates.
    *
    * @serial
    */
  private String m_sDefaultInterval = new String ("1");

  /**
    * Is this a special year?
    *
    * @serial
    */
  private boolean m_fSpecialYear = true;

  /**
    * Creates a new date with standard starting time &quot;01.01.00&quot;.
    */
  public Date() {
    this ("01.01.00");
  }

  /**
    * Creates a new date with the given starting time.
    *
    * @param sNewDate a String representing the date to be set, format: dd.mm.yy (day.month.year)
    *
    * @exception IllegalArgumentException if the given String has an invalid format
    */
  public Date (String sNewDate)
    throws IllegalArgumentException {
    super();
    setTime (sNewDate);
  }

  /**
    * Set the given date.
    *
    * @override Never
    *
    * @param oTime a String representing the date to be set, format: dd.mm.yy (day.month.year)
    *
    * @exception IllegalArgumentException if the given String has an invalid format
    */
  public void setTime (Object oTime)
    throws IllegalArgumentException {
    validate (oTime);
    m_sDate = (String) oTime;
    long lHelp = Long.parseLong (m_sDate.substring (6,8));
    if (lHelp % 4 == 0) {
      m_fSpecialYear = true;
    }
    else {
      m_fSpecialYear = false;
    }
  }

  /**
    * Get the current date.
    *
    * @override Never
    *
    * @return a String representing the current date
    */
  public Object getTime() {
    return (m_sDate);
  }

  /**
    * Increment the time by the given time interval.
    *
    * @override Never
    *
    * @param oInterval the interval to increment time. Must be a String that gives the number of days by which
    * to increment.
    *
    * @exception IllegalArgumentException if the given interval is no String or cannot be transformed into a
    * long value.
    */
  public void goAhead (Object oInterval) throws IllegalArgumentException {
    if (!(oInterval instanceof String)) throw new IllegalArgumentException ("Parameter has to be of type String.");
    long lInterval = Long.parseLong ((String) oInterval);   // transform String to long
    long lDay = Long.parseLong (m_sDate.substring (0,2));
    long lMonth = Long.parseLong (m_sDate.substring (3,5));
    long lYear = Long.parseLong (m_sDate.substring (6,8));

    lDay += lInterval;               // increase days

    // now correct overflown days
    while ((((lMonth == 1) || (lMonth == 3) || (lMonth == 5) || (lMonth == 7) ||
             (lMonth == 8) || (lMonth == 10) || (lMonth == 12)) && (lDay > 31)) ||
           (((lMonth == 4) || (lMonth == 6) || (lMonth == 9) || (lMonth == 11)) &&
              (lDay > 30)) ||
             ((lMonth == 2) && ((lDay > 28) && (!m_fSpecialYear))) || ((lMonth == 2) && ((lDay > 29) && (m_fSpecialYear))))
      {   // still more days than this month has ?

      if (((lMonth == 1) || (lMonth == 3) || (lMonth == 5) || (lMonth == 7) ||
           (lMonth == 8) || (lMonth == 10) || (lMonth == 12)) && (lDay > 31)) { // 31 day month ?
        lDay -= 31; // okay, decrease
        lMonth++;   // next month
      }

      if (((lMonth == 4) || (lMonth == 6) || (lMonth == 9) || (lMonth == 11)) &&
              (lDay > 30)) {     // 30 day month ?
        lDay -= 30;  // okay, decrease
        lMonth++;    // next month
      }

      if ((lMonth == 2) && (lDay > 28)) {  // february ?
        if (!m_fSpecialYear) {
          lDay -= 28; // okay, descrease
          lMonth++;   // next month
        }
        else {
          if (lDay > 29) {
            lDay -= 29;
            lMonth++;   // next month
          }
        }
      }

      if (lMonth > 12) { // more than 12 month ?
        long i = 0;
        while (lMonth > 12) {
          lMonth -= 12;
          i++;  // how many years will pass ?
        }
        lYear+= i;;   // increase years
        if (lYear > 99) {lYear = 0;} // y2k bug ? no ! well, yes, but we don't care.
        if (lYear % 4 == 0) {
          m_fSpecialYear = true;
        }
        else {
          m_fSpecialYear = false;
        }
      }
    }

    // Build new String representation:
    String sHelp = "";
    if (lDay < 10) {sHelp = "0" + Long.toString(lDay) + ".";}
    else {sHelp = Long.toString(lDay) + ".";}
    if (lMonth < 10) {sHelp = sHelp + "0" + Long.toString(lMonth) + ".";}
    else {sHelp = sHelp + Long.toString(lMonth) + ".";}
    if (lYear < 10) {sHelp = sHelp + "0" + Long.toString(lYear);}
    else {sHelp = sHelp + Long.toString(lYear);}

    m_sDate = new String (sHelp); // set new time
  }

  /**
    * Get the default time interval.
    *
    * @override Never
    *
    * @return a String describing the default time step in days (standard: &quot;1&quot;).
    */
  public Object getDefaultInterval() {
    return (m_sDefaultInterval);
  }

  /**
    * Create and return a time stamp.
    *
    * @override Never
    *
    * @param lStampNumber the number of the stamp
    *
    * @return a fresh time stamp.
    */
  public Comparable getTimeStamp (long lStampNumber) {
    java.util.StringTokenizer stDate = new java.util.StringTokenizer (m_sDate, ".");

    String sReturn = ("000000000" + Long.toString (lStampNumber)).substring(Long.toString (lStampNumber).length());
                                              //number of stamp in this intervall

    sReturn = stDate.nextToken() + sReturn;   //day
    sReturn = stDate.nextToken() + sReturn;   //month
    sReturn = stDate.nextToken() + sReturn;   //year

    return sReturn;
  }

  /**
    * Returns the current date.
    *
    * @override Sometimes
    *
    * @return a String describing the current date
    */
  public String toString() {return m_sDate;}

  /**
    * Validate a given String, i.e. check it represents a date.
    *
    * @override Never
    */
  private void validate (Object oToValidate) throws IllegalArgumentException {
    if (!(oToValidate instanceof String)) throw new IllegalArgumentException ("Parameter has to be of type String.");
    if (!(((String) oToValidate).length() == 8)) throw new IllegalArgumentException ("Parameter has to consist of 8 chars: dd.mm.yy");
    java.util.StringTokenizer stToValidate = new java.util.StringTokenizer ((String) oToValidate, ".");

    String sDay = stToValidate.nextToken();
    if (sDay.length() != 2) throw new IllegalArgumentException ("Parameter has to consist of 8 chars: dd.mm.yy");

    String sMonth = stToValidate.nextToken();
    if (sMonth.length() != 2) throw new IllegalArgumentException ("Parameter has to consist of 8 chars: dd.mm.yy");

    String sYear = stToValidate.nextToken();
    if (sYear.length() != 2) throw new IllegalArgumentException ("Parameter has to consist of 8 chars: dd.mm.yy");

    long lDay = Long.parseLong (sDay);     // throws IllegalArgumentException if neccessary
    long lMonth = Long.parseLong (sMonth); // throws IllegalArgumentException if neccessary
    long lYear = Long.parseLong (sYear);   // throws IllegalArgumentException if neccessary

    if ((lMonth > 12) || (lMonth < 1)) throw new IllegalArgumentException ("Month has to be between 1 and 12");
    switch ((int) lMonth) {
      case 1: case 3: case 5: case 7: case 8: case 10: case 12:
        if ((lDay > 31) || (lDay < 1)) throw new IllegalArgumentException ("Day has to be between 1 and 31");
        break;
      case 4: case 6: case 9: case 11:
        if ((lDay > 30) || (lDay < 1)) throw new IllegalArgumentException ("Day has to be between 1 and 30");
        break;
      default : // case 2
        if (lYear % 4 == 0 && (lDay > 29 || lDay < 1)) throw new IllegalArgumentException ("Day has to be between 1 and 29");
        if (lYear % 4 != 0 && (lDay > 28 || lDay < 1)) throw new IllegalArgumentException ("Day has to be between 1 and 28");
    }
  }
  
  /** Convert the date to a {@link java.util.Date} object.
    * <B>Note:</B> As the entire sale.Date class is not Y2K compliant, this method also is not.
    *
    * @return The converted date.
    * @since v2.0 (06/07/2000)
    */
  public java.util.Date getDateValue() {
    try {
      java.util.StringTokenizer stToValidate = new java.util.StringTokenizer (m_sDate, ".");

      String sDay = stToValidate.nextToken();
      String sMonth = stToValidate.nextToken();
      String sYear = stToValidate.nextToken();

      long lDay = Long.parseLong (sDay);     // throws IllegalArgumentException if neccessary
      long lMonth = Long.parseLong (sMonth); // throws IllegalArgumentException if neccessary
      long lYear = Long.parseLong (sYear);   // throws IllegalArgumentException if neccessary
      
      java.util.Calendar c = java.util.Calendar.getInstance();
      c.clear();
      c.set ((int) lYear + 1900, (int) lMonth, (int) lDay);
      
      return c.getTime();
    }
    catch (IllegalArgumentException iae) {
      return null;
    }
  }
}