package nxm.sys.libg;

import java.awt.Choice;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.StringTokenizer;

import nxm.sys.inc.MessageHandler;
import nxm.sys.inc.ProvisionalUseOnly;

/** A class to implement a graphical Calendar widget.

  @author Wayne Dengel (Zeta Associates)
*/
public class GCalendar extends GWidget {
  static final int LEFT_GAP       = 5;
  static final int RIGHT_GAP      = 5;
  static final int TOP_GAP        = 20;
  static final byte BG            = 0;
  static final byte WEEKEND_BG    = 0;
  static final byte NEIGHBOR_MONTH_DATE   = 2;
  static final byte HIGHLIGHT     = 3;
  static final byte UNSELECTABLE  = 4;
  static final byte MAX_COLORS    = 5;
  static final byte MAX_CAL_CELLS = 42;
  private int   iCellWidth        = 0;
  private int   iCellHeight       = 0;
  private int   iCalendarWidth    = 0;
  private int   iCalendarHeight   = 0;
  private int   iWeekStart        = 0;
  private int   iTopSpace         = 0;
  private int   iBottomMargin     = 0;
  private int   userWidth         = 0;
  private int   userHeight        = 0;
  private byte  cbHighlightedCell = -1;
  private Date  dMin              = null;
  private Date  dMax              = null;
  private Date  dInitial          = null;
  private String  sWeekNames      = null;
  private Choice  months          = null;
  private Choice  years           = null;
  private Color  cCalendar[]      = new Color[MAX_COLORS];
  private calendarCell Cell[]     = new calendarCell[MAX_CAL_CELLS];

  /** Construct a new GCalendar, open the graphics window and add it to the specified parent.
     @param parent the parent Object
     @param name the name of this GCalendar
     @param flags widget flags
     @param mh the MessageHandler
   */
  public GCalendar (Object parent, String name, int flags,
      MessageHandler mh) {
    super("GCalendar",parent,name,flags,mh);
    open (name);
    addTo (parent);
  }

  /** Opens the graphics window.
     @param name not used ?
  */
  public void open (String name) {
    super.open();
    String sBuffer;

    Calendar calInitial  = Calendar.getInstance();
    months               = new Choice();
    years                = new Choice();
    cCalendar[BG]        = new Color(Color.lightGray.getRGB());
    cCalendar[WEEKEND_BG]           = new Color(cCalendar[BG].getRGB());
    cCalendar[NEIGHBOR_MONTH_DATE]  = new Color(Color.gray.getRGB());
    cCalendar[HIGHLIGHT]            = new Color(Color.red.getRGB());
    cCalendar[UNSELECTABLE]         = new Color(Color.white.getRGB());
    sWeekNames           = new String("S M T W T F S");
    iWeekStart           = 1;
    int iMinYear         = 2000;
    int iMaxYear         = 2100;

    for (int i=iMinYear;i<=iMaxYear;i++) {
      years.addItem(String.valueOf(i));
    }

    dMin = new Date();
    dMin = assignDateOf(2000,0,1);
    if (iMinYear > GetDateEntity(dMin,Calendar.YEAR))
      dMin = assignDateOf(iMinYear,0,1);

    dMax = new Date();
    dMax = assignDateOf(2100,11,31);
    if (iMaxYear<GetDateEntity(dMax,Calendar.YEAR))
      dMax = assignDateOf(iMaxYear,11,31);

    dInitial         = new Date();
    Calendar calToday   = Calendar.getInstance();
    dInitial       = calToday.getTime();

    sBuffer = new String("January February March April May June July August September October November December");
    StringTokenizer stMonths = new StringTokenizer(sBuffer," ");
    while (stMonths.hasMoreTokens())
      months.addItem(stMonths.nextToken());
    iTopSpace     = 10 + TOP_GAP;
    iBottomMargin = 5;

    calInitial.setTime(dInitial);
    years.select(String.valueOf(calInitial.get(Calendar.YEAR)));
    months.select(calInitial.get(Calendar.MONTH));

    for (int i=0;i<MAX_CAL_CELLS;i++) {
      Cell[i] = new calendarCell();
      Cell[i].date = new Date();
      if ((i%7 == (7 - iWeekStart)%7) || (i%7==(8-iWeekStart)%7))
        Cell[i].bWeekend = true;
    }
    userWidth =  userHeight = 120;   // default widget height,width
    SetCellDates();
    setup();
  }

  public void setup() {
    if ((userWidth!=0)&&(userHeight!=0)) {
        pos.w = userWidth;
        pos.h = userHeight;
    }
  }

  public void resize (int mode) {
    super.resize(mode);
  }

  /**
   *  @deprecated Since NeXtMidas 3.9.0 use {@link #paintComponent(Graphics)}
   */
  @Deprecated
  public void paint () {
    refresh();
  }

  @ProvisionalUseOnly("Treat this as protected and should only be called by subclass paintComponent methods")
  @Override
  public void paintComponent (Graphics g) {
    super.paintComponent(g); //3.8.1 Need to check if this should be called here. (or in all gcontrol classes)
    if (g==null) return;
    int i,iOffset;
    String sBuffer;
    MBox d=pos;

    if (parent instanceof MWindow) {
      if (userWidth==0) {
        d.w=pos.w-5;
        d.h=d.w;
      } else{
        d.w = userWidth;
        d.h = userHeight;
      }
    } else if (parent instanceof Container) {
      if (userWidth!=0) {
        d.w = userWidth;
        d.h = userHeight;
      }
    }

    iCellWidth      = (d.w - LEFT_GAP - RIGHT_GAP)/7;
    iCellHeight     = (d.h - iTopSpace - iBottomMargin)/6;
    iCalendarWidth  = iCellWidth * 7;
    iCalendarHeight = iCellHeight * 6;

    g.setColor(Color.white);
    g.drawLine(0,0,d.w,0);
    g.drawLine(0,1,d.w,1);
    g.drawLine(0,2,d.w,2);
    g.drawLine(0,0,0,d.h);
    g.drawLine(1,0,1,d.h);
    g.drawLine(2,0,2,d.h);
    g.setColor(cCalendar[BG].darker());
    g.drawLine(2,d.h-3,d.w,d.h-3);
    g.drawLine(1,d.h-2,d.w,d.h-2);
    g.drawLine(0,d.h-1,d.w,d.h-1);
    g.drawLine(d.w-1,0,d.w-1,d.h);
    g.drawLine(d.w-2,1,d.w-2,d.h);
    g.drawLine(d.w-3,2,d.w-3,d.h);
    g.setColor(cCalendar[BG]);
    g.fillRect(3,3,d.w-6,d.h-6);
    g.setColor(cCalendar[WEEKEND_BG]);
    g.fillRect(LEFT_GAP+iCellWidth*((7-iWeekStart)%7), iTopSpace,
               iCellWidth, iCalendarHeight);
    g.fillRect(LEFT_GAP+iCellWidth*((8-iWeekStart)%7), iTopSpace,
               iCellWidth, iCalendarHeight);


    Font fontWeeknames = new Font("Helvetica",Font.PLAIN,12);
    g.setFont(fontWeeknames);
    FontMetrics fm = g.getFontMetrics();
    StringTokenizer stWeekNames = new StringTokenizer(sWeekNames," ");
    i = 0;
    while (stWeekNames.hasMoreTokens()) {
      sBuffer = stWeekNames.nextToken();
      iOffset = fm.stringWidth(sBuffer)/2;
      g.setColor(Color.white);
      g.drawString(sBuffer,LEFT_GAP+i*iCellWidth+iCellWidth/2-iOffset,
                   iTopSpace-2-1);
      g.setColor(cCalendar[BG].darker());
      g.drawString(sBuffer,LEFT_GAP+i*iCellWidth+iCellWidth/2+1-iOffset,
                   iTopSpace-2);
      i++;
    }

    // Draw The Current Month String
    sBuffer = months.getSelectedItem()+" "+years.getSelectedItem();
    iOffset = fm.stringWidth(sBuffer)/2;
    g.setColor(cCalendar[NEIGHBOR_MONTH_DATE]);
    g.drawString(sBuffer,LEFT_GAP,iTopSpace/2);
    g.setColor(cCalendar[NEIGHBOR_MONTH_DATE].darker());
    g.drawString(sBuffer,LEFT_GAP+1,iTopSpace/2+1);

    g.setColor(Color.white);
    for (i=1;i<7;i++) {
      g.drawLine(LEFT_GAP, iTopSpace+i*iCellHeight-1, LEFT_GAP+iCalendarWidth-1,
                 iTopSpace+i*iCellHeight-1);
    }

    for (i=1;i<8;i++) {
      g.drawLine(LEFT_GAP+i*iCellWidth-1, iTopSpace, LEFT_GAP+i*iCellWidth-1,
                 iTopSpace+iCalendarHeight-1);
    }
    g.setColor(cCalendar[BG].darker());
    for (i=0;i<6;i++) {
      g.drawLine(LEFT_GAP,iTopSpace+i*iCellHeight, LEFT_GAP+iCalendarWidth-1,
                 iTopSpace+i*iCellHeight);
    }
    for (i=0;i<7;i++) {
      g.drawLine(LEFT_GAP+i*iCellWidth,iTopSpace, LEFT_GAP+i*iCellWidth,
                 iTopSpace+iCalendarHeight-1);
    }

    Font fontDates = new Font("Helvetica",Font.BOLD,(int)(iCellHeight*0.7));
    g.setFont(fontDates);
    DrawDates(g);

    if (cbHighlightedCell == -1) {
      Calendar calInital = Calendar.getInstance();
      calInital.setTime(dInitial);
      Calendar calCurrent = Calendar.getInstance();
      for (i=0;i<MAX_CAL_CELLS;i++) {
        calCurrent.setTime(Cell[i].date);
        if (calInital.get(Calendar.MONTH)==calCurrent.get(Calendar.MONTH)) {
          if (calInital.get(Calendar.DATE)==calCurrent.get(Calendar.DATE)) break;
        }
      }
      cbHighlightedCell = (byte)i;
    }
    if (Cell[cbHighlightedCell].bSelectable)
      highlightCell(g,cbHighlightedCell,cCalendar[HIGHLIGHT]); // Updated to use g in 3.9.0 for NXM-3213
  }

  @Override
  public int processEventMessage (String name, int info, Object data) {
    if (!name.equals("BUTTON")) return -1;
    if (info==1) {
      int x = px, y = py;
      int iClickedCell = getCellNumber(x,y);
      byte i;
      if (iClickedCell < 0) return -1;
      Date dBuffer = Cell[iClickedCell].date;
      long iTotalDaysUntildBuffer = GetTotalDaysUntil(dBuffer);
      try {

        if (x>LEFT_GAP && x<LEFT_GAP+iCalendarWidth &&
           y>iTopSpace && y<iTopSpace+iCalendarHeight &&
           Cell[iClickedCell].bSelectable) {
          if (months.getSelectedIndex()==GetDateEntity(dBuffer,Calendar.MONTH)) {
            // Removed these calls to highlightCell as it is done in paintComponent() since 3.9.0
            //highlightCell(cbHighlightedCell, Cell[cbHighlightedCell].bWeekend?cCalendar[WEEKEND_BG]:cCalendar[BG]);
            cbHighlightedCell = getCellNumber(x,y);
            //highlightCell(cbHighlightedCell,cCalendar[HIGHLIGHT]);
            if (Cell[cbHighlightedCell].bSelectable) {

              DecimalFormat decFormat = new DecimalFormat();
              decFormat.setMinimumIntegerDigits(2);
              String tstr = GetDateEntity(Cell[cbHighlightedCell].date,Calendar.YEAR)+":"+
                              decFormat.format(GetDateEntity(Cell[cbHighlightedCell].date,Calendar.MONTH)+1)+":"+
                              decFormat.format(GetDateEntity(Cell[cbHighlightedCell].date,Calendar.DATE));
              sendWidgetMessage (label,0,tstr);
              if (!is(PERM)) {
                close();
              }
            }
          } else {
            years.select(String.valueOf(GetDateEntity(dBuffer,Calendar.YEAR)));
            months.select(GetDateEntity(dBuffer,Calendar.MONTH));
            SetCellDates();
            for (i=0;i<MAX_CAL_CELLS;i++) {
              if (GetTotalDaysUntil(Cell[i].date)==iTotalDaysUntildBuffer) {
                cbHighlightedCell = i;
                break;
              }
            }
            // refresh(); // Pre Swing
          }
          refresh(); // Post Swing change
        }
      } catch (Exception ex) {
        M.warning("Exception "+ex+" in GCalendar");
      }
    } else if (info==3 && !is(PERM)) {
      close();
    }
    return 1;
  }

  @Override
  public Object setKey (String key, Object value) {
    return value;
  }

  @Override
  public Object getKey (String key) {
    Object value=null;
    if (key.equals("DATE")) {
      value = getDate();
    } else if (key.equals("MONTH")) {
      DecimalFormat decFormat = new DecimalFormat();
      decFormat.setMinimumIntegerDigits(2);
      value = decFormat.format(GetDateEntity(Cell[cbHighlightedCell].date,Calendar.MONTH)+1);
    } else if (key.equals("YEAR")) {
      value = Integer.toString(GetDateEntity(Cell[cbHighlightedCell].date,Calendar.YEAR));
    } else if (key.equals("DAY")) {
      DecimalFormat decFormat = new DecimalFormat();
      decFormat.setMinimumIntegerDigits(2);
      value = decFormat.format(GetDateEntity(Cell[cbHighlightedCell].date,Calendar.DATE));
    }
    return value;
  }

  private Date assignDateOf(int iYear, int iMonth, int iDate) {
    Calendar calBuf  = Calendar.getInstance();
    calBuf.set(iYear,iMonth,iDate);
    return calBuf.getTime();
  }

  /** Gets the value for a given time field.
     @param dBuffer the Date used to set the Calendar
     @param iEntity the time field to retrieve (See java.util.Calendar field summary for valid fields)
     @return the value of the requested time field
     @throws ArrayIndexOutOfBoundsException if specified iEntity is out of range
     (<code>iEntity &lt; 0 || iEntity &gt;= java.util.Calendar.FIELD_COUNT</code>).
  */
  public int GetDateEntity(Date dBuffer, int iEntity) {
    Calendar calBuf = Calendar.getInstance();
    calBuf.setTime(dBuffer);
    return calBuf.get(iEntity);
  }

  private void SetCellDates() {
    Date dBuffer = new Date();
    Calendar calBuffer = Calendar.getInstance();
    long lTotalDaysUpto_dMin, lTotalDaysUpto_dMax, lTotalDaysUptoDate;
    int i, iNumDaysInMonth,iCounter=0;

    lTotalDaysUpto_dMin  = GetTotalDaysUntil(dMin);
    lTotalDaysUpto_dMax = GetTotalDaysUntil(dMax);
    int iSelectedYear   = Integer.parseInt(years.getSelectedItem());
    int iSelectedMonth  = months.getSelectedIndex();
    dBuffer       = assignDateOf(iSelectedYear,iSelectedMonth,1);
    dBuffer       = GetWeekBeginDate(dBuffer);
    calBuffer.setTime(dBuffer);
    iNumDaysInMonth     = GetMonthDays(calBuffer.get(Calendar.YEAR),calBuffer.get(Calendar.MONTH));
    if (calBuffer.get(Calendar.DATE)!=1) {
      for (i=calBuffer.get(Calendar.DATE);i<=iNumDaysInMonth;i++) {
        Cell[iCounter].date = assignDateOf(calBuffer.get(Calendar.YEAR),calBuffer.get(Calendar.MONTH),i);
        lTotalDaysUptoDate = GetTotalDaysUntil(calBuffer.get(Calendar.YEAR),calBuffer.get(Calendar.MONTH),i);
        if (lTotalDaysUptoDate < lTotalDaysUpto_dMin || lTotalDaysUptoDate > lTotalDaysUpto_dMax) {
          Cell[iCounter].bSelectable = false;
        } else {
          Cell[iCounter].bSelectable = true;
        }
        iCounter++;
      }
    }
    dBuffer = assignDateOf(iSelectedYear,iSelectedMonth,1);
    calBuffer.setTime(dBuffer);
    iNumDaysInMonth = GetMonthDays(iSelectedYear,iSelectedMonth);
    for (i=1;i<=iNumDaysInMonth;i++) {
      Cell[iCounter].date = assignDateOf(iSelectedYear, iSelectedMonth, i);
      lTotalDaysUptoDate = GetTotalDaysUntil(iSelectedYear,iSelectedMonth,i);
      if (lTotalDaysUptoDate<lTotalDaysUpto_dMin||lTotalDaysUptoDate > lTotalDaysUpto_dMax) {
        Cell[iCounter].bSelectable = false;
      } else {
        Cell[iCounter].bSelectable = true;
      }
      iCounter++;
    }
    calBuffer.set(iSelectedYear,iSelectedMonth+1,1);

    for (i=1;iCounter<42;i++) {
      Cell[iCounter].date = assignDateOf(calBuffer.get(Calendar.YEAR),calBuffer.get(Calendar.MONTH),i);
      lTotalDaysUptoDate = GetTotalDaysUntil(calBuffer.get(Calendar.YEAR),calBuffer.get(Calendar.MONTH),i);
      if (lTotalDaysUptoDate<lTotalDaysUpto_dMin||lTotalDaysUptoDate > lTotalDaysUpto_dMax) {
        Cell[iCounter].bSelectable = false;
      } else {
        Cell[iCounter].bSelectable = true;
      }
      iCounter++;
    }
  }

  private void DrawDates(Graphics g) {
    int iHorizOffset,iVertOffset;
    String sBuffer;

    FontMetrics fm = g.getFontMetrics();
    iVertOffset = fm.getHeight()/2-fm.getMaxDescent()+1;

    for (int i=0;i<MAX_CAL_CELLS;i++) {
      sBuffer = String.valueOf(GetDateEntity(Cell[i].date,Calendar.DATE));
      iHorizOffset = fm.stringWidth(sBuffer)/2;

      if (!Cell[i].bSelectable) {
        g.setColor(cCalendar[UNSELECTABLE]);
      }
      else if (GetDateEntity(Cell[i].date,Calendar.MONTH)!=months.getSelectedIndex()) {
        g.setColor(cCalendar[NEIGHBOR_MONTH_DATE]);
      }
      else {
        g.setColor(Color.black);
      }
      g.drawString(sBuffer,LEFT_GAP+(i%7)*iCellWidth+iCellWidth/2-iHorizOffset,
                   iTopSpace+(i/7)*iCellHeight+iCellHeight/2+iVertOffset);
    }
  }

  /** Gets the day of the week on which the given date occurs.
     @param iYear the year
     @param iMonth the month
     @param iDate the date
     @return the day of the week on which the date occurs
  */
  public int GetDayFromDate(int iYear, int iMonth, int iDate) {
    return((int)(GetTotalDaysUntil(iYear,iMonth,iDate)%7L)+(7-iWeekStart))%7;
  }

  /** Gets the total number of days from now until the given date.
     @param dBuffer the Date object representing the date in question
     @return the number of days from now until the given date
  */
  public long GetTotalDaysUntil(Date dBuffer) {
    Calendar calBuffer = Calendar.getInstance();
    calBuffer.setTime(dBuffer);
    return GetTotalDaysUntil(calBuffer);
  }

  /** Gets the total number of days from now until the given date.
     @param calBuffer the Calendar object representing the date in question
     @return the number of days from now until the given date
  */
  public long GetTotalDaysUntil(Calendar calBuffer) {
    return GetTotalDaysUntil(calBuffer.get(Calendar.YEAR),calBuffer.get(Calendar.MONTH),calBuffer.get(Calendar.DATE));
  }

  /** Gets the total number of days from now until the given month, day, and year.
     @param iYear the year
     @param iMonth the month
     @param iDate the day of the month
     @return the number of days from now until the given date
  */
  public long GetTotalDaysUntil(int iYear, int iMonth, int iDate) {
    int i;
    long lTotal=0;
    int iDays[]={31,28,31,30,31,30,31,31,30,31,30,31};
    iMonth++;
    if (iYear<1 || iYear>9999||iMonth<1||iMonth>12)
      return -1;
    lTotal = ((iYear-1)*365L+(iYear-1)/4L);
    if (iYear>1752) {
      lTotal -= 11;
      lTotal -=(long)(((iYear-1)/100)-17);
      lTotal +=(long)(((iYear-1)/400)-4);
    }
    for (i=1;i<iMonth;i++)
      lTotal += (IsLeapYear(iYear)&&i==2)?29:iDays[i-1];

    lTotal +=(long)(iDate-1);
    return lTotal;
  }

  /** Determines whether the given year is a leap year.
     @param iYear the year
     @return <table><caption style="visibility:hidden">""</caption><tr><td><CODE>true</CODE></td><td>if the given year
     is a leap year</td></tr>
    <tr><td><CODE>false</CODE></td><td>otherwise</td></tr></table>
  */
  public boolean IsLeapYear(int iYear) {
    if (iYear%4 > 0)
      return false;
    if (iYear > 1752) {
      if (iYear%100==0) {
        if (iYear%400==0)
          return true;
        return false;
      }
    }
    return true;
  }

  /** Gets the number of days in the given month and year.
     @param iYear the year (is it a leap year?)
     @param iMonth the month
     @return the number of days in the given month
  */
  public int GetMonthDays(int iYear, int iMonth) {
    int iDays[] ={31,28,31,30,31,30,31,31,30,31,30,31};
    return (iMonth==1)&&IsLeapYear(iYear)?29:iDays[iMonth];
  }

  /** Gets the date of the beginning of the week in which the given date occurs.
     @param d the date to check the week's begin date
     @return the date of the day the week begins
  */
  public Date GetWeekBeginDate(Date d) {
    Calendar calBuffer = Calendar.getInstance();
    boolean bReturn = true;

    calBuffer.setTime(d);
    int iNewDate  = calBuffer.get(Calendar.DATE);
    int iNewMonth = calBuffer.get(Calendar.MONTH);
    int iNewYear  = calBuffer.get(Calendar.YEAR);
    long lTotalDays = GetTotalDaysUntil(iNewYear,iNewMonth,iNewDate);
    int iDay = GetDayFromDate(iNewYear,iNewMonth,iNewDate);
    long lTotalDaysUntilWeekBeginDate = lTotalDays - iDay;
    iNewDate -=iDay;
    if (iNewDate<1) {
      iNewDate = 21;
      iNewMonth -=1;
    }
    if (iNewMonth < 0) {
      iNewMonth=11;
      iNewYear -=1;
    }

    while (lTotalDaysUntilWeekBeginDate!=GetTotalDaysUntil(iNewYear,iNewMonth,iNewDate)) {
      iNewDate++;
      if (iNewDate>31) {
        bReturn = false;
        break;
      }
    }
    if (bReturn) {
      calBuffer.set(iNewYear,iNewMonth,iNewDate);
      d = calBuffer.getTime();
    }
    return d;
  }

  /** Gets the Date represented by the currently selected calendar cell.
     @return the Date selected by the user
  */
  public Date getSelection() {
    if (Cell[cbHighlightedCell].bSelectable) {
      return Cell[cbHighlightedCell].date;
    } else {
      return null;
    }
  }
  /** Gets the Date represented by the currently selected calendar cell
      and sets the integer parameters for month, date, and year accordingly.
     @param iYear the year
     @param iMonth the month
     @param iDate the date
  */
  public void getSelection(int iYear, int iMonth, int iDate) {
    Calendar calBuffer = Calendar.getInstance();
    calBuffer.setTime(getSelection());
    iYear = calBuffer.get(Calendar.YEAR);
    iMonth = calBuffer.get(Calendar.MONTH);
    iDate = calBuffer.get(Calendar.DATE);
  }

  /** Gets the Date represented by the currently selected calendar cell
      and sets the given parameters equal to String representations of the
      integer values for month, date, and year accordingly.
     @param sYear the year
     @param sMonth the month
     @param sDate the date
  */
  public void getSelection(String sYear, String sMonth, String sDate) {
    int iYear=0,iMonth=0,iDate=0;
    getSelection(iYear,iMonth,iDate);
    sYear  = String.valueOf(iYear);
    sMonth = String.valueOf(iMonth);
    sDate  = String.valueOf(iDate);
  }

  /** Gets the year field of the currently selected calendar cell.
     @return the year field
  */
  public int getSelectedYear() {
    return GetDateEntity(getSelection(),Calendar.YEAR);
  }

  /** Gets the month field of the currently selected calendar cell.
     @return the month field
  */
  public int getSelectedMonth() {
    return GetDateEntity(getSelection(),Calendar.MONTH)+1;
  }

  /** Gets the date field of the currently selected calendar cell.
     @return the date field
  */
  public int getSelectedDate() {
    return GetDateEntity(getSelection(),Calendar.DATE);
  }

  /** Sets the date for this GCalendar to the given month, date,
      and year, and refreshes the graphics display.
     @param iYear the year
     @param iMonth the month starting at 1
     @param iDOM the day of the month starting at 1
   */
  public void setDate(int iYear, int iMonth, int iDOM) {
    iMonth--; // The methods in java.util.Calendar addume month is zero-based.
    Calendar calInitial = Calendar.getInstance();
    dInitial = assignDateOf(iYear,iMonth,iDOM);
    calInitial.setTime(dInitial);
    years.select(String.valueOf(calInitial.get(Calendar.YEAR)));
    months.select(calInitial.get(Calendar.MONTH));
    SetCellDates();

    // This code block was in paint, but needs to be done here so that getKey(YEAR|MONTH|DAY) will retrieve correct date since 3.9.0 NXM-3213
    calInitial.setTime(dInitial);
    Calendar calCurrent = Calendar.getInstance();
    int i;
    for (i=0;i<MAX_CAL_CELLS;i++) {
      calCurrent.setTime(Cell[i].date);
      if (calInitial.get(Calendar.MONTH)==calCurrent.get(Calendar.MONTH)) {
        if (calInitial.get(Calendar.DATE)==calCurrent.get(Calendar.DATE)) break;
      }
    }
    cbHighlightedCell = (byte)i;

    refresh();
  }

  /** Get the selected date
   *  @return the selected date as a string.  Return NULL if none selected */
  public String getDate() {
    return Cell[cbHighlightedCell].date.toString();
  }

  /** See {@link #getDate() } */
  public Object getValue() {
    return getDate();
  }

  /** Sets the date using year, month and day of month
   *  See {@link #setDate(int,int,int)}
   *  @param iYear the year
   *  @param iMonth the month starting at 1
   *  @param iDOM the day of the month starting at 1
   */
  public void setValue (int iYear, int iMonth, int iDOM) {
    this.setDate(iYear, iMonth, iDOM);
  }

  private void highlightCell (Graphics g, int iCellNumber, Color cHighlight) {
    int iRow = iCellNumber/7,
      iColumn = iCellNumber % 7,
      x = iColumn * iCellWidth+2+LEFT_GAP,
      y = iRow * iCellHeight+2+iTopSpace;
    // this.getGraphics();
    g.setColor(cHighlight);
    g.drawLine(x,y,x+iCellWidth-4,y);
    g.drawLine(x+iCellWidth-4,y,x+iCellWidth-4,y+iCellHeight-4);
    g.drawLine(x+iCellWidth-4,y+iCellHeight-4,x,y+iCellHeight-4);
    g.drawLine(x,y+iCellHeight-4,x,y);
  }


  /** Get the cell number that at the specified screen coordinates

     @param x x-coordinate on screen where user clicked
     @param y y-coordinate on screen where user clicked
     @return cell number on calendar for specified coordinate if x,y is on the
             calendar, otherwise -1 if outside the bounds of the calendar
  */
  private byte getCellNumber(int x,int y) {
    int x1 = (x-LEFT_GAP);
    int y1 = (y-iTopSpace);

    // if x or y is above or to the left of the calendar return -1
    // we need this intermediate check because int/int returns int with the remainder dropped
    // hence -1 / 2, results in 0 (even though it is out of range)
    if (x1 < 0 || y1 < 0) return -1;

    int iColumn = (x1/iCellWidth);
    int iRow  = (y1/iCellHeight);
    // if column or rows are beyond calendar cells, return -1
    if (iColumn > 6 || iRow > 5) return -1;

    return (byte)((iRow*7)+iColumn);
  }

  private class calendarCell{
    Date  date;
    boolean bSelectable;
    boolean bWeekend;
  }
}
