package stamp.peripheral.rtc;
import stamp.util.text.*;

/**
 * This class provides generic calendar time and date methods
 * that can be used by a variety of clock devices.
 *
 * To create a calendar object:
 * <p>
 * <code>
 *   Calendar cal = new Calendar(hun,sec,min,hrs,day,month,year);
 * </code>
 *
 * <p><pre>
 * @author Peter Verkaik (peterverkaik@boselectro.nl)
 * @version 1.5 September 30, 2003
 */

public class Calendar {

  //Calendar fields     //decimal contents
  public int hundredth; //0-99
  public int seconds;   //0-59
  public int minutes;   //0-59
  public int hours;     //0-23
  public int day;       //0-30
  public int month;     //0-11
  public int year;      //0-32767
  public int weekday;   //0-6
  /*
    Note: these fields are public for easy reading of the fields.
    Use the set() method to set the fields.
  */

  //weekday names and month names
  public static String[] dayName = new String[]{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
  public static String[] monthName = new String[]{"January","February","March","April","May","June","July","August","September","October","November","December"};

  //time and date conversion buffers
  private static char[] outbuf = new char[37];
  private static char[] inbuf = new char[10];
  private static Calendar cld = new Calendar();
  private static int[] val = new int[1];

  /**
   * Create calendar object.
   ( Initializes to January 1, 2003 00:00:00.00
   */
  public Calendar() {
    set(0, 0, 0, 0, 0, 0, 2003); //January 1, 2003, 00:00:00.00
  }

  /**
   * Create calendar object.
   (
   * @param hundredth hundredths of a second (0-99)
   * @param seconds   seconds (0-59)
   * @param minutes   minutes (0-59)
   * @param hours     hours (0-23)
   * @param day       day of the month (0-30)
   * @param month     month (0-11)
   * @param year      year (1900-32767)
   */
  public Calendar(int hundredth, int seconds, int minutes, int hours,
                  int day, int month, int year) {
    set(hundredth, seconds, minutes, hours, day, month, year);
  }

  /**
   * Set calendar record.
   * Although the fields are public, use this method to set a calendar
   * record as this method will set the weekday field correctly.
   *
   * @param hundredth hundredths of a second (0-99)
   * @param seconds   seconds (0-59)
   * @param minutes   minutes (0-59)
   * @param hours     hours (0-23)
   * @param day       day of the month (0-30)
   * @param month     month (0-11)
   * @param year      year (0-32767)
   */
  public void set(int hundredth, int seconds, int minutes, int hours,
                  int day, int month, int year) {
    this.hundredth = hundredth;
    this.seconds = seconds;
    this.minutes = minutes;
    this.hours = hours;
    this.day = day;
    this.month = month;
    this.year = year;
    weekDay();
  }

  /**
   * Convert bcd data to decimal data.
   * Valid bcd data range 0x0000 to 0x9999.
   *
   * @param bcdData bcd data to be converted.
   * @return decimal representation (0 to 9999) of bcd data.
   */
  public int bcd2dec(int bcdData) {
    int result;
    result = ((bcdData>>12)&0x0F)*1000;
    result = result + (((bcdData>>8)&0x0F)*100);
    result = result + (((bcdData>>4)&0x0F)*10);
    return result + (bcdData&0x0F);
  }

  /**
   * Convert decimal data to bcd data.
   * Valid decimal data range 0 to 9999.
   *
   * @param decData decimal data to be converted.
   * @return bcd representation (0x0000 to 0x9999) of decimal data.
   */
  public int dec2bcd(int decData) {
    int result;
    result = ((decData/1000)<<12);
    decData = decData % 1000;
    result = result | ((decData/100)<<8);
    decData = decData % 100;
    result = result | ((decData/10)<<4);
    return result | (decData % 10);
  }

  /**
   * Compare two strings s1 and s2 for equal start characters.
   *
   * @param s1 String that must equal s2 for all its characters
   * @param s2 String may have more characters than s1
   * @return True if s2 begins with s1.
   */
  private boolean strcmp(char[] s1, char[] s2) {
    int j; boolean test=false;
    j=0;
    while (s1[j]!=0 && s2[j]!=0) {
      test = (s1[j] == s2[j]);
      j++;
      if (!test) break;
    }
    return test;
  }

  /**
   * Calculate weekday 0-6 (sunday = 0) and update weekday field
   * The fields day, month and year must be valid
   */
  public void weekDay() {
    int dy, mn, yr;
    dy = day+1;   //adjust to 1-31
    mn = month+1; //adjust to 1-12
    yr = year;
    if (mn < 3) { //Jan & Feb = 13 & 14 preceding year
      mn += 12;
      yr--;
    }
    weekday = (dy + 2*mn + 3*(mn+1)/5 + yr + yr/4 - yr/100 + yr/400 + 1) % 7;
  }

  /**
   * Create HH:MM:SS.nn asciiz string
   *
   * @param hh True to include HH in output string
   * @param mm True to include MM in output string
   * @param ss True to include SS in output string
   * @param nn True to include NN in output string
   * @return Address of static buffer holding asciiz output string.
   *         The buffer will be overwritten by other methods.
   */
  public char[] ascTime(boolean hh, boolean mm, boolean ss, boolean nn) {
    int k=0;
    if (hh) k = Format.bprintf(outbuf,k,"%02d",hours);
    if (hh && mm) outbuf[k++] = ':';
    if (mm) k = Format.bprintf(outbuf,k,"%02d",minutes);
    if (mm && ss) outbuf[k++] = ':';
    if (ss) k = Format.bprintf(outbuf,k,"%02d",seconds);
    if (ss && nn) outbuf[k++] = '.';
    if (nn) k = Format.bprintf(outbuf,k,"%02d",hundredth);
    outbuf[k]=0;
    return outbuf;
  }

  /**
   * Convert HH:MM:SS.nn ascii string
   *
   * @param str character array holding the ascii string
   * @param index Index in str to start from
   * @param hh True to include HH in output string
   * @param mm True to include MM in output string
   * @param ss True to include SS in output string
   * @param nn True to include NN in output string
   * @return Static calendar record with time fields set.
   *         The static record will be overwritten by other methods.
   */
  public Calendar binTime(char[] str, int index, boolean hh, boolean mm, boolean ss, boolean nn) {
    int k=index;
    if (hh) {
      k = Format.bscanf(str,k," %d",val);
      cld.hours = val[0];
    }
    k++; //seperator
    if (mm) {
      k = Format.bscanf(str,k,"%d",val);
      cld.minutes = val[0];
    }
    k++; //seperator
    if (ss) {
      k = Format.bscanf(str,k,"%d",val);
      cld.seconds = val[0];
    }
    k++; //seperator
    if (nn) {
      k = Format.bscanf(str,k,"%d",val);
      cld.hundredth = val[0];
    }
    return cld;
  }

  /**
   * Create MM%DD%YY or MM%DD%YYYY asciiz string (US style)
   * Create DD%MM%YY or DD%MM%YYYY asciiz string (European style)
   *
   * @param US True for US style, false for european style
   * @param Long True for YYYY, false for YY
   * @param seperator Char to seperate fields (common are - and /)
   * @return Address of static buffer holding asciiz string.
   *         The buffer will be overwritten by other methods.
   */
  public char[] ascDate(boolean US, boolean Long, char seperator) {
    int k=0;
    if (US) {
      k = Format.bprintf(outbuf,k,"%02d",month+1);
      outbuf[k++] = seperator;
      k = Format.bprintf(outbuf,k,"%02d",day+1);
      outbuf[k++] = seperator;
    } else {
        k = Format.bprintf(outbuf,k,"%02d",day+1);
        outbuf[k++] = seperator;
        k = Format.bprintf(outbuf,k,"%02d",month+1);
        outbuf[k++] = seperator;
      }
    if (Long) k = Format.bprintf(outbuf,k,"%04d",year);
      else k = Format.bprintf(outbuf,k,"%02d",year%100);
    outbuf[k]=0;
    return outbuf;
  }

  /**
   * Convert MM%DD%YY or MM%DD%YYYY ascii string (US style)
   * Convert DD%MM%YY or DD%MM%YYYY ascii string (European style)
   *
   * @param str character array holding the ascii string
   * @param index Index in str to start from
   * @param US True for US style, false for european style
   * @return Static calendar record with date fields set.
   *         The static record will be overwritten by other methods.
   */
  public Calendar binDate(char[] str, int index, boolean US) {
    int k=0;
    if (US) {
      k = Format.bscanf(str,k," %d",val);
      cld.month = val[0]-1;
      k++; //seperator
      k = Format.bscanf(str,k,"%d",val);
      cld.day = val[0]-1;
      k++; //seperator
    } else {
        k = Format.bscanf(str,k," %d",val);
        cld.day = val[0]-1;
        k++; //seperator
        k = Format.bscanf(str,k,"%d",val);
        cld.month = val[0]-1;
        k++; //seperator
      }
    k = Format.bscanf(str,k,"%d",val);
    cld.year = val[0];
    cld.weekDay();
    return cld;
  }

  /**
   * Create Sun Jan 01 12:59:00 1999 asciiz string (short, fixed length)
   * Create Sunday January 1 12:59:00 1999 asciiz string (long, variable length)
   *
   * @param Long True for long, false for short
   * @return Address of static buffer holding asciiz string.
   *         The buffer will be overwritten by other methods.
   */
  public char[] ascClock(boolean Long) {
    int k=0;
    weekDay();
    if (Long) {
      k = Format.bprintf(outbuf,k,"%6.9s",dayName[weekday]);
      k = Format.bprintf(outbuf,k," %3.9s",monthName[month]);
      k = Format.bprintf(outbuf,k," %d",day+1);
    } else {
        k = Format.bprintf(outbuf,k,"%3.3s",dayName[weekday]);
        k = Format.bprintf(outbuf,k," %3.3s",monthName[month]);
        k = Format.bprintf(outbuf,k," %02d",day+1);
      }
    k = Format.bprintf(outbuf,k," %02d:",hours);
    k = Format.bprintf(outbuf,k,"%02d:",minutes);
    k = Format.bprintf(outbuf,k,"%02d",seconds);
    k = Format.bprintf(outbuf,k," %04d",year);
    outbuf[k]=0;
    return outbuf;
  }

  /**
   * Convert Sun Jan 01 12:59:00 1999 ascii string (short, fixed length)
   * Convert Sunday January 1 12:59:00 1999 ascii string (long, variable length)
   *
   * @param str character array holding the ascii string
   * @param index Index in str to start from
   * @return Static calendar record with all fields set.
   *         The static record will be overwritten by other methods.
   */
  public Calendar binClock(char[] str, int index) {
    int k=index; int n;
    k = Format.bscanf(str,k,"%s",inbuf);
    for (n=0; n<7; n++) {
      if (strcmp(inbuf,dayName[n].toCharArray())) break;
    }
    cld.weekday = n%7;
    k = Format.bscanf(str,k,"%s",inbuf);
    for (n=0; n<12; n++) {
      if (strcmp(inbuf,monthName[n].toCharArray())) break;
    }
    cld.month = n%12;
    k = Format.bscanf(str,k,"%d",val);
    cld.day = val[0]-1;
    k = Format.bscanf(str,k,"%d",val);
    cld.hours = val[0];
    k = Format.bscanf(str,k,":%d",val);
    cld.minutes = val[0];
    k = Format.bscanf(str,k,":%d",val);
    cld.seconds = val[0];
    k = Format.bscanf(str,k,"%d",val);
    cld.year = val[0];
    cld.hundredth = 0;
    return cld;
  }

  /**
   * Get calendar time into int
   *
   * @return time coded in int
   *         b15-b11 = hours     (0-23)
   *         b10- b5 = minutes   (0-59)
   *         b4 - b0 = seconds/2 (0-29)
   */
  public int getTimeInt() {
    return (hours<<11) | (minutes<<5) | (seconds>>1);
  }

  /**
   * Set calendar time from int
   *
   * @param value Int holding time
   *        b15-b11 = hours     (0-23)
   *        b10- b5 = minutes   (0-59)
   *        b4 - b0 = seconds/2 (0-29)
   */
  public void setTimeInt(int value) {
    hours = (value>>11)&0x1F;
    minutes = (value>>5)&0x3F;
    seconds = (value&0x1F)<<1;
  }

  /**
   * Get calendar date into int
   *
   * @return date coded in int
   *         b15- b9 = year%100 (0-99)
   *         b8 - b5 = month    (0-11)
   *         b4 - b0 = day      (0-30)
   */
  public int getDateInt() {
    return ((year%100)<<9) | (month<<5) | (day);
  }

  /**
   * Set calendar date from int.
   * This will also set the weekday field.
   *
   * @param value Int holding date
   *        b15- b9 = year%100 (0-99)
   *        b8 - b5 = month    (0-11)
   *        b4 - b0 = day      (0-30)
   */
  public void setDateInt(int value) {
    year = ((value>>9)&0x7F) + 2000;
    month = (value>>5)&0x0F;
    day = (value&0x1F);
    weekDay();
  }

} 