package semorg.gui;

import java.sql.SQLException;
import java.sql.Timestamp;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import semorg.gui.list.CompanyBookingListWindow;
import semorg.gui.util.AssociationTabControl;
import semorg.gui.util.CalendarControl;
import semorg.gui.util.ForeignKeyConstraintSelector;
import semorg.gui.util.Messages;
import semorg.gui.util.CalendarControl.SWTCalendarEvent;
import semorg.gui.util.CalendarControl.SWTCalendarListener;
import semorg.sql.tables.AbstractTable;
import semorg.sql.tables.ClientBooking;
import semorg.sql.tables.CompanyBooking;
import semorg.sql.tables.SimpleIDKey;
import semorg.sql.util.Utility;

/** The window for editing new or existing companybooking records. */
public class CompanyBookingWindow {

    /**
     * Inner class which provides a listener reacting on changes of the input in
     * this window.
     */
    class InputChangedListener implements ModifyListener, SelectionListener,
	    SWTCalendarListener {

	/**
	 * Appends to the title of the window an asterisk (*) if the input has
	 * changed. Furthermore it sets the flag {@link #inputChanged} to
	 * <tt>true</tt>.
	 */
	private void inputChanged() {
	    if (!settingInput && !inputChanged) {
		inputChanged = true;
		shell.setText(shell.getText() + "*");
	    }
	}

	/** Invokes {@link #inputChanged()} if a text has been changed. */
	public void modifyText(ModifyEvent e) {
	    inputChanged();
	}

	/** Invokes {@link #inputChanged()} if a widget has been selected. */
	public void widgetSelected(SelectionEvent e) {
	    inputChanged();
	}

	/** Invokes {@link #inputChanged()} if a doubleclick has occured. */
	public void widgetDefaultSelected(SelectionEvent e) {
	    inputChanged();
	}

	/** Invokes {@link #inputChanged()} if a date has been changed. */
	public void dateChanged(SWTCalendarEvent event) {
	    inputChanged();
	}

    }

    /**
     * Holds the data of the actual displayed data record. If a new record is
     * entered it is set to <tt>null</tt> until the changes have been
     * committed.
     */
    private CompanyBooking input;

    /**
     * Is set to <tt>true</tt> if the data from the database (or corresponding
     * from {@link #input}) is to be set into the window form, otherwise
     * <tt>false</tt>. <br>
     * <br>
     * That is needed to prevent the ModifyListener from firing when the data is
     * load into the window form by using the .setText() methods of the gui
     * elements.
     */
    private boolean settingInput = false;

    /**
     * Is set to <tt>true</tt> if the input has been changed and not committed
     * yet, otherwise it is set to <tt>false</tt>.
     */
    private boolean inputChanged;

    /**
     * <tt>true</tt> if the editing of the input (i.e. the elements of the
     * form) has been enabled, otherwise it is <tt>false</tt>. <br>
     * <br>
     * This flag indicates whether the actual displayed record has been locked
     * by the actual editing window or not. This avoids the releasing of records
     * which are locked by other editing windows.
     */
    private boolean inputEditingEnabled;

    /** Listener instance added to the most elements of the window. */
    private InputChangedListener inputChangedListener = new InputChangedListener();

    /* **** GUI ELEMETS **** */

    /** The editing window. */
    private Shell shell;

    /** The height of the title bar. */
    private int titleBarHeight;

    /* Text- and Combofields, CalendarControls and Buttons */
    private final Label numberLabel;

    private final Text numberText;

    private final CalendarControl enrolledCalendar;

    private final CalendarControl confirmedCalendar;

    private final CalendarControl billedCalendar;

    private final CalendarControl signedOffCalendar;

    private final CalendarControl messagedCalendar;

    private final ForeignKeyConstraintSelector companySelector;

    private final ForeignKeyConstraintSelector presentationSelector;

    private final Button okButton;

    private final Button applyButton;

    private final Button newButton;

    private final Button abortButton;

    private final Button prevButton;

    private final Button nextButton;

    /** The button to create a message. */
    private final Button messageButton;

    /** The button to create the bill. */
    private final Button billButton;

    /** The button to handle repayments. */
    private final Button repaymentButton;

    /** The button to check the payment behavior. */
    private final Button paymentBehaviorButton;

    /**
     * Creates a {@link CompanyBookingWindow} instance with the given shell as
     * the parent shell. <br>
     * <br>
     * 
     * It also adds a listener which reacts on closing events. It checks if
     * changes have been made and if so it opens a dialog which informs the user
     * and asks for a decision (confirm/discard). Finally it releases the locked
     * record (if one is displayed and it is locked by this editing window).
     * 
     * @param parent
     *                Shell instance used the as parent shell of the window.
     * @param caller
     *                {@link AssociationTabControl} instance which called the
     *                editing window. If editing window is called by a menu this
     *                parameter is <tt>null</tt>.
     */
    private CompanyBookingWindow(Shell parent,
	    final AssociationTabControl caller) {

	shell = new Shell(parent, SWT.CLOSE);

	shell.addListener(SWT.Close, new Listener() {
	    public void handleEvent(Event event) {
		if (inputChanged) {
		    if (confirmClose()) {
			if (inputEditingEnabled && input != null)
			    CompanyBooking.lockedIds.remove(new Integer(input
				    .getId()));
		    } else
			event.doit = false;
		} else if (input != null && inputEditingEnabled)
		    CompanyBooking.lockedIds.remove(new Integer(input.getId()));
	    }
	});

	shell.setLayout(new GridLayout(1, false));
	shell.setSize(425, 415);
	shell.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "CompanyBookingWindow");

	GridData mgd = new GridData();
	mgd.horizontalAlignment = GridData.FILL;
	mgd.verticalAlignment = GridData.FILL;
	mgd.grabExcessHorizontalSpace = true;
	mgd.grabExcessVerticalSpace = true;

	final Group numberField = new Group(shell, SWT.NULL);
	numberField.setLayout(new GridLayout(1, false));
	GridData numberFieldGridData = new GridData();
	numberFieldGridData.horizontalAlignment = GridData.CENTER;
	numberFieldGridData.grabExcessHorizontalSpace = true;
	numberFieldGridData.grabExcessVerticalSpace = true;
	numberField.setLayoutData(numberFieldGridData);

	final Group dateField = new Group(shell, SWT.NULL);
	dateField.setLayout(new GridLayout(2, false));
	GridData dateFieldGridData = new GridData();
	dateFieldGridData.horizontalAlignment = GridData.CENTER;
	dateFieldGridData.grabExcessHorizontalSpace = true;
	dateField.setLayoutData(dateFieldGridData);

	final Group connectionField = new Group(shell, SWT.NULL);
	connectionField.setLayout(new GridLayout(2, false));
	GridData connectionFieldGridData = new GridData();
	connectionFieldGridData.horizontalAlignment = GridData.FILL;
	connectionFieldGridData.grabExcessHorizontalSpace = true;
	connectionField.setLayoutData(connectionFieldGridData);

	numberLabel = new Label(numberField, SWT.READ_ONLY);
	numberLabel.setText(Messages.getString("GUIText.NumberText"));

	numberText = new Text(numberField, SWT.SINGLE | SWT.BORDER
		| SWT.READ_ONLY);
	numberText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "NumberText");

	Utility.setLength(numberText, 10);
	numberText.addModifyListener(inputChangedListener);
	numberText.setToolTipText(Messages.getString("GUIText.NumberToolTip"));

	final Label enrolledLabel = new Label(dateField, SWT.READ_ONLY);
	enrolledLabel.setText(Messages.getString("GUIText.EnrolledText"));
	Utility.alignRight(enrolledLabel);

	enrolledCalendar = new CalendarControl(dateField, titleBarHeight);
	enrolledCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"EnrolledCalendar");
	enrolledCalendar.addSWTCalendarlistener(inputChangedListener);

	Label confirmedLabel = new Label(dateField, SWT.READ_ONLY);
	confirmedLabel.setText(Messages.getString("GUIText.ConfirmedText"));
	Utility.alignRight(confirmedLabel);

	confirmedCalendar = new CalendarControl(dateField, titleBarHeight);
	confirmedCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"ConfirmedCalendar");
	confirmedCalendar.addSWTCalendarlistener(inputChangedListener);

	Label billedLabel = new Label(dateField, SWT.READ_ONLY);
	billedLabel.setText(Messages.getString("GUIText.BilledText"));
	Utility.alignRight(billedLabel);

	billedCalendar = new CalendarControl(dateField, titleBarHeight);
	billedCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"BilledCalendar");
	billedCalendar.addSWTCalendarlistener(inputChangedListener);

	Label signedOffLabel = new Label(dateField, SWT.READ_ONLY);
	signedOffLabel.setText(Messages.getString("GUIText.SignedOffText"));
	Utility.alignRight(signedOffLabel);

	signedOffCalendar = new CalendarControl(dateField, titleBarHeight);
	signedOffCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"SignedOffCalendar");
	signedOffCalendar.addSWTCalendarlistener(inputChangedListener);

	Label messagedLabel = new Label(dateField, SWT.READ_ONLY);
	messagedLabel.setText(Messages.getString("GUIText.MessagedText"));
	Utility.alignRight(messagedLabel);

	messagedCalendar = new CalendarControl(dateField, titleBarHeight);
	messagedCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"MessagedCalendar");
	messagedCalendar.addSWTCalendarlistener(inputChangedListener);

	Label companyLabel = new Label(connectionField, SWT.NULL);
	companyLabel.setText(Messages.getString("GUIText.CustomerText"));
	Utility.alignRight(companyLabel);

	companySelector = new ForeignKeyConstraintSelector(connectionField,
		ForeignKeyConstraintSelector.FK_COMPANY);
	companySelector.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"CompanySelector");
	companySelector.addModifyListener(inputChangedListener);

	Label presentationLabel = new Label(connectionField, SWT.NULL);
	presentationLabel.setText(Messages
		.getString("GUIText.CompanyInternalPresentationText"));
	Utility.alignRight(presentationLabel);

	presentationSelector = new ForeignKeyConstraintSelector(
		connectionField,
		ForeignKeyConstraintSelector.FK_COMPANYINTERNALPRESENTATION);
	presentationSelector.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"PresentationSelector");
	presentationSelector.addModifyListener(inputChangedListener);

	final Composite newButtons = new Composite(shell, shell.getStyle());
	GridLayout newButtonsLayout = new GridLayout(7, false);
	newButtonsLayout.marginBottom = 5;
	newButtons.setLayout(newButtonsLayout);
	GridData newButtonsLayoutData = new GridData();
	newButtonsLayoutData.horizontalAlignment = GridData.CENTER;
	newButtons.setLayoutData(newButtonsLayoutData);

	messageButton = new Button(newButtons, SWT.PUSH);
	messageButton.setText(Messages.getString("GUIText.MessageButtonText"));
	messageButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_HORIZONTAL));

	billButton = new Button(newButtons, SWT.PUSH);
	billButton.setText(Messages.getString("GUIText.BillButtonText"));
	billButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_HORIZONTAL));

	repaymentButton = new Button(newButtons, SWT.PUSH);
	repaymentButton.setText(Messages
		.getString("GUIText.RepaymentButtonText"));
	repaymentButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_HORIZONTAL));

	paymentBehaviorButton = new Button(newButtons, SWT.PUSH);
	paymentBehaviorButton.setText(Messages
		.getString("GUIText.PaymentBehaviorButtonText"));
	paymentBehaviorButton.setLayoutData(new GridData(
		GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));

	final Composite buttons = new Composite(shell, shell.getStyle());
	GridLayout buttonsLayout = new GridLayout(7, false);
	buttons.setLayout(buttonsLayout);
	GridData buttonsLayoutData = new GridData();
	buttonsLayoutData.horizontalAlignment = GridData.CENTER;
	buttons.setLayoutData(buttonsLayoutData);

	okButton = new Button(buttons, SWT.PUSH);
	okButton.setText(Messages.getString("GUIText.OkButtonText")); //$NON-NLS-1$
	okButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	applyButton = new Button(buttons, SWT.PUSH);
	applyButton.setText(Messages.getString("GUIText.ApplyButtonText")); //$NON-NLS-1$
	applyButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	newButton = new Button(buttons, SWT.PUSH);
	newButton.setText(Messages.getString("GUIText.NewButtonText")); //$NON-NLS-1$
	newButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	abortButton = new Button(buttons, SWT.PUSH);
	abortButton.setText(Messages.getString("GUIText.AbortButtonText")); //$NON-NLS-1$
	abortButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	GridData arrowButtonGridData = new GridData();
	arrowButtonGridData.verticalAlignment = GridData.FILL;
	arrowButtonGridData.grabExcessVerticalSpace = true;
	arrowButtonGridData.widthHint = 23;

	prevButton = new Button(buttons, SWT.ARROW | SWT.LEFT);
	prevButton.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "Previous");
	prevButton.setLayoutData(arrowButtonGridData);

	nextButton = new Button(buttons, SWT.ARROW | SWT.RIGHT);
	nextButton.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "Next");
	nextButton.setLayoutData(arrowButtonGridData);

	createButtonListener(buttons, caller);

    }

    /**
     * Creates an instance of this class, sets the input and finally opens it.
     * 
     * @param parent
     *                the parent shell to which this window belongs
     * @param input
     *                the data object to be displayed in the window
     * 
     * @see #CompanyBookingWindow(Shell, AssociationTabControl)
     */
    public CompanyBookingWindow(Shell parent, CompanyBooking input,
	    final AssociationTabControl caller) {
	this(parent, caller);

	setInput(input);

	shell.open();
    }

    /**
     * Loads a given data record into the window form of initialize the elements
     * of the form the empty string/null if no data is given.
     * 
     * @param booking
     *                {@link CompanyBooking} instance which contains the data to
     *                be displayed or <tt>null</tt> if a completely new record
     *                is to be edited.
     */
    private void setInput(CompanyBooking booking) {
	/* STEP 1: switch off the listeners */
	settingInput = true;
	enrolledCalendar.setFocus();

	/* STEP 2: unlock the actually displayed data record (if exists) */
	if (this.input != null && inputEditingEnabled)
	    CompanyBooking.lockedIds.remove(new Integer(this.input.getId()));

	/*
	 * STEP 3: loading the given (new) instance into the window form (if not
	 * null else initialize the elements)
	 */
	if (booking != null) {
	    numberLabel.setEnabled(true);
	    numberText.setText(Integer.toString(booking.getId()));
	    enrolledCalendar.setDate(booking.getEnrolled());
	    confirmedCalendar.setDate(booking.getConfirmed());
	    billedCalendar.setDate(booking.getBilled());
	    signedOffCalendar.setDate(booking.getSignedOff());
	    messagedCalendar.setDate(booking.getMessaged());

	    companySelector.setSelectedId(booking.getCompanyId());
	    presentationSelector.setSelectedId(booking.getPresentationId());
	} else {
	    // no data given: initialize elements
	    numberLabel.setEnabled(false);
	    numberText.setText("");

	    enrolledCalendar.setDate(null);
	    confirmedCalendar.setDate(null);
	    billedCalendar.setDate(null);
	    signedOffCalendar.setDate(null);
	    messagedCalendar.setDate(null);

	    companySelector.setSelectedId(AbstractTable.NULL_ID);
	    presentationSelector.setSelectedId(AbstractTable.NULL_ID);
	}

	/*
	 * STEP 4: Check if the given data record is already locked, i.e. it is
	 * already edited (if the record is not null, else enable the elements). ->
	 * yes: disable the form elements -> no: enable them and lock the new
	 * record
	 */
	if (booking != null) {
	    boolean alreadyEditing = CompanyBooking.lockedIds
		    .contains(new Integer(booking.getId()));
	    setInputComponentsEnabled(!alreadyEditing);

	    if (!alreadyEditing) {
		CompanyBooking.lockedIds.add(new Integer(booking.getId()));
	    }
	}
	// completely new data record
	else
	    setInputComponentsEnabled(true);

	/*
	 * STEP 5: set input to the new data record, inputChanged to true and
	 * enable next/prev buttons
	 */
	this.input = booking;
	inputChanged = (booking == null);

	prevButton.setEnabled(this.input != null);
	nextButton.setEnabled(this.input != null);

	/* STEP 6: set the window title */
	String shellText;
	if (input == null)
	    shellText = Messages.getString("GUIText.NewItemWindowTitleText");
	else
	    shellText = "" + input.getId() + " - " + input.getCompanyId()
		    + ", " + input.getPresentationId();

	shellText += Messages
		.getString("GUIText.CompanyBookingWindowTitleText");
	if (input == null)
	    shellText += "*";
	shell.setText(shellText);

	/* STEP 7: switch on the listeners */
	settingInput = false;
    }

    /**
     * According to the value of the given parameter (<tt>true</tt>/<tt>false</tt>)
     * this method enables / disables the widgets of the window form.<br>
     * <br>
     * 
     * <i>Note:</i> It also sets the member {@link #inputEditingEnabled} to the
     * value of the parameter to signal that editing of the input has been
     * enabled or not.
     * 
     * @param enabled
     *                if equals <tt>true</tt> the widgets are enabled
     *                otherwise the widgets get disabled.
     */
    private void setInputComponentsEnabled(boolean enabled) {
	enrolledCalendar.setEnabled(enabled);
	confirmedCalendar.setEnabled(enabled);
	billedCalendar.setEnabled(enabled);
	signedOffCalendar.setEnabled(enabled);
	messagedCalendar.setEnabled(enabled);

	companySelector.setEnabled(enabled);
	presentationSelector.setEnabled(enabled);

	okButton.setEnabled(enabled);
	applyButton.setEnabled(enabled);
	newButton.setEnabled(enabled);
	abortButton.setEnabled(enabled);
	prevButton.setEnabled(enabled);
	nextButton.setEnabled(enabled);

	inputEditingEnabled = enabled;

    }

    /**
     * Adds a listener to several buttons of the window form.<br>
     * <br>
     * 
     * This involves at the moment the buttons {@link #okButton},
     * {@link #abortButton}, {@link #applyButton}, {@link #newButton},
     * {@link #prevButton}, {@link #nextButton}.
     */
    private void createButtonListener(Composite buttons,
	    final AssociationTabControl caller) {

	if (caller == null) { // normal mode, no caller

	    okButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event event) {
		    if (onSave())
			shell.close();
		}
	    });
	    applyButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event arg0) {
		    onSave();
		}
	    });
	} else {// caller mode

	    okButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event event) {
		    if (onSave()) {
			caller.addSelectedIds(new SimpleIDKey[] { input },
				false);
			shell.close();
		    }
		}
	    });
	    applyButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event arg0) {
		    if (onSave()) {
			caller.addSelectedIds(new SimpleIDKey[] { input },
				false);
		    }
		}
	    });
	}

	newButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		if (onSave())
		    setInput(null);
	    }
	});

	abortButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		shell.close();
	    }
	});

	prevButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		if (inputChanged) {
		    if (confirmClose())
			setInput(CompanyBooking.getPrevious(input.getId()));
		} else
		    setInput(CompanyBooking.getPrevious(input.getId()));
	    }
	});

	nextButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		if (inputChanged) {
		    if (confirmClose())
			setInput(CompanyBooking.getNext(input.getId()));
		} else
		    setInput(CompanyBooking.getNext(input.getId()));
	    }
	});

	messageButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		MessageDialog
			.openInformation(
				shell,
				Messages
					.getString("GUIText.NotImplementedMessageDialogWindowTitle"),
				Messages
					.getString("GUIText.NotImplementedMessageDialogHintText"));
	    }
	});

	billButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		MessageDialog
			.openInformation(
				shell,
				Messages
					.getString("GUIText.NotImplementedMessageDialogWindowTitle"),
				Messages
					.getString("GUIText.NotImplementedMessageDialogHintText"));
	    }
	});

	repaymentButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		MessageDialog
			.openInformation(
				shell,
				Messages
					.getString("GUIText.NotImplementedMessageDialogWindowTitle"),
				Messages
					.getString("GUIText.NotImplementedMessageDialogHintText"));
	    }
	});

	paymentBehaviorButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		MessageDialog
			.openInformation(
				shell,
				Messages
					.getString("GUIText.NotImplementedMessageDialogWindowTitle"),
				Messages
					.getString("GUIText.NotImplementedMessageDialogHintText"));
	    }
	});
    }

    /**
     * Checks if all obligatory attributes of a data record are entered into the
     * window form.
     * 
     * @return <tt>true</tt> if all mandatory fields are set and the fee
     *         values are valid float numbers (if entered), otherwise
     *         <tt>false</tt>.
     * 
     */
    protected boolean checkInput() {
	boolean inputOK = true;
	String errorMessage = "";
	if (companySelector.getSelectedId() == AbstractTable.NULL_ID) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryAssociationCustomerText");
	    inputOK = false;
	}
	if (presentationSelector.getSelectedId() == AbstractTable.NULL_ID) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryAssociationCompanyInternalPresentationText");
	    inputOK = false;
	}

	if (!inputOK) {
	    MessageDialog
		    .openInformation(
			    shell,
			    Messages
				    .getString("GUIText.ErrorMessageDialogMandatoryFieldWindowTitle"),
			    errorMessage);
	}

	return inputOK;
    }

    /**
     * Reads the data from the window form elements, sets the values to a
     * {@link CompanyBooking} instance and commits the changes / the new record
     * into the database.
     * 
     * Furthermore the member {@link #input} is set to the new values.
     * 
     */
    protected void commitInputToDB() {

	if (input == null) {
	    // new record into the db
	    try {

		CompanyBooking newInput = new CompanyBooking(
			CompanyBooking.NULL_ID, enrolledCalendar.getDate(),
			confirmedCalendar.getDate(), billedCalendar.getDate(),
			signedOffCalendar.getDate(),
			messagedCalendar.getDate(), presentationSelector
				.getSelectedId(), companySelector
				.getSelectedId(), new Timestamp(System
				.currentTimeMillis()), new Timestamp(System
				.currentTimeMillis()));
		input = newInput;

		input.insertIntoDB();
	    } catch (SQLException e) {
		e.printStackTrace();
	    }
	} else {
	 // update an existing record

	    input.setEnrolled(enrolledCalendar.getDate());
	    input.setConfirmed(confirmedCalendar.getDate());
	    input.setBilled(billedCalendar.getDate());
	    input.setSignedOff(signedOffCalendar.getDate());
	    input.setMessaged(messagedCalendar.getDate());
	    input.setPresentationId(presentationSelector.getSelectedId());
	    input.setCompanyId(companySelector.getSelectedId());

	    try {
		input.updateDB();
	    } catch (SQLException e) {
		e.printStackTrace();
	    }
	}

	CompanyBookingListWindow.instance.update();
    }

    /**
     * Creates a message dialog if the data in the window form has been changed
     * (or was entered newly) and these changes haven't been confirmed (e.g. by
     * clicking the ok button) or discarded so far. It also receives the
     * decision of the user (confirm/discard changes or abort the window
     * closing) and handels it.
     * 
     * In the positive case (confirmation of the changes) it invokes the method
     * {@link #onSave()}.
     * 
     * @return <tt>true</tt> if the shell can be closed and <tt>false</tt>
     *         otherwise.
     */
    private boolean confirmClose() {

	// has no visual representation until .open() is called
	MessageDialog dlg;

	if (input == null) {
	    dlg = new MessageDialog(
		    shell,
		    Messages
			    .getString("GUIText.SaveNewItemMessageDialogWindowTitle"), //$NON-NLS-1$
		    null,
		    Messages
			    .getString("GUIText.SaveNewItemMessageDialogQuestionText"), //$NON-NLS-1$
		    MessageDialog.QUESTION,
		    new String[] {
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogYesButtonText"), Messages.getString("GUIText.SaveNewItemMessageDialogNoButtonText"), //$NON-NLS-1$ //$NON-NLS-2$
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogAbortButtonText") }, 0); //$NON-NLS-1$
	} else {
	    dlg = new MessageDialog(
		    shell,
		    Messages
			    .getString("GUIText.SaveChangesMessageDialogWindowTitle"), //$NON-NLS-1$
		    null,
		    Messages
			    .getString("GUIText.SaveChangesMessageDialogQuestionText"), //$NON-NLS-1$
		    MessageDialog.QUESTION,
		    new String[] {
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogYesButtonText"), Messages.getString("GUIText.SaveChangesMessageDialogNoButtonText"), //$NON-NLS-1$ //$NON-NLS-2$
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogAbortButtonText") }, 0); //$NON-NLS-1$
	}
	switch (dlg.open()) {
	case 0:
	    return onSave();
	case 1:
	    return true;
	case 2:
	    return false;
	default:
	    return true;
	}
    }

    /**
     * Checks whether the input has been changed (i.e. inputChanged equals
     * <tt>true</tt>) or not. If changes have been done, it firstly checks if
     * the changes are valid ({@link #checkInput()}), secondly invokes the
     * method {@link #commitInputToDB()} to commit the changes and thirdly
     * updates the GUI by invoking the method {@link #setInput(CompanyBooking)}.
     * 
     * @return <tt>true</tt> if nothing has changed or the commit of the
     *         changes to the database was successful, <tt>false</tt>
     *         otherwise.
     */
    private boolean onSave() {
	if (!inputChanged)
	    return true;
	if (checkInput()) {
	    commitInputToDB();
	    setInput(input);
	    return true;
	} else
	    return false;
    }
}
