/*
 * 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.Type;
import org.gnu.gtk.event.CalendarEvent;
import org.gnu.gtk.event.CalendarListener;
import org.gnu.gtk.event.GtkEventType;

/**
 * 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());
		initializeEventHandlers();
	}

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

	/**
	 * 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.
	 */
	public void freeze() {
		Calendar.gtk_calendar_freeze(getHandle());
	}

	/**
	 * Defrosts a Calendar.  All changes made since the last
	 * <code>freeze()</code> are displayed.
	 */
	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) {
				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()) {
			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 handleMonthChanged() {
		fireCalendarEvent(new CalendarEvent(this, CalendarEvent.Type.MONTH_CHANGED));
	}

	private void handleYearChanged() {
		fireCalendarEvent(new CalendarEvent(this, CalendarEvent.Type.YEAR_CHANGED));
	}

	protected void initializeEventHandlers() {
		if (!eventsInitialized) {
			super.initializeEventHandlers();
			evtMap.initialize(this);
			eventsInitialized = true;
		}
	}

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

	public GtkEventType getEventType(String signal) {
		GtkEventType 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("month_changed", "handleMonthChanged", CalendarEvent.Type.MONTH_CHANGED, CalendarListener.class);
		anEvtMap.addEvent("next_year", "handleYearChanged", CalendarEvent.Type.YEAR_CHANGED, CalendarListener.class);
		anEvtMap.addEvent("prev_year", "handleYearChanged", CalendarEvent.Type.YEAR_CHANGED, CalendarListener.class);
		anEvtMap.addEvent("next_month", "handleMonthChanged", CalendarEvent.Type.MONTH_CHANGED, CalendarListener.class);
		anEvtMap.addEvent("prev_month", "handleMonthChanged", CalendarEvent.Type.MONTH_CHANGED, CalendarListener.class);
	}

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

	/****************************************
	 * BEGINNING OF JNI CODE
	 ****************************************/
	native static final protected int gtk_calendar_get_type();
	native static final protected int gtk_calendar_new();
	native static final protected int gtk_calendar_select_month(int calendar, int month, int year);
	native static final protected void gtk_calendar_select_day(int calendar, int day);
	native static final protected int gtk_calendar_mark_day(int calendar, int day);
	native static final protected int gtk_calendar_unmark_day(int calendar, int day);
	native static final protected void gtk_calendar_clear_marks(int calendar);
	native static final protected void gtk_calendar_set_display_options(int calendar, int flags);
	native static final protected int gtk_calendar_get_display_options(int calendar);
	native static final protected void gtk_calendar_get_date(int calendar, int[] year, int[] month, int[] day);
	native static final protected void gtk_calendar_freeze(int calendar);
	native static final protected void gtk_calendar_thaw(int calendar);
	/****************************************
	 * END OF JNI CODE
	 ****************************************/
}
