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.Combo;
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.TabFolder;
import org.eclipse.swt.widgets.Text;

import semorg.gui.list.AssociateListWindow;
import semorg.gui.util.CalendarControl;
import semorg.gui.util.Messages;
import semorg.gui.util.CalendarControl.SWTCalendarEvent;
import semorg.gui.util.CalendarControl.SWTCalendarListener;
import semorg.sql.tables.Associate;
import semorg.sql.tables.Enumeration;
import semorg.sql.util.Utility;

/** The window for editing new or existing associate records. */
public class AssociateWindow {

    /**
     * Inner class which provides a listener reacting on changes of the input in
     * the lecturer 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 semorg.gui.AssociateWindow#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 associate record. If a new record
     * is entered it is set to <tt>null</tt> until the changes have been
     * committed.
     */
    private Associate 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 Combo salutationCombo;

    private final Combo titleCombo;

    private final Text firstNameText;

    private final Text nameText;

    private final Text streetText;

    private final Text zipCodeText;

    private final Text cityText;

    private final Combo countryCombo;

    private final Combo annexCombo;

    private final Text phoneText;

    private final Text mobileText;

    private final Text faxText;

    private final Text emailText;

    private final CalendarControl birthDayCalendar;

    private final CalendarControl firstContactCalendar;

    private final Button[] entitlementButtons;

    private final Text passwordText;

    private final Text jobText;

    private final Text shortMessageText;

    private final Text memoText;

    private final Button okButton;

    private final Button applyButton;

    private final Button newButton;

    private final Button abortButton;

    /** The triangle-button with the apex to the left. */
    private final Button prevButton;

    /** The triangle-button with the apex to the right. */
    private final Button nextButton;

    private final Button addressLabelButton;

    /**
     * Creates a AssociateWindow 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 associate
     *                window.
     */
    private AssociateWindow(Shell parent) {
	// corresponds to a function createContents()
	shell = new Shell(parent, SWT.CLOSE);
	shell.setSize(850, 560);
	shell.setLayout(new GridLayout(1, false));
	shell.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "AssociateWindow");

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

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

	final Group mainForm = new Group(shell, SWT.NULL);
	mainForm.setLayoutData(mgd);
	mainForm.setLayout(new GridLayout(2, false));

	final TabFolder tabs = new TabFolder(shell, SWT.FLAT);
	tabs.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "Tabs");
	GridData tgd = new GridData();
	tgd.heightHint = 130;
	tgd.horizontalAlignment = GridData.FILL;
	tgd.grabExcessHorizontalSpace = true;
	tabs.setLayoutData(tgd);

	final Composite leftColumn = new Composite(mainForm, SWT.NULL);
	leftColumn.setLayout(new GridLayout(1, false));
	GridData lcgd = new GridData();
	lcgd.widthHint = 410;
	lcgd.horizontalAlignment = GridData.FILL;
	lcgd.grabExcessHorizontalSpace = true;
	lcgd.verticalAlignment = GridData.BEGINNING;
	leftColumn.setLayoutData(lcgd);

	final Composite rightColumn = new Composite(mainForm, SWT.NULL);
	rightColumn.setLayout(new GridLayout(1, false));
	GridData rightColumnGridData = new GridData();
	rightColumnGridData.horizontalAlignment = GridData.FILL;
	rightColumnGridData.grabExcessHorizontalSpace = true;
	rightColumn.setLayoutData(rightColumnGridData);

	Composite numberComp = new Composite(leftColumn, SWT.NULL);
	numberComp.setLayout(new GridLayout(2, false));

	numberLabel = new Label(numberComp, SWT.READ_ONLY);
	numberLabel.setText(Messages.getString("GUIText.NumberText"));
	numberLabel.setAlignment(SWT.RIGHT);
	GridData numberLabelGridData = new GridData();
	numberLabelGridData.widthHint = 58;
	numberLabel.setLayoutData(numberLabelGridData);

	numberText = new Text(numberComp, 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 Group nameField = new Group(leftColumn, SWT.NULL);
	nameField.setText(Messages.getString("GUIText.NameFieldText"));
	nameField.setLayout(new GridLayout(2, false));
	GridData nameFieldGridData = new GridData();
	nameFieldGridData.horizontalAlignment = GridData.FILL;
	nameFieldGridData.grabExcessHorizontalSpace = true;
	nameField.setLayoutData(nameFieldGridData);

	final Group contactField = new Group(rightColumn, SWT.NULL);
	contactField.setText(Messages.getString("GUIText.ContactFieldText"));
	GridLayout contactGridLayout = new GridLayout(2, false);
	contactGridLayout.marginHeight = 11;
	contactField.setLayout(contactGridLayout);
	GridData contactFieldGridData = new GridData();
	contactFieldGridData.horizontalAlignment = GridData.FILL;
	contactFieldGridData.grabExcessHorizontalSpace = true;
	contactField.setLayoutData(contactFieldGridData);

	final Label salutationLabel = new Label(nameField, SWT.READ_ONLY);
	salutationLabel.setText(Messages.getString("GUIText.SalutationText"));
	salutationLabel.setAlignment(SWT.RIGHT);
	GridData salutationLabelGridData = new GridData();
	salutationLabelGridData.widthHint = 55;
	salutationLabel.setLayoutData(salutationLabelGridData);

	Composite nameRowCompletion = new Composite(nameField, SWT.NULL);
	GridLayout rcgl = new GridLayout(3, false);
	rcgl.marginWidth = 0;
	rcgl.marginHeight = 0;
	nameRowCompletion.setLayout(rcgl);

	salutationCombo = new Combo(nameRowCompletion, SWT.DROP_DOWN
		| SWT.BORDER);
	salutationCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"SalutationCombo");
	Enumeration.addSalutationEnums(salutationCombo);
	salutationCombo.setBackground(Utility.LIGHT_YELLOW);
	salutationCombo.addFocusListener(Utility.checkEmptyListener);
	salutationCombo.setTextLimit(13);
	GridData scgd = new GridData();
	scgd.widthHint = 80;
	salutationCombo.setLayoutData(scgd);
	salutationCombo.setToolTipText(Messages
		.getString("GUIText.SalutationToolTip"));
	salutationCombo.addModifyListener(inputChangedListener);

	final Label titleLabel = new Label(nameRowCompletion, SWT.READ_ONLY);
	titleLabel.setText(Messages.getString("GUIText.TitelText"));

	titleCombo = new Combo(nameRowCompletion, SWT.DROP_DOWN | SWT.BORDER);
	titleCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "TitleCombo");
	Enumeration.addTitleEnums(titleCombo);
	Utility.setLength(titleCombo, 13);
	titleCombo.setToolTipText(Messages.getString("GUIText.TitelToolTip"));
	titleCombo.addModifyListener(inputChangedListener);

	final Label firstNameLabel = new Label(nameField, SWT.READ_ONLY);
	firstNameLabel.setText(Messages.getString("GUIText.FirstNameText"));
	Utility.alignRight(firstNameLabel);

	firstNameText = new Text(nameField, SWT.CHECK | SWT.SINGLE | SWT.BORDER);
	firstNameText
		.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "FirstNameText");
	Utility.setLength(firstNameText, 30);
	firstNameText.setToolTipText(Messages
		.getString("GUIText.FirstNameToolTip"));
	firstNameText.addModifyListener(inputChangedListener);

	final Label nameLabel = new Label(nameField, SWT.READ_ONLY);
	nameLabel.setText(Messages.getString("GUIText.LastNameText"));
	Utility.alignRight(nameLabel);

	nameText = new Text(nameField, SWT.SINGLE | SWT.BORDER);
	nameText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "NameText");
	Utility.setLength(nameText, 30);
	nameText.setToolTipText(Messages.getString("GUIText.LastNameToolTip"));
	nameText.setBackground(Utility.LIGHT_YELLOW);
	nameText.addFocusListener(Utility.checkEmptyListener);
	nameText.addModifyListener(inputChangedListener);

	final Group addressField = new Group(leftColumn, SWT.NULL);
	addressField.setText(Messages.getString("GUIText.AdressFieldText"));
	addressField.setLayout(new GridLayout(2, false));
	GridData addressFieldGridData = new GridData();
	addressFieldGridData.horizontalAlignment = GridData.FILL;
	addressFieldGridData.grabExcessHorizontalSpace = true;
	addressField.setLayoutData(addressFieldGridData);

	final Label streetLabel = new Label(addressField, SWT.READ_ONLY);
	streetLabel.setText(Messages.getString("GUIText.StreetText"));
	streetLabel.setAlignment(SWT.RIGHT);
	GridData streetLabelGridData = new GridData();
	streetLabelGridData.widthHint = 55;
	streetLabel.setLayoutData(streetLabelGridData);

	streetText = new Text(addressField, SWT.SINGLE | SWT.BORDER);
	streetText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "StreetText");
	Utility.setLength(streetText, 50);
	streetText.setToolTipText(Messages.getString("GUIText.StreetToolTip"));
	streetText.addModifyListener(inputChangedListener);

	final Label cityLabel = new Label(addressField, SWT.READ_ONLY);
	cityLabel.setText(Messages.getString("GUIText.CityZipCodeText"));
	Utility.alignRight(cityLabel);

	Composite cityRowCompletion = new Composite(addressField, SWT.NULL);
	cityRowCompletion.setLayout(rcgl);

	zipCodeText = new Text(cityRowCompletion, SWT.SINGLE | SWT.BORDER);
	zipCodeText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "ZipCodeText");
	Utility.setLength(zipCodeText, 5);
	zipCodeText
		.setToolTipText(Messages.getString("GUIText.ZipCodeToolTip"));
	zipCodeText.addModifyListener(inputChangedListener);

	cityText = new Text(cityRowCompletion, SWT.SINGLE | SWT.BORDER);
	cityText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "CityText");
	Utility.setLength(cityText, 30);
	cityText.setToolTipText(Messages.getString("GUIText.CityToolTip"));
	cityText.addModifyListener(inputChangedListener);

	final Label countryLabel = new Label(addressField, SWT.READ_ONLY);
	countryLabel.setText(Messages.getString("GUIText.CountryText"));
	Utility.alignRight(countryLabel);

	Composite countryRowCompletion = new Composite(addressField, SWT.NULL);
	countryRowCompletion.setLayout(rcgl);

	countryCombo = new Combo(countryRowCompletion, SWT.DROP_DOWN
		| SWT.BORDER);
	countryCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "CountryCombo");
	Enumeration.addCountryEnums(countryCombo);
	Utility.setLength(countryCombo, 13);
	countryCombo.addModifyListener(inputChangedListener);
	countryCombo.setToolTipText(Messages
		.getString("GUIText.CountryToolTip"));

	final Label annexLabel = new Label(countryRowCompletion, SWT.READ_ONLY);
	annexLabel.setText(Messages.getString("GUIText.AnnexText"));

	annexCombo = new Combo(countryRowCompletion, SWT.DROP_DOWN | SWT.BORDER);
	annexCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "AnnexCombo");
	Enumeration.addAnnexEnums(annexCombo);
	Utility.setLength(annexCombo, 13);
	annexCombo.setToolTipText(Messages.getString("GUIText.AnnexToolTip"));
	annexCombo.addModifyListener(inputChangedListener);

	final Label phoneLabel = new Label(contactField, SWT.READ_ONLY);
	phoneLabel.setText(Messages.getString("GUIText.PhoneText"));
	phoneLabel.setAlignment(SWT.RIGHT);
	GridData phoneLabelGridData = new GridData();
	phoneLabelGridData.widthHint = 97;
	phoneLabel.setLayoutData(phoneLabelGridData);

	phoneText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	phoneText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "PhoneText");
	Utility.setLength(phoneText, 20);
	phoneText.setToolTipText(Messages.getString("GUIText.PhoneToolTip"));
	phoneText.addModifyListener(inputChangedListener);

	final Label mobileLabel = new Label(contactField, SWT.READ_ONLY);
	mobileLabel.setText(Messages.getString("GUIText.MobileText"));
	Utility.alignRight(mobileLabel);

	mobileText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	mobileText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "MobileText");
	Utility.setLength(mobileText, 20);
	mobileText.setToolTipText(Messages.getString("GUIText.MobileToolTip"));
	mobileText.addModifyListener(inputChangedListener);

	final Label faxLabel = new Label(contactField, SWT.READ_ONLY);
	faxLabel.setText(Messages.getString("GUIText.FaxText"));
	Utility.alignRight(faxLabel);

	faxText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	faxText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "FaxText");
	Utility.setLength(faxText, 20);
	faxText.setToolTipText(Messages.getString("GUIText.FaxToolTip"));
	faxText.addModifyListener(inputChangedListener);

	final Label emailLabel = new Label(contactField, SWT.READ_ONLY);
	emailLabel.setText(Messages.getString("GUIText.EmailText"));
	Utility.alignRight(emailLabel);

	emailText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	emailText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "EmailText");
	Utility.setLength(emailText, 256, 20);
	emailText.setToolTipText(Messages.getString("GUIText.EmailToolTip"));
	emailText.addModifyListener(inputChangedListener);

	Composite additionalComponentsComp = new Composite(rightColumn,
		SWT.NULL);
	additionalComponentsComp.setLayout(new GridLayout(2, false));

	Label birthDayLabel = new Label(additionalComponentsComp, SWT.READ_ONLY);
	birthDayLabel.setText(Messages.getString("GUIText.BirthdayText"));
	birthDayLabel.setAlignment(SWT.RIGHT);
	GridData birthDayLabelGridData = new GridData();
	birthDayLabelGridData.widthHint = 100;
	birthDayLabel.setLayoutData(birthDayLabelGridData);

	birthDayCalendar = new CalendarControl(additionalComponentsComp,
		titleBarHeight);
	birthDayCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"BirthDayCalendar");
	birthDayCalendar.addSWTCalendarlistener(inputChangedListener);
	birthDayCalendar.setToolTipText(Messages
		.getString("GUIText.BirthdayToolTip"));

	Label firstContactLabel = new Label(additionalComponentsComp,
		SWT.READ_ONLY);
	firstContactLabel.setText(Messages
		.getString("GUIText.FirstContactText"));
	Utility.alignRight(firstContactLabel);

	firstContactCalendar = new CalendarControl(additionalComponentsComp,
		titleBarHeight);
	firstContactCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"FirstContactCalendar");
	firstContactCalendar.addSWTCalendarlistener(inputChangedListener);
	firstContactCalendar.setToolTipText(Messages
		.getString("GUIText.FirstContactToolTip"));

	Label authorizationLabel = new Label(additionalComponentsComp,
		SWT.READ_ONLY);
	authorizationLabel.setToolTipText(Messages
		.getString("GUIText.AuthorizationToolTip"));

	Utility.alignRight(authorizationLabel);
	authorizationLabel.setText(Messages
		.getString("GUIText.AuthorizationText"));

	Composite entitlementGroup = new Composite(additionalComponentsComp,
		SWT.NULL);
	entitlementGroup.setLayout(new GridLayout(2, false));

	entitlementButtons = new Button[4];
	for (int i = 0; i < entitlementButtons.length; i++) {
	    entitlementButtons[i] = new Button(entitlementGroup, SWT.RADIO);
	}

	entitlementButtons[0]
		.setText(Messages
			.getString("GUIText.EntitlementButtonsSeminarAdministratorText"));
	entitlementButtons[0].addSelectionListener(inputChangedListener);

	entitlementButtons[1]
		.setText(Messages
			.getString("GUIText.EntitlementButtonsCustomerAdministratorText"));
	entitlementButtons[1].addSelectionListener(inputChangedListener);

	entitlementButtons[2]
		.setText(Messages
			.getString("GUIText.EntitlementButtonsPresentatioOrganizerText"));
	entitlementButtons[2].addSelectionListener(inputChangedListener);

	entitlementButtons[3].setText(Messages
		.getString("GUIText.EntitlementButtonsAdministratorText"));
	entitlementButtons[3].addSelectionListener(inputChangedListener);

	Label passwordLabel = new Label(additionalComponentsComp, SWT.READ_ONLY);
	Utility.alignRight(passwordLabel);
	passwordLabel.setText(Messages.getString("GUIText.PasswordText"));

	passwordText = new Text(additionalComponentsComp, SWT.SINGLE
		| SWT.BORDER);
	passwordText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "PasswordText");
	Utility.setLength(passwordText, 6);
	passwordText.setToolTipText(Messages
		.getString("GUIText.PasswordToolTip"));

	Label jobLabel = new Label(additionalComponentsComp, SWT.READ_ONLY);
	Utility.alignRight(jobLabel);
	jobLabel.setText(Messages.getString("GUIText.JobText"));

	jobText = new Text(additionalComponentsComp, SWT.SINGLE | SWT.BORDER);
	jobText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "JobText");
	Utility.setLength(jobText, 30);
	jobText.setToolTipText(Messages.getString("GUIText.JobToolTip"));

	shortMessageText = Utility.createTextTab(tabs, Messages
		.getString("GUIText.ShortMessageText"), null, 200);
	shortMessageText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"ShortMessageText");
	shortMessageText.addModifyListener(inputChangedListener);
	memoText = Utility.createTextTab(tabs, Messages
		.getString("GUIText.MemoText"), null, 200);
	memoText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "MemoText");
	memoText.addModifyListener(inputChangedListener);

	final Composite buttons = new Composite(shell, shell.getStyle());
	buttons.setLayout(new GridLayout(7, false));

	okButton = new Button(buttons, SWT.PUSH);
	okButton.setText(Messages.getString("GUIText.OkButtonText"));
	okButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	applyButton = new Button(buttons, SWT.PUSH);
	applyButton.setText(Messages.getString("GUIText.ApplyButtonText"));
	applyButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	newButton = new Button(buttons, SWT.PUSH);
	newButton.setText(Messages.getString("GUIText.NewButtonText"));
	newButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	abortButton = new Button(buttons, SWT.PUSH);
	abortButton.setText(Messages.getString("GUIText.AbortButtonText"));
	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);

	addressLabelButton = new Button(buttons, SWT.PUSH);
	addressLabelButton.setText(Messages
		.getString("GUIText.AddressButtonText"));
	addressLabelButton.setEnabled(true);
	addressLabelButton.setLayoutData(new GridData(
		GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));

	createButtonListener();
    }

    /**
     * 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 lecturer data to be displayed in the window
     */
    public AssociateWindow(Shell parent, Associate input) {
	this(parent);

	setInput(input);

	shell.open();
    }

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

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

	/*
	 * STEP 3: loading the given (new) lecturer instance into the window
	 * form (if not null else initialize the elements)
	 */
	if (associate != null) {
	    salutationCombo.setFocus();
	    numberLabel.setEnabled(true);
	    numberText.setEnabled(true);
	    numberText.setText(new Integer(associate.getId()).toString());
	    if (associate.getSalutation() != null)
		salutationCombo.setText(associate.getSalutation());
	    else
		salutationCombo.setText("");
	    if (associate.getTitle() != null)
		titleCombo.setText(associate.getTitle());
	    else
		titleCombo.setText("");
	    if (associate.getFirstname() != null)
		firstNameText.setText(associate.getFirstname());
	    else
		firstNameText.setText("");
	    if (associate.getName() != null)
		nameText.setText(associate.getName());
	    else
		nameText.setText("");
	    if (associate.getStreet() != null)
		streetText.setText(associate.getStreet());
	    else
		streetText.setText("");
	    if (associate.getZipCode() != null)
		zipCodeText.setText(associate.getZipCode());
	    else
		zipCodeText.setText("");
	    if (associate.getCity() != null)
		cityText.setText(associate.getCity());
	    else
		cityText.setText("");
	    if (associate.getCountry() != null)
		countryCombo.setText(associate.getCountry());
	    else
		countryCombo.setText("");
	    if (associate.getAnnex() != null)
		annexCombo.setText(associate.getAnnex());
	    else
		annexCombo.setText("");
	    if (associate.getPhone() != null)
		phoneText.setText(associate.getPhone());
	    else
		phoneText.setText("");
	    if (associate.getMobile() != null)
		mobileText.setText(associate.getMobile());
	    else
		mobileText.setText("");
	    if (associate.getFax() != null)
		faxText.setText(associate.getFax());
	    else
		faxText.setText("");
	    if (associate.getEmail() != null)
		emailText.setText(associate.getEmail());
	    else
		emailText.setText("");

	    birthDayCalendar.setDate(associate.getBirthday());
	    firstContactCalendar.setDate(associate.getFirstContact());

	    if (associate.getEntitlement() != 0)
		for (int i = 0; i < entitlementButtons.length; i++)
		    entitlementButtons[i].setSelection(associate
			    .getEntitlement() - 1 == i);

	    if (associate.getPassword() != null)
		passwordText.setText(associate.getPassword());
	    else
		passwordText.setText("");
	    if (associate.getOccupation() != null)
		jobText.setText(associate.getOccupation());
	    else
		jobText.setText("");
	    if (associate.getShortinfo() != null)
		shortMessageText.setText(associate.getShortinfo());
	    else
		shortMessageText.setText("");
	    if (associate.getNotices() != null)
		memoText.setText(associate.getNotices());
	    else
		memoText.setText("");
	} else {
	    // no data given (lecturer == null): initialize elements
	    numberLabel.setEnabled(false);
	    numberText.setEnabled(false);
	    numberText.setText("");
	    salutationCombo.setFocus();
	    salutationCombo.setText("");
	    titleCombo.setText("");
	    firstNameText.setText("");
	    nameText.setText("");
	    streetText.setText("");
	    zipCodeText.setText("");
	    cityText.setText("");
	    countryCombo.setText("");
	    annexCombo.setText("");
	    phoneText.setText("");
	    mobileText.setText("");
	    faxText.setText("");
	    emailText.setText("");
	    birthDayCalendar.setDate(null);
	    firstContactCalendar.setDate(null);
	    for (int i = 0; i < entitlementButtons.length; i++) {
		entitlementButtons[i].setSelection(false);
	    }
	    passwordText.setText("");
	    jobText.setText("");
	    shortMessageText.setText("");
	    memoText.setText("");

	}

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

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

	/*
	 * STEP 5: set input to the new lecturer, inputChanged to true and
	 * enable next/prev buttons (if lecturer != null else...)
	 */
	this.input = associate;
	inputChanged = (associate == 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.getSalutation() + ", " //$NON-NLS-2$
		    + input.getName() + ", "
		    + entitlementButtons[input.getEntitlement() - 1].getText();

	shellText += Messages.getString("GUIText.AssociateWindowTitleText");
	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) {
	numberText.setEnabled(enabled);
	salutationCombo.setEnabled(enabled);
	titleCombo.setEnabled(enabled);
	firstNameText.setEnabled(enabled);
	nameText.setEnabled(enabled);
	streetText.setEnabled(enabled);
	zipCodeText.setEnabled(enabled);
	cityText.setEnabled(enabled);
	countryCombo.setEnabled(enabled);
	annexCombo.setEnabled(enabled);
	phoneText.setEnabled(enabled);
	mobileText.setEnabled(enabled);
	faxText.setEnabled(enabled);
	emailText.setEnabled(enabled);
	birthDayCalendar.setEnabled(enabled);
	firstContactCalendar.setEnabled(enabled);
	for (int i = 0; i < entitlementButtons.length; i++) {
	    entitlementButtons[i].setEnabled(enabled);
	}
	passwordText.setEnabled(enabled);
	jobText.setEnabled(enabled);
	shortMessageText.setEnabled(enabled);
	memoText.setEnabled(enabled);
	okButton.setEnabled(enabled);
	applyButton.setEnabled(enabled);
	newButton.setEnabled(enabled);
	abortButton.setEnabled(enabled);
	prevButton.setEnabled(enabled);
	nextButton.setEnabled(enabled);
	// addressLabelButton.setEnabled(enabled);

	inputEditingEnabled = enabled;

    }

    /** Returns the number of selected entitlements (radio) buttons. */
    private int getEntitlementValue() {
	int val = 0;
	for (int i = 0; i < entitlementButtons.length; i++) {
	    if (entitlementButtons[i].getSelection() == true)
		val = i + 1;
	}
	return val;
    }

    /**
     * Checks if all obligatory attributes (salutation, lastname, entitlement)
     * of a associate record are entered into the window form.
     * 
     * @return <tt>true</tt> if all mandatory fields are set, otherwise
     *         <tt>false</tt>.
     */
    protected boolean checkInput() {
	boolean inputOK = true;
	String errorMessage = "";
	if (salutationCombo.getText().equals("")) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryFieldSalutationText");
	    inputOK = false;
	}
	if (nameText.getText().equals("")) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryFieldLastNameText");
	    inputOK = false;
	}
	if (getEntitlementValue() == 0) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryFieldEntitlementText");
	    inputOK = false;
	}

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

	return inputOK;
    }

    /**
     * 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() {

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

	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(Associate.getPrevious(input.getId()));
		} else
		    setInput(Associate.getPrevious(input.getId()));
	    }
	});

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

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

    /**
     * Reads the data from the window form elements, sets the values to a
     * Lecturer 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
	    int entitlement = getEntitlementValue();

	    Associate newInput = new Associate(Associate.NULL_ID,
		    salutationCombo.getText(), titleCombo.getText(),
		    firstNameText.getText(), nameText.getText(), streetText
			    .getText(), zipCodeText.getText(), cityText
			    .getText(), countryCombo.getText(), annexCombo
			    .getText(), phoneText.getText(), mobileText
			    .getText(), faxText.getText(), emailText.getText(),
		    birthDayCalendar.getDate(), firstContactCalendar.getDate(),
		    entitlement, passwordText.getText(), jobText.getText(),
		    shortMessageText.getText(), memoText.getText(),
		    new Timestamp(System.currentTimeMillis()), new Timestamp(
			    System.currentTimeMillis()));
	    input = newInput;
	    try {
		int id = input.insertIntoDB();
		numberText.setText(new Integer(id).toString());
	    } catch (SQLException e) {
		e.printStackTrace();
	    }
	} else {
	    // update an existing record
	    input.setSalutation(salutationCombo.getText());
	    input.setTitle(titleCombo.getText());
	    input.setFirstname(firstNameText.getText());
	    input.setName(nameText.getText());
	    input.setStreet(streetText.getText());
	    input.setZipCode(zipCodeText.getText());
	    input.setCity(cityText.getText());
	    input.setCountry(countryCombo.getText());
	    input.setAnnex(annexCombo.getText());
	    input.setPhone(phoneText.getText());
	    input.setMobile(mobileText.getText());
	    input.setFax(faxText.getText());
	    input.setEmail(emailText.getText());
	    input.setBirthday(birthDayCalendar.getDate());
	    input.setFirstContact(firstContactCalendar.getDate());
	    input.setEntitlement(getEntitlementValue());
	    input.setPassword(passwordText.getText());
	    input.setOccupation(jobText.getText());
	    input.setShortinfo(shortMessageText.getText());
	    input.setNotices(memoText.getText());

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

	AssociateListWindow.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"),
		    null,
		    Messages
			    .getString("GUIText.SaveNewItemMessageDialogQuestionText"),
		    MessageDialog.QUESTION,
		    new String[] {
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogYesButtonText"), Messages.getString("GUIText.SaveNewItemMessageDialogNoButtonText"), //$NON-NLS-2$
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogAbortButtonText") },
		    0);
	} else {
	    dlg = new MessageDialog(
		    shell,
		    Messages
			    .getString("GUIText.SaveChangesMessageDialogWindowTitle"),
		    null,
		    Messages
			    .getString("GUIText.SaveChangesMessageDialogQuestionText"),
		    MessageDialog.QUESTION,
		    new String[] {
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogYesButtonText"), Messages.getString("GUIText.SaveChangesMessageDialogNoButtonText"), //$NON-NLS-2$
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogAbortButtonText") },
		    0);
	}
	switch (dlg.open()) {
	case 0: // "yes" selected
	    return onSave();
	case 1: // "no" selected -> close immediatly
	    return true;
	case 2: // "abort" selected -> don't close the shell
	    return false;
	default: // "x" button clicked -> close immediatly
	    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(Associate)}.
     * 
     * @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;
    }
}
