/*
 *
 *  Copyright (C) 2023 Andrew Gegg
 *
 * 	This file is part of the Gardeners Notebook application
 *
 *  The Gardeners Notebook application is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/gpl.html>.
 *
 *
 */

package uk.co.gardennotebook;

import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextField;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;

/**
 * A version of DatePicker which guards against short form date bug JDK-8269867, checks for valid dates (range checks) and enforces mandatory entry.
 * This component is for use at 'editor' level (i.e. stand-alone Date fields) and not, e.g. in comment tables.
 *
 * @implNote
 * The class and constructors MUST be {@code public} or FXML won't load it.
 *
 * Tab out bug: JDK-8303478
 *
 * @author Andy Gegg
 *	@version	3.1.0
 *	@since	3.1.0
 */
final public class SafeDatePicker extends DatePicker
{
    private static final Logger LOGGER = LogManager.getLogger();

    private boolean isMandatory = false;

    //  inclusive min and max allowed dates.
    private LocalDate minValidDate = LocalDate.MIN;
    private LocalDate maxValidDate = LocalDate.MAX;

    public SafeDatePicker()
    {
        this(null);
    }

    public SafeDatePicker(LocalDate localDate)
    {
        super(localDate);
//        LOGGER.traceEntry("constructor");
        setOnAction(ev -> {
//            LOGGER.debug("in onAction");
            checkValue();
        } );
        setDayCellFactory(dpk -> {
                return new DateCell()
                {
                    @Override
                    public void updateItem(LocalDate item, boolean empty)
                    {
                        super.updateItem(item, empty);
                        if (empty || item == null)
                        {
                            setText(null);
                            setGraphic(null);
                        }
                        else if (item.isBefore(minValidDate) || item.isAfter(maxValidDate))
                        {
                            setDisable(true);
                        }
                    }
                };
        });
        TextField editor = getEditor();
//        LOGGER.debug("editor is: {}", editor);
        editor.focusedProperty().addListener((obj, wasFocused, isFocused)->{
            if (wasFocused && !isFocused)
            {
//                LOGGER.debug("in lostFocus");
                checkValue();
            }
        });
    }

    /**
     * This field is to be treated as mandatory.
     *
     * @return  this SafeDatePicker
     */
    SafeDatePicker setMandatory()
    {
        isMandatory = true;
        return this;
    }

    /**
     * Set the earliest (inclusive) date permitted
     * @param date - minimum valid date - inclusive
     * @return  this SafeDatePicker
     */
    SafeDatePicker setMinValidDate (LocalDate date)
    {
        minValidDate = date;
        return this;
    }
    /**
     * Set the latest (inclusive) date permitted
     * @param date - maximum valid date - inclusive
     * @return  this SafeDatePicker
     */
    SafeDatePicker setMaxValidDate (LocalDate date)
    {
        maxValidDate = date;
        return this;
    }


    private void checkValue()
    {
//        LOGGER.traceEntry("checkValue()");
        try
        {
            if (isMandatory && getValue() == null)
            {
//                getEditor().setText(getConverter().toString(LocalDate.now()));
                cancelEdit();
                setStyle("-fx-border-color: diary-error-border-colour;");
                setValue(LocalDate.now());
                requestFocus();
                return;
            }

            if (getValue() == null) return;

//            LOGGER.debug("just before reading text");
            LocalDate item = getConverter().fromString(getEditor().getText());
            if (item.getYear() < 100)
                item = item.plusYears(2000);

//            LOGGER.debug("before min check");
            if (item.isBefore(minValidDate))
            {
                setValue(minValidDate);
                setStyle("-fx-border-color: diary-error-border-colour;");
                requestFocus();
                return;
            }

//            LOGGER.debug("before max check");
            if (item.isAfter(maxValidDate))
            {
                setValue(maxValidDate);
                setStyle("-fx-border-color: diary-error-border-colour;");
                requestFocus();
                return;
            }

//            LOGGER.debug("before setting style");
            if (isMandatory)
            {
                setStyle("-fx-border-color: diary-mandatory-border-colour;");
            }
            else
            {
                setStyle("-fx-border-color: -fx-text-box-border;");
            }

//            LOGGER.debug("before setValue");
            setValue(item);

        } catch (DateTimeParseException e) {
            LOGGER.debug("in parseException handler: {}", getEditor().getText());
//            getEditor().setText(getConverter().toString(getValue()));
            cancelEdit();
            setStyle("-fx-border-color: diary-error-border-colour;");
            requestFocus();
        }
    }



}
