/*
 * Copyright (c) 2011, Patrik Dufresne. All rights reserved. Patrik Dufresne
 * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.planimod.ui.views.nonavailabilities;

import java.util.Calendar;
import java.util.Date;
import java.util.StringTokenizer;
import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.conversion.Converter;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.databinding.validation.MultiValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.dialog.TitleAreaDialogSupport;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import com.patrikdufresne.jface.databinding.datetime.DateTimeSupport;
import com.patrikdufresne.jface.databinding.preference.PreferenceStoreProperties;
import com.patrikdufresne.jface.dialogs.DefaultValidationMessageProvider;
import com.patrikdufresne.jface.resource.OverlayImageDescriptor;
import com.patrikdufresne.util.DateFormatRegistry;
import com.patrikdufresne.util.Localized;
import com.planimod.core.TimeRanges;
import com.planimod.ui.theme.Resources;

/**
 * This dialog implementation is used to let the user select the printing time
 * range for the non-availability print factory.
 * <p>
 * Notice, the time range provided by this dialog doesn't matches the expected
 * time range of the print factory. This dialog, allow the user to select the
 * months to be printed. The method {@link #getStart()} return the starting
 * month to be printed inclusively. The method {@link #getEnd()} return the
 * ending month to be printed inclusively. To print a single month, this class
 * will then return the same date value as starting and ending date.
 * 
 * @author Patrik Dufresne
 */
public class NonAvailabilityPrintDialog extends TitleAreaDialog {

    /**
     * Resource id for title image.
     */
    private static final String TITLE_IMAGE = "NonAvailabilityPrintDialog.titleImage"; //$NON-NLS-1$

    /**
     * Return the title image
     * 
     * @return
     */
    private static Image getTitleImage() {
        Image image = JFaceResources.getImage(TITLE_IMAGE);
        if (image == null) {
            OverlayImageDescriptor icon =
                    new OverlayImageDescriptor(JFaceResources.getImageRegistry()
                            .getDescriptor(DLG_IMG_TITLE_BANNER),
                            Resources.getImageDescriptor(Resources.ICON_PRINT_48),
                            SWT.LEFT, SWT.CENTER);
            JFaceResources.getImageRegistry().put(TITLE_IMAGE, icon);
            image = JFaceResources.getImage(TITLE_IMAGE);
        }
        return image;
    }

    /**
     * Databinding
     */
    private DataBindingContext dbc;

    /**
     * Widget used to select the ending date
     */
    private Combo endDate;

    /**
     * Hold the ending date value.
     */
    WritableValue endDateValue = new WritableValue(null, Date.class);

    /**
     * Widget to enter filters.
     */
    private Text filter;

    /**
     * Observable filter value.
     */
    WritableValue filterValue = new WritableValue(null, String.class);

    /**
     * Used for localization.
     */
    Localized localized = Localized.load(NonAvailabilityPrintDialog.class);

    /**
     * Widget used to select the starting date.
     */
    private Combo startDate;

    /**
     * Hold the start date value.
     */
    WritableValue startDateValue = new WritableValue(null, Date.class);

    private Text titleText;

    WritableValue titleValue = new WritableValue(null, String.class);

    /**
     * Create a new dialog.
     * 
     * @param parentShell the parent shell.
     */
    public NonAvailabilityPrintDialog(Shell parentShell) {
        super(parentShell);
    }

    /**
     * Sets the binding between the values and the widgets.
     */
    protected void bindValues() {
        this.dbc = new DataBindingContext();
        /*
         * Bind title
         */
        this.dbc.bindValue(
                WidgetProperties.text(SWT.FocusOut).observe(this.titleText),
                this.titleValue);
        /*
         * Bind the time range
         */
        Calendar cal = Calendar.getInstance();
        cal.setTime(TimeRanges.getMonthStart(new Date()));
        cal.add(Calendar.MONTH, -12);
        Date from = cal.getTime();
        cal.add(Calendar.MONTH, 24);
        Date to = cal.getTime();
        DateTimeSupport.create(this.startDate, this.dbc, this.startDateValue,
                DateFormatRegistry.getFormat(DateFormatRegistry.MONTH, true), from,
                to, Calendar.MONTH, 1);
        DateTimeSupport.create(this.endDate, this.dbc, this.endDateValue,
                DateFormatRegistry.getFormat(DateFormatRegistry.MONTH, true), from,
                to, Calendar.MONTH, 1);
        // Create the multi-validator for start and end time to check the start
        // value is before the end value.
        MultiValidator validator = new MultiValidator() {

            @Override
            protected IStatus validate() {
                // Calculate the validation status
                Date start =
                        (Date) NonAvailabilityPrintDialog.this.startDateValue
                                .getValue();
                Date end =
                        (Date) NonAvailabilityPrintDialog.this.endDateValue.getValue();
                if (start == null || end == null || start.compareTo(end) > 0) {
                    return ValidationStatus
                            .error(NonAvailabilityPrintDialog.this.localized
                                    .get("NonAvailabilityPrintDialog.message.selectTimeRange")); //$NON-NLS-1$
                }
                return ValidationStatus.ok();
            }
        };
        this.dbc.addValidationStatusProvider(validator);
        /*
         * Bind the filter.
         */
        this.dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(this.filter),
                this.filterValue);
        /*
         * Bind the title area to the binding context
         */
        TitleAreaDialogSupport.create(this, this.dbc).setValidationMessageProvider(
                new DefaultValidationMessageProvider(this.localized
                        .get("NonAvailabilityPrintDialog.titleArea.message"))); //$NON-NLS-1$
        /*
         * Bind the binding status to the OK button
         */
        UpdateValueStrategy modelToTarget = new UpdateValueStrategy();
        modelToTarget.setConverter(new Converter(IStatus.class, Boolean.TYPE) {

            @Override
            public Object convert(Object fromObject) {
                return Boolean.valueOf(((IStatus) fromObject).getSeverity() == IStatus.OK);
            }
        });
        UpdateValueStrategy targetToModel =
                new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER);
        this.dbc.bindValue(SWTObservables.observeEnabled(this.getButton(OK)),
                new AggregateValidationStatus(this.dbc.getBindings(),
                        AggregateValidationStatus.MAX_SEVERITY), targetToModel,
                modelToTarget);
    }

    /**
     * This implementation sets the binding.
     */
    @Override
    protected Control createContents(Composite parent) {
        Control control = super.createContents(parent);
        bindValues();
        return control;
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite comp = (Composite) super.createDialogArea(parent);
        Composite composite = new Composite(comp, SWT.NONE);
        composite.setLayout(new GridLayout(1, false));
        composite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
        // Setup the title area
        setTitle(this.localized.get("NonAvailabilityPrintDialog.titleArea.title")); //$NON-NLS-1$
        setTitleImage(getTitleImage());
        // Title
        Label label = new Label(composite, SWT.NONE);
        label.setText(this.localized.get("NonAvailabilityPrintDialog.label.title")); //$NON-NLS-1$
        this.titleText = new Text(composite, SWT.BORDER);
        this.titleText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        this.titleText.setToolTipText(this.localized
                .get("NonAvailabilityPrintAction.title.tooltipText")); //$NON-NLS-1$
        // Start date To End date
        Composite compDates = new Composite(composite, SWT.NONE);
        GridLayout layout = new GridLayout(3, false);
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        layout.verticalSpacing = 0;
        compDates.setLayout(layout);
        this.startDate = new Combo(compDates, SWT.DROP_DOWN | SWT.BORDER);
        this.startDate.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
        label = new Label(compDates, SWT.NONE);
        label.setText(this.localized
                .get("NonAvailabilityPrintDialog.label.timeRange.to")); //$NON-NLS-1$
        this.endDate = new Combo(compDates, SWT.DROP_DOWN | SWT.BORDER);
        this.endDate.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
        // Filter widget
        label = new Label(composite, SWT.NONE);
        label.setText(this.localized.get("NonAvailabilityPrintDialog.label.filter")); //$NON-NLS-1$
        this.filter = new Text(composite, SWT.BORDER);
        this.filter.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        this.filter.setToolTipText(this.localized
                .get("NonAvailabilityPrintAction.filter.tooltipText")); //$NON-NLS-1$
        // Build the separator line
        Label separator = new Label(comp, SWT.HORIZONTAL | SWT.SEPARATOR);
        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        return comp;
    }

    /**
     * Return the ending month (inclusive) or null if not set.
     * 
     * @return
     */
    public Date getEnd() {
        if (this.endDateValue.getValue() instanceof Date) {
            return (Date) this.endDateValue.getValue();
        }
        return null;
    }

    public String getFilter() {
        if (!(this.filterValue.getValue() instanceof String)) {
            return ""; //$NON-NLS-1$
        }
        return (String) this.filterValue.getValue();
    }

    /**
     * Return the starting month (inclusive) or null if not set.
     * 
     * @return
     */
    public Date getStart() {
        if (this.startDateValue.getValue() instanceof Date) {
            return (Date) this.startDateValue.getValue();
        }
        return null;
    }

    /**
     * Return the print title value.
     * 
     * @return
     */
    public String getPrintTitle() {
        if (!(this.titleValue.getValue() instanceof String)) {
            return ""; //$NON-NLS-1$
        }
        return (String) this.titleValue.getValue();
    }

    /**
     * Sets the ending month (inclusive).
     * 
     * @param date the new date value or null to unset.
     */
    public void setEnd(Date date) {
        this.endDateValue.setValue(date);
    }

    /**
     * Set the starting month (inclusive).
     * 
     * @param date the new date value or null to unset.
     */
    public void setStart(Date date) {
        this.startDateValue.setValue(date);
    }

    /**
     * Sets the print title value.
     */
    public void setPrintTitle(String title) {
        this.titleValue.setValue(title);
    }

    /**
     * Sets the filter value.
     * 
     * @param filter
     */
    public void setFilter(String filter) {
        this.filterValue.setValue(filter);
    }
}
