/*
 * Copyright (C) 2018, 2022 Andrew Gegg
 *
 *	This file is part of the Garden Notebook application
 *
 * The Garden 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>.
 */

/*
	Change log
    2.4.0   Support tab-out on comments
            Disallow deletion of Species level notes
	3.0.0	Fix bug deleting (last) PlantNote
    3.0.1	CommentAdder handling moved to separate class
    3.0.2	Check before Cancel if changes have been made
    3.0.4	Use new convenience class NotebookBeanDeleter for deletion.
    		Use new convenience class NotebookBeanCanceller to handle edit cancellation.
    		Use new convenience class EditorCommentTableHandler to handle Comment table construction.
    		Set focus on first field.
 */

package uk.co.gardennotebook;

import java.io.IOException;
//import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
//import java.time.format.DateTimeFormatter;
//import java.time.format.FormatStyle;

import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty;
//import javafx.beans.value.ChangeListener;
//import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
//import javafx.scene.text.Text;
//import javafx.scene.control.Alert;
//import javafx.scene.control.ButtonType;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import uk.co.gardennotebook.fxbean.CommentBean;
import uk.co.gardennotebook.fxbean.PlantNoteBean;
import uk.co.gardennotebook.fxbean.PlantVarietyBean;
import uk.co.gardennotebook.fxbean.PlantSpeciesBean;
import uk.co.gardennotebook.spi.GNDBException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.EntryMessage;

/**
	*	Controller class for create/update of PlantVariety items
	*
	*	@author Andy Gegg
	*	@version	3.0.4
	*	@since	1.0
 */
final public class PlantVarietyEditor extends GridPane implements INotebookLoadable
{

	private static final Logger LOGGER = LogManager.getLogger();

	private final PlantSpeciesBean parentBean;
	private final PlantVarietyBean thisValueBean;

	private Consumer<Node> loadSplit;
	private Consumer<Node> clearSplit;
	private BiConsumer<String, Node> loadTab;
	private Consumer<Node> clearTab;

	//	an observable property that the caller can monitor when a new item is being created
	private SimpleObjectProperty<PlantVarietyBean> newBean;
	private final boolean addingItem;

	//	an observable property that the caller can monitor when an item is deleted
	private SimpleObjectProperty<Object> deletedBean;

	@FXML
	private ResourceBundle resources;

	@FXML
	private Button btnSave;
	@FXML
	private Button btnDelete;
	@FXML
	private Button btnDeleteNote;
	@FXML
	private TextField fldSpeciesCommonName;
	@FXML
	private TextField fldCommonName;
	@FXML
	private TextField fldLatinName;
	@FXML
	private TextField fldSynonymSet;
	@FXML
	private TextField fldDescription;
	@FXML
	private TextField fldUtility;
	@FXML
	private TextField fldHardiness;
	@FXML
	private TextField fldLifeType;
	@FXML
	private TextField fldPlantType;

	@FXML
	private TabPane plantNotes;
	
	@FXML
	private TableView<CommentBean> tbvComment;
	@FXML
	private TableColumn<CommentBean, CommentBean> tbvCommentDate;
	@FXML
	private TableColumn<CommentBean, String> tbvCommentText;

	PlantVarietyEditor(PlantSpeciesBean parent)
	{
		this(parent, null);
	}

	PlantVarietyEditor(PlantSpeciesBean parent, PlantVarietyBean initialVal)
	{
		this.thisValueBean = (initialVal != null ? initialVal : new PlantVarietyBean());
		this.addingItem = (initialVal == null);
		this.parentBean = parent;
		this.thisValueBean.setPlantSpecies(parentBean);
		FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/PlantVarietyEditor.fxml"),
			ResourceBundle.getBundle("notebook") );
		fxmlLoader.setRoot(this);
		fxmlLoader.setController(this);
		try {
			fxmlLoader.load();	// NB initialize is called from in here
		} catch (IOException exception) {
			throw new RuntimeException(exception);
		}
	}

	/*
	* Initializes the controller class.
	*/
	@FXML
	private void initialize()
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("initialize()");
//		tbvCommentDate.setCellValueFactory(e-> new SimpleObjectProperty<>( e.getValue()));
//		tbvCommentDate.setCellFactory(x -> new EditorCommentDateTableCell(resources));
//		tbvCommentText.setCellValueFactory(e->e.getValue().commentProperty());
//		tbvCommentText.setCellFactory(x -> new EditorCommentTextTableCell(resources));  //  2.4.0
//		tbvComment.setColumnResizePolicy(NotebookResizer.using(tbvComment));
//		tbvComment.getItems().setAll(thisValueBean.getComments());
//		CommentBean cb_new = new CommentBean(this.thisValueBean);
//
//		ChangeListener<String> commentAdder = new EditorCommentAdder<>(thisValueBean, tbvComment);
//		cb_new.commentProperty().addListener(commentAdder);
//		tbvComment.getItems().add(cb_new);
//		CommentBean cb_one = tbvComment.getItems().get(0);
//		Text t = new Text(cb_one.getDate().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)));
//		double wid = t.getLayoutBounds().getWidth();
//		tbvCommentDate.setPrefWidth(wid+10);

		final EditorCommentTableHandler<PlantVarietyBean> cth = new EditorCommentTableHandler<>(resources);
		cth.constructCommentTable(tbvComment, tbvCommentDate, tbvCommentText, thisValueBean);

		thisValueBean.setSaveRequired(true);

        fldSpeciesCommonName.setText(thisValueBean.getPlantSpecies().getCommonName());
		fldCommonName.textProperty().bindBidirectional(thisValueBean.commonNameProperty());
		fldCommonName.setTextFormatter(MaxLengthTextFormatter.getFormatter(255));

		fldLatinName.textProperty().bindBidirectional(thisValueBean.latinNameProperty());
		fldLatinName.setTextFormatter(MaxLengthTextFormatter.getFormatter(255));

		fldSynonymSet.textProperty().bindBidirectional(thisValueBean.synonymSetProperty());
		fldSynonymSet.setTextFormatter(MaxLengthTextFormatter.getFormatter(255));

		fldDescription.textProperty().bindBidirectional(thisValueBean.descriptionProperty());
		fldDescription.setTextFormatter(MaxLengthTextFormatter.getFormatter(255));

		fldUtility.textProperty().bindBidirectional(thisValueBean.utilityProperty());
		fldUtility.setTextFormatter(MaxLengthTextFormatter.getFormatter(45));

		fldHardiness.textProperty().bindBidirectional(thisValueBean.hardinessProperty());
		fldHardiness.setTextFormatter(MaxLengthTextFormatter.getFormatter(45));

		fldLifeType.textProperty().bindBidirectional(thisValueBean.lifeTypeProperty());
		fldLifeType.setTextFormatter(MaxLengthTextFormatter.getFormatter(45));

		fldPlantType.textProperty().bindBidirectional(thisValueBean.plantTypeProperty());
		fldPlantType.setTextFormatter(MaxLengthTextFormatter.getFormatter(45));

		ObservableList<PlantNoteBean> pnbList = FXCollections.emptyObservableList();
		// first add any notes from the species, but NOT editable
		try {
			pnbList = thisValueBean.getPlantSpecies().getPlantNote();
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}
		for (PlantNoteBean pnb : pnbList)
		{
			PlantNoteTab pnt = new PlantNoteTab(pnb, false);
			Tab tab = new Tab("", pnt);
			plantNotes.getTabs().add(tab);
			tab.textProperty().bind(pnt.titleProperty());
		}
        //  2.4.0
        //  if there are any species level notes, the first will be selected on entry, so set the Delete button off
        if (!pnbList.isEmpty())
        {
            btnDeleteNote.setDisable(true);
        }
        
		// and now any variety specific notes
		try {
			pnbList = thisValueBean.getPlantNote();
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}
		for (PlantNoteBean pnb : pnbList)
		{
			PlantNoteTab pnt = new PlantNoteTab(pnb);
			Tab tab = new Tab("", pnt);
			plantNotes.getTabs().add(tab);
			tab.textProperty().bind(pnt.titleProperty());
		}
        //  2.4.0, 3.0.0
//        plantNotes.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> btnDeleteNote.setDisable(!((PlantNoteTab)(newTab.getContent())).isEditable()));
		plantNotes.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> btnDeleteNote.setDisable((newTab == null) || !((PlantNoteTab)(newTab.getContent())).isEditable()));

		btnSave.disableProperty().bind(fldCommonName.textProperty().isNull().or(fldCommonName.textProperty().isEmpty()));
		try
		{
			btnDelete.setDisable(addingItem || !(this.thisValueBean.canDelete()));
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}

		Platform.runLater(() -> {fldCommonName.requestFocus(); fldCommonName.deselect();});

		LOGGER.traceExit(log4jEntryMsg);
	}	//	initialize()

	@Override
	public void setLoadSplit(Consumer<Node> code)
	{
		loadSplit = code;
	}

	@Override
	public void setClearSplit(Consumer<Node> code)
	{
		clearSplit = code;
	}

	@Override
	public void setLoadTab(BiConsumer<String, Node> code)
	{
		loadTab = code;
	}

	@Override
	public void setClearTab(Consumer<Node> code)
	{
		clearTab = code;
	}

	@Override
	public void clearUpOnClose(Event e)
	{
		if (isCancelDenied())
		{
			e.consume();
			return;
		}

		thisValueBean.cancelEdit();
	}

	@FXML
	private void btnCancelOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("btnCancelOnAction()");
		if (thisValueBean == null)
		{
			LOGGER.debug("thisValueBean is null");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}

		if (isCancelDenied())
		{
			return;
		}

		thisValueBean.cancelEdit();
		
		ObservableList<PlantNoteBean> pvNotes = FXCollections.emptyObservableList();
		try {
			pvNotes = thisValueBean.getPlantNote();
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}

		for (PlantNoteBean pvn : pvNotes)
		{
			pvn.cancelEdit();
		}
				
		clearTab.accept(this);
		LOGGER.traceExit(log4jEntryMsg);
	}	//	btnCancelOnAction()

	/**
	 * Check if user really wants to quit if changes have been made
	 *
	 * @return	true	user does NOT want to quit
	 */
	private boolean isCancelDenied()
	{
		if (thisValueBean.needSave())
		{
//			Alert checkQuit = new Alert(Alert.AlertType.CONFIRMATION, resources.getString("alert.confirmquit"), ButtonType.NO, ButtonType.YES);
//			Optional<ButtonType> result = checkQuit.showAndWait();
//			LOGGER.debug("after delete dialog: result:{}, result.get:{}",result, result.orElse(null));
//			if (result.isPresent() && result.get() == ButtonType.NO)
//			{
//				LOGGER.debug("after quit denied");
//				return true;
//			}
			final NotebookEditorCanceller<PlantVarietyBean> cancelChecker = new NotebookEditorCanceller<>(resources);
			return cancelChecker.isCancelDenied(thisValueBean);
		}
		return false;
	}

	@FXML
	private void btnDeleteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("btnDeleteOnAction()");
		if (thisValueBean == null)
		{
			LOGGER.debug("thisValueBean is null");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}

//		boolean canDelete = false;
//		try
//		{
//			canDelete = thisValueBean.canDelete();
//		} catch (GNDBException ex) {
//			PanicHandler.panic(ex);
//		}
//		if (!canDelete)
//		{
//			Alert checkDelete = new Alert(Alert.AlertType.INFORMATION, resources.getString("alert.cannotdelete"), ButtonType.OK);
//			Optional<ButtonType> result = checkDelete.showAndWait();
//			LOGGER.debug("item cannot be deleted");
//			return;
//		}
//		Alert checkDelete = new Alert(Alert.AlertType.CONFIRMATION, resources.getString("alert.confirmdelete"), ButtonType.NO, ButtonType.YES);
//		Optional<ButtonType> result = checkDelete.showAndWait();
//		LOGGER.debug("after delete dialog: result:{}, result.get:{}",result, result.get());
//		if (result.isPresent() && result.get() == ButtonType.YES)
//		{
//			LOGGER.debug("after delete confirmed");
//			try
//			{
//				thisValueBean.delete();
//			} catch (GNDBException ex) {
//				PanicHandler.panic(ex);
//			}
//			deletedBeanProperty().setValue(new Object());
//			clearTab.accept(this);
//		}
		final NotebookBeanDeleter<PlantVarietyBean> deleterImpl = new NotebookBeanDeleter<>(resources);
		if (deleterImpl.deleteItemImpl(thisValueBean))
		{
			deletedBeanProperty().setValue(new Object());
			clearTab.accept(this);
		}
		LOGGER.traceExit(log4jEntryMsg);
	}	//	btnDeleteOnAction()

	@FXML
	private void btnSaveOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("btnSaveOnAction()");
		if (thisValueBean == null)
		{
			LOGGER.debug("thisValueBean is null");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}
		try
		{
			thisValueBean.save();
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}

		for (Tab tab : plantNotes.getTabs())
		{
			LOGGER.debug("PlantNote loop: for: {}", tab.toString());
			PlantNoteBean pnb = ((PlantNoteTab)(tab.getContent())).getPlantNote();
			LOGGER.debug("PlantNote loop: plant note: {}", pnb);
			if (!pnb.canSave())
			{
				continue;
			}
			if (!pnb.needSave())
			{
				continue;
			}
			try {
				pnb.save();
			} catch (GNDBException ex) {
				PanicHandler.panic(ex);
			}
		}

		newBeanProperty().setValue(thisValueBean);
		clearTab.accept(this);
		LOGGER.traceExit(log4jEntryMsg);
	}	//	btnSaveOnAction()

	@FXML
	private void btnAddNoteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("btnAddNoteOnAction()");
		PlantNoteTab pnt = new PlantNoteTab(thisValueBean);
		Tab tab = new Tab("", pnt);
		plantNotes.getTabs().add(tab);
		tab.textProperty().bind(pnt.titleProperty());
		plantNotes.getSelectionModel().select(tab);		
	}
	
	@FXML
	private void btnDeleteNoteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("btnDeleteNoteOnAction()");
		
		Tab tab = plantNotes.getSelectionModel().getSelectedItem();
		if (tab == null)
			return;
        if (!((PlantNoteTab)(tab.getContent())).isEditable())   //  2.4.0
            return;
        
		PlantNoteBean pnb = ((PlantNoteTab)(tab.getContent())).getPlantNote();
		
		boolean canDelete = true;
		try {
			canDelete = pnb.canDelete();
		} catch (GNDBException ex) {
				PanicHandler.panic(ex);
		}
		if (!canDelete)
			return;
		
		try {
			pnb.delete();
		} catch (GNDBException ex) {
				PanicHandler.panic(ex);
		}
		plantNotes.getTabs().remove(tab);
	}

	SimpleObjectProperty<PlantVarietyBean> newBeanProperty()
	{
		if (newBean == null)
		{
			newBean = new SimpleObjectProperty<>();
		}
		return newBean;
	}

	SimpleObjectProperty<Object> deletedBeanProperty()
	{
		if (deletedBean == null)
		{
			deletedBean = new SimpleObjectProperty<>();
		}
		return deletedBean;
	}

}
