/**
 * Copyright (C) 2019 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.planimod.ui.views.nonavailabilities;

import static com.planimod.ui.Localized._;

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.databinding.wizard.WizardPageSupport;
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.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";

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

    /**
     * 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(_("Entrez la date de début et la date de fin."));
                }
                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(_("Sélectionnez une date de début et une date de fin pour l'impression.")));
        /*
         * 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(_("Imprimer calendrier d'absence"));
        setTitleImage(getTitleImage());
        // Title
        Label label = new Label(composite, SWT.NONE);
        label.setText(_("Titre :"));
        this.titleText = new Text(composite, SWT.BORDER);
        this.titleText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        this.titleText.setToolTipText(_("Titre du rapport. P.ex.: Calendrier de vacances."));
        // 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(_("au"));
        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(_("Filtre :"));
        this.filter = new Text(composite, SWT.BORDER);
        this.filter.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        this.filter.setToolTipText(_("Filtre :"));
        // 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 "";
        }
        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 "";
        }
        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);
    }
}
