/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2004 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gtk;

import java.util.Vector;

import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.glib.Type;
import org.gnu.gtk.event.CalendarEvent;
import org.gnu.gtk.event.CalendarListener;
import org.gnu.glib.Handle;

/**
 * A Calendar is a widget that displays a calendar one month at a time.
 */
public class Calendar extends Widget {

    /**
     * Create a new Calendar object with the current date selected.
     */
    public Calendar() {
        super(gtk_calendar_new());
    }

    /**
     * Construct a calendar using a handle to a native resource.
     */
    public Calendar(Handle handle) {
        super(handle);
    }

    /**
     * Internal static factory method to be used by Java-Gnome only.
     */
    public static Calendar getCalendar(Handle handle) {
        if (handle == null) {
            return null;
        }

        Calendar obj = (Calendar) GObject.getGObjectFromHandle(handle);

        if (obj == null) {
            obj = new Calendar(handle);
        }

        return obj;
    }

    /**
     * Shifts the calendar to a different month.
     * 
     * @param month
     *            The month to select. This value is zero based.
     * @param year
     *            The year the month is in.
     */
    public void selectMonth(int month, int year) {
        Calendar.gtk_calendar_select_month(getHandle(), month, year);
    }

    /**
     * Selects a day from the current month.
     * 
     * @param day
     *            A day number between 1 and 31, or 0 to unselect the currently
     *            selected day.
     */
    public void selectDay(int day) {
        Calendar.gtk_calendar_select_day(getHandle(), day);
    }

    /**
     * Places a visual marker on a particular day.
     * 
     * @param day
     *            The day number to mark between 1 and 31.
     */
    public void markDay(int day) {
        Calendar.gtk_calendar_mark_day(getHandle(), day);
    }

    /**
     * Removes the visual marker from a particular day.
     * 
     * @param day
     *            The day number to unmark between 1 and 31.
     */
    public void unmarkDay(int day) {
        Calendar.gtk_calendar_unmark_day(getHandle(), day);
    }

    /**
     * Remove all visual marks.
     */
    public void clearMarks() {
        Calendar.gtk_calendar_clear_marks(getHandle());
    }

    /**
     * Sets the display options (whether to display the heading and the month
     * headings.
     * 
     * @param displayOptions
     *            The options to set.
     */
    public void setDisplayOptions(CalendarDisplayOptions displayOptions) {
        gtk_calendar_set_display_options(getHandle(), displayOptions.getValue());
    }

    /**
     * Gets the display options (whether to display the heading and the month
     * headings.
     */
    public CalendarDisplayOptions getDisplayOptions() {
        return CalendarDisplayOptions
                .intern(gtk_calendar_get_display_options(getHandle()));
    }

    /**
     * Gets the selected date from a Calendar.
     * 
     * @return A Calendar object that represents the date selected in the
     *         widget.
     */
    public java.util.Calendar getDate() {
        int[] year = new int[1];
        int[] month = new int[1];
        int[] day = new int[1];
        Calendar.gtk_calendar_get_date(getHandle(), year, month, day);
        java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.set(java.util.Calendar.YEAR, year[0]);
        cal.set(java.util.Calendar.MONTH, month[0]);
        cal.set(java.util.Calendar.DAY_OF_MONTH, day[0]);
        return cal;
    }

    /**
     * Locks the display of the Calendar until it is thawed with the
     * <code>thaw()</code> method.
     * 
     * @deprecated
     */
    public void freeze() {
        Calendar.gtk_calendar_freeze(getHandle());
    }

    /**
     * Defrosts a Calendar. All changes made since the last
     * <code>freeze()</code> are displayed.
     * 
     * @deprecated
     */
    public void thaw() {
        Calendar.gtk_calendar_thaw(getHandle());
    }

    /***************************************************************************
     * EVENT LISTENERS
     **************************************************************************/

    /**
     * Listeners for handling calendar events
     */
    private Vector calendarListeners = null;

    /**
     * Register an object to handle calendar events.
     * 
     * @see org.gnu.gtk.event.CalendarListener
     */
    public void addListener(CalendarListener listener) {
        // Don't add the listener a second time if it is in the Vector.
        int i = findListener(calendarListeners, listener);
        if (i == -1) {
            if (null == calendarListeners) {
                evtMap.initialize(this, CalendarEvent.Type.DAY_SELECTED);
                evtMap.initialize(this,
                        CalendarEvent.Type.DAY_SELECTED_DOUBLE_CLICK);
                evtMap.initialize(this, CalendarEvent.Type.MONTH_CHANGED_NEXT);
                evtMap.initialize(this, CalendarEvent.Type.MONTH_CHANGED_PREV);
                evtMap.initialize(this, CalendarEvent.Type.YEAR_CHANGED_NEXT);
                evtMap.initialize(this, CalendarEvent.Type.YEAR_CHANGED_PREV);
                calendarListeners = new Vector();
            }
            calendarListeners.addElement(listener);
        }
    }

    /**
     * Removes a listener
     * 
     * @see #addListener(CalendarListener)
     */
    public void removeListener(CalendarListener listener) {
        int i = findListener(calendarListeners, listener);
        if (i > -1) {
            calendarListeners.remove(i);
        }
        if (0 == calendarListeners.size()) {
            evtMap.uninitialize(this, CalendarEvent.Type.DAY_SELECTED);
            evtMap.uninitialize(this,
                    CalendarEvent.Type.DAY_SELECTED_DOUBLE_CLICK);
            evtMap.uninitialize(this, CalendarEvent.Type.MONTH_CHANGED_NEXT);
            evtMap.uninitialize(this, CalendarEvent.Type.MONTH_CHANGED_PREV);
            evtMap.uninitialize(this, CalendarEvent.Type.YEAR_CHANGED_NEXT);
            evtMap.uninitialize(this, CalendarEvent.Type.YEAR_CHANGED_PREV);
            calendarListeners = null;
        }
    }

    private void fireCalendarEvent(CalendarEvent event) {
        if (null == calendarListeners) {
            return;
        }
        int size = calendarListeners.size();
        int i = 0;
        while (i < size) {
            CalendarListener cl = (CalendarListener) calendarListeners
                    .elementAt(i);
            cl.calendarEvent(event);
            i++;
        }
    }

    private void handleDaySelected() {
        fireCalendarEvent(new CalendarEvent(this,
                CalendarEvent.Type.DAY_SELECTED));
    }

    private void handleDaySelectedDC() {
        fireCalendarEvent(new CalendarEvent(this,
                CalendarEvent.Type.DAY_SELECTED_DOUBLE_CLICK));
    }

    private void handleNextMonth() {
        fireCalendarEvent(new CalendarEvent(this,
                CalendarEvent.Type.MONTH_CHANGED_NEXT));
    }

    private void handlePrevMonth() {
        fireCalendarEvent(new CalendarEvent(this,
                CalendarEvent.Type.MONTH_CHANGED_PREV));
    }

    private void handleNextYear() {
        fireCalendarEvent(new CalendarEvent(this,
                CalendarEvent.Type.YEAR_CHANGED_NEXT));
    }

    private void handlePrevYear() {
        fireCalendarEvent(new CalendarEvent(this,
                CalendarEvent.Type.YEAR_CHANGED_PREV));
    }

    public Class getEventListenerClass(String signal) {
        Class cls = evtMap.getEventListenerClass(signal);
        if (cls == null)
            cls = super.getEventListenerClass(signal);
        return cls;
    }

    public EventType getEventType(String signal) {
        EventType et = evtMap.getEventType(signal);
        if (et == null)
            et = super.getEventType(signal);
        return et;
    }

    private static EventMap evtMap = new EventMap();
    static {
        addEvents(evtMap);
    }

    /**
     * Implementation method to build an EventMap for this widget class. Not
     * useful (or supported) for application use.
     */
    private static void addEvents(EventMap anEvtMap) {
        anEvtMap.addEvent("day_selected", "handleDaySelected",
                CalendarEvent.Type.DAY_SELECTED, CalendarListener.class);
        anEvtMap.addEvent("day_selected_double_click", "handleDaySelectedDC",
                CalendarEvent.Type.DAY_SELECTED_DOUBLE_CLICK,
                CalendarListener.class);
        anEvtMap.addEvent("next_year", "handleNextYear",
                CalendarEvent.Type.YEAR_CHANGED_NEXT, CalendarListener.class);
        anEvtMap.addEvent("prev_year", "handlePrevYear",
                CalendarEvent.Type.YEAR_CHANGED_PREV, CalendarListener.class);
        anEvtMap.addEvent("next_month", "handleNextMonth",
                CalendarEvent.Type.MONTH_CHANGED_NEXT, CalendarListener.class);
        anEvtMap.addEvent("prev_month", "handlePrevMonth",
                CalendarEvent.Type.MONTH_CHANGED_PREV, CalendarListener.class);
    }

    /**
     * Retrieve the runtime type used by the GLib library.
     */
    public static Type getType() {
        return new Type(gtk_calendar_get_type());
    }

    native static final protected int gtk_calendar_get_type();

    native static final protected Handle gtk_calendar_new();

    native static final protected int gtk_calendar_select_month(
            Handle calendar, int month, int year);

    native static final protected void gtk_calendar_select_day(Handle calendar,
            int day);

    native static final protected int gtk_calendar_mark_day(Handle calendar,
            int day);

    native static final protected int gtk_calendar_unmark_day(Handle calendar,
            int day);

    native static final protected void gtk_calendar_clear_marks(Handle calendar);

    native static final protected void gtk_calendar_set_display_options(
            Handle calendar, int flags);

    native static final protected int gtk_calendar_get_display_options(
            Handle calendar);

    native static final protected void gtk_calendar_get_date(Handle calendar,
            int[] year, int[] month, int[] day);

    native static final protected void gtk_calendar_freeze(Handle calendar);

    native static final protected void gtk_calendar_thaw(Handle calendar);

    /*
     * Deprecated functions. native static final private void
     * gtk_calendar_display_options(Handle calendar, int flags);
     */
}
