/*
 * 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.6.1   Code tidy-up
    2.9.6	When a Diary entry is added/changed, make sure updated comments are shown
    3.0.2	Add convenience functions to context menu.
    3.0.4	Use new convenience class NotebookBeanDeleter for deletion.
    		Set first row of catalogue selected.
	3.0.5	Use factory pattern DiaryBean
			Code tidy
 */

package uk.co.gardennotebook;


//import java.util.Optional;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
//import javafx.beans.value.ChangeListener;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.stage.WindowEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
//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.cell.TextFieldTableCell;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.EntryMessage;
import uk.co.gardennotebook.fxbean.*;
import uk.co.gardennotebook.spi.GNDBException;
import uk.co.gardennotebook.spi.ReminderIntervalType;
import uk.co.gardennotebook.util.StoryLineTree;

/**
	*	Controller class for create/update of Reminder catalogue
	*
	*	@author Andy Gegg
	*	@version	3.0.4
	*	@since	1.1.0
 */
final class ReminderCat extends AnchorPane implements INotebookLoadable
{

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

	@FXML
	private ResourceBundle resources;

	@FXML
	private TableView<ReminderBean> tblCatalogue;
	@FXML
	private TableColumn<ReminderBean, HusbandryClassBean> colHusbandryClass;
	@FXML
	private TableColumn<ReminderBean, GroundworkActivityBean> colGroundworkActivity;
	@FXML
	private TableColumn<ReminderBean, PlantSpeciesBean> colPlantSpecies;
	@FXML
	private TableColumn<ReminderBean, PlantVarietyBean> colPlantVariety;
	@FXML
	private TableColumn<ReminderBean, LocalDate> colShowFrom;
	@FXML
	private TableColumn<ReminderBean, Boolean> colSingleShot;
	@FXML
	private TableColumn<ReminderBean, ReminderIntervalType> colRepeatInterval;
	@FXML
	private TableColumn<ReminderBean, Integer> colRepeatQuantifier;
	@FXML
	private TableColumn<ReminderBean, LocalDate> colRepeatUntil;
	@FXML
	private TableColumn<ReminderBean, String> colDescription;
	@FXML
	private TableColumn<ReminderBean, HusbandryClassBean> colComment;
	@FXML
	private Button btnChange;
	@FXML
	private Button btnDelete;

	@FXML
	private MenuItem ctxmnuDelete;
	@FXML
	private MenuItem ctxmnuPriorHistory;
	@FXML
	private MenuItem ctxmnuOrigin;
	@FXML
	private MenuItem ctxmnuComplete;


	private Consumer<Node> loadSplit;
	private Consumer<Node> clearSplit;
	private BiConsumer<String, Node> loadTab;
	private Consumer<Node> clearTab;
	
	//	handles addition of multiple reminders
	//	not sure how to trigger the 'added' condition - description is mandatory but also some combination of
	//	HusbandryClass, GroundworkActivity and PlantSpecies
//	private final ChangeListener reminderAdder = new ChangeListener() {
//		@Override
//		public void changed(ObservableValue obj, Object oldVal, Object newVal) {
//			if (newVal != null)
//			{
//				ReminderBean pib_new = new ReminderBean();
//				pib_new.descriptionProperty().addListener(this);
//				tblCatalogue.getItems().add(pib_new);
//				obj.removeListener(reminderAdder);
//			}
//		}
//	};

	ReminderCat()
	{
		FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/ReminderCat.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);
		}
	}// constructor

	/*
	* Initializes the controller class.
	*/
	@FXML
	private void initialize()
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("initialize()");
		

		ObservableList<ReminderBean> items = FXCollections.emptyObservableList();
		try {
			items = ReminderBean.fetchAll();
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}
		tblCatalogue.setColumnResizePolicy(NotebookResizer.using(tblCatalogue));
		
		colHusbandryClass.setCellFactory(x -> new NotebookDropDownCellType<>(
													(ri, ci) -> new HusbandryClassCombo(ci),
													HusbandryClassBean::getName) );
		colHusbandryClass.setCellValueFactory(cdf -> cdf.getValue().husbandryClassProperty());

		colGroundworkActivity.setCellFactory(x -> new NotebookDropDownCellType<>(
													(ri, ci) -> new GroundworkActivityCombo(ci),
													hc -> (hc==null ? null : hc.getName())) );
		colGroundworkActivity.setCellValueFactory(cdf -> cdf.getValue().groundworkActivityProperty());

		colPlantSpecies.setCellFactory(x -> new NotebookDropDownCellType<>(
													(ri, ci) -> new PlantSpeciesCombo(ci),
													hc -> (hc==null ? null : hc.getCommonName())) );
		colPlantSpecies.setCellValueFactory(cdf -> cdf.getValue().plantSpeciesProperty());

		colPlantVariety.setCellValueFactory(cdf -> cdf.getValue().plantVarietyProperty());
		colPlantVariety.setCellFactory(x -> new NotebookDropDownCellType<>(
				(ri, ci) -> new PlantVarietyCombo(ri.getPlantSpecies(), ci),
				hc -> (hc == null ? null : hc.getCommonName()))
		{
			@Override
			protected boolean mayEdit(ReminderBean rowItem, PlantVarietyBean cellItem)
			{
				return rowItem != null && rowItem.getPlantSpecies() != null;
			}

		});

		colShowFrom.setCellFactory(x -> new NotebookDateCellType<>() );
		colShowFrom.setCellValueFactory(cdf -> cdf.getValue().showFromProperty());

		colSingleShot.setCellFactory(CheckBoxTableCell.forTableColumn(colSingleShot));
		colSingleShot.setCellValueFactory(cdf -> cdf.getValue().singleShotProperty());
		
		colRepeatQuantifier.setCellFactory(cdf -> new NotebookIntegerCellType<>(1, true)
		{
			@Override
			protected boolean mayEdit(ReminderBean rowItem, Integer cellItem)
			{
				return rowItem != null && !(rowItem.isSingleShot());
			}
		});
		colRepeatQuantifier.setCellValueFactory(cdf -> (ObservableValue)(cdf.getValue().repeatQuantifierProperty()));
		
		colRepeatInterval.setCellValueFactory(cdf -> cdf.getValue().repeatIntervalProperty());
		colRepeatInterval.setCellFactory(x -> new TableCell<>()
		{
			@Override
			protected void updateItem(ReminderIntervalType item, boolean empty)
			{
				super.updateItem(item, empty);
				if (empty || item == null)
				{
					setGraphic(null);
					setText(null);
					return;
				}
				updateViewMode();
			}

			@Override
			public void startEdit()
			{
				super.startEdit();
				updateViewMode();
			}

			@Override
			public void cancelEdit()
			{
				super.cancelEdit();
				updateViewMode();
			}

			private void updateViewMode()
			{
				LOGGER.debug("colRepeatInterval: updateViewMode(): item: {}", getItem());
				setGraphic(null);
				setText(null);
				if (getItem() == null)
					return;
				ReminderBean rowItem = getTableView().getSelectionModel().getSelectedItem();
				if (isEditing() && rowItem != null && !rowItem.isSingleShot())
				{
					VBox vBox = new VBox();
					ReminderIntervalCombo cb = new ReminderIntervalCombo(resources, getItem());
					cb.setEditable(false);

					cb.setOnAction(ev -> {
						LOGGER.debug("colRepeatInterval: setOnAction: selection: {}", cb.getSelectionModel().getSelectedItem());
						ReminderIntervalType rpt = cb.getSelectionModel().getSelectedItem();
						if (rpt == null) return;
						commitEdit(rpt);
					});
					vBox.getChildren().add(cb);
					setGraphic(vBox);
				}
				else
				{
					if (getItem() != null /*&& !getItem().isEmpty() */)
					{
						setText(getItem().getDisplayName(resources));
					}
				}
			}
		});
		
		colRepeatUntil.setCellFactory(x -> new NotebookDateCellType<>()
		{
			@Override
			protected boolean mayEdit(ReminderBean rowItem, LocalDate cellItem)
			{
				return rowItem != null && !(rowItem.isSingleShot());
			}
		});
		colRepeatUntil.setCellValueFactory(cdf -> cdf.getValue().repeatUntilProperty());

		colDescription.setCellFactory(TextFieldTableCell.forTableColumn());
		colDescription.setCellValueFactory(cdf -> cdf.getValue().descriptionProperty());
		
		tblCatalogue.getItems().setAll(items);

// not sure how to trigger the save, so keep it like all the other catalogues		
//		ReminderBean rb = new ReminderBean();
//		rb.descriptionProperty().addListener(reminderAdder);
//		tblCatalogue.getItems().add(rb);

		// only allow change and delete if there is a row selected
		btnChange.disableProperty().bind(tblCatalogue.getSelectionModel().selectedItemProperty().isNull());
		btnDelete.setDisable(true);
		tblCatalogue.getSelectionModel().selectedItemProperty().addListener((obs, old, nval) ->{
			try {
				btnDelete.setDisable( (nval == null) || !(nval.canDelete()) );
			} catch (GNDBException ex) {
				PanicHandler.panic(ex);
			}
				});

		tblCatalogue.getSelectionModel().select(0);
		Platform.runLater(() -> tblCatalogue.requestFocus());	//	this enables keyboard navigation with the up/down arrows

//		if (items.size() <50)
//		{
//			double nameWidth = 0;
//			for (ReminderBean b : items)
//			{
//				Text t = new Text(b.getName());
//				double wid = t.getLayoutBounds().getWidth();
//				if (wid > nameWidth) nameWidth = wid;
//			}
//			colName.setPrefWidth(nameWidth+10);
//		}

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

	@FXML
	private void ctxmnuOnShowing(WindowEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuOnShowing()");
		ReminderBean ixBean = tblCatalogue.getSelectionModel().selectedItemProperty().get();
		try {
			ctxmnuDelete.setDisable( (ixBean == null) || !(ixBean.canDelete()) );
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}

		ctxmnuComplete.setDisable((ixBean == null) || !ixBean.isSingleShot());
		ctxmnuOrigin.setDisable((ixBean == null) || !ixBean.hasHusbandry());
		ctxmnuPriorHistory.setDisable((ixBean == null) || !ixBean.hasHusbandry());

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

	@FXML
	private void btnAddOnAction(ActionEvent event)
	{
		ctxmnuAddOnAction(event);
	}

	@FXML
	private void btnChangeOnAction(ActionEvent event)
	{
		ctxmnuChangeOnAction(event);
	}

	@FXML
	private void btnDeleteOnAction(ActionEvent event)
	{
		ctxmnuDeleteOnAction(event);
	}

	@FXML
	private void ctxmnuAddOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuAddOnAction()");
		ReminderEditor tabCon = new ReminderEditor();
		loadTab.accept(resources.getString("tab.reminder"), tabCon);
		tabCon.newBeanProperty().addListener((obs, oldVal, newVal) -> tblCatalogue.getItems().add(newVal));
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuAddOnAction()

	@FXML
	private void ctxmnuChangeOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuChangeOnAction()");
		final ReminderBean ixBean = tblCatalogue.getSelectionModel().selectedItemProperty().get();
		if (ixBean == null)
		{
			LOGGER.debug("thisValueBean is null");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}

		ReminderEditor tabCon = new ReminderEditor(ixBean);
		loadTab.accept(resources.getString("tab.reminder"), tabCon);
		tabCon.deletedBeanProperty().addListener((obs, oldVal, newVal) -> tblCatalogue.getItems().remove(ixBean));
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuChangeOnAction()

	@FXML
	private void ctxmnuDeleteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuDeleteOnAction()");
		final ReminderBean ixBean = tblCatalogue.getSelectionModel().selectedItemProperty().get();
		if (ixBean == null)
		{
			LOGGER.debug("thisValueBean is null");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}

		NotebookBeanDeleter<ReminderBean> deleterImpl = new NotebookBeanDeleter<>(resources);
		if (deleterImpl.deleteItemImpl(ixBean))
		{
			tblCatalogue.getItems().remove(ixBean);
		}

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

	@FXML
	private void ctxmnuCompleteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuToDoCompleteOnAction()");
		final ReminderBean tdb = tblCatalogue.getSelectionModel().getSelectedItem();
		if (!tdb.isSingleShot()) return;

		try {
			tdb.completeAction(hb -> {
						HusbandryEditor customTest = new HusbandryEditor(hb);
						loadTab.accept(resources.getString("tab.husbandry"), customTest);
					},
					gb -> {
						GroundworkEditor customTest = new GroundworkEditor(gb);
						loadTab.accept(resources.getString("tab.groundwork"), customTest);
					});
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}
		tblCatalogue.getItems().remove(tdb);
		if (tblCatalogue.getItems().isEmpty())
		{
			tblCatalogue.setVisible(false);
		}
	}

	@FXML
	private void ctxmnuOrigin (ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuToDoOrigin()");
		final ReminderBean tdb = tblCatalogue.getSelectionModel().getSelectedItem();
		if (tdb.hasHusbandry())
		{
			final HusbandryBean hb = tdb.getHusbandry();
			final HusbandryEditor customTest = new HusbandryEditor(hb);
			loadTab.accept(resources.getString("tab.husbandry"), customTest);
		}
	}

	@FXML
	private void ctxmnuPriorHistory (ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuToDoPriorHistory()");
		final ReminderBean tdb = tblCatalogue.getSelectionModel().getSelectedItem();
		if (tdb.hasHusbandry())
		{
			final HusbandryBean hb = tdb.getHusbandry();
			try
			{
				if (hb.hasAncestor())
				{
					final DiaryBean diaryBean = DiaryBean.from(hb);
					final StoryLineTree<DiaryBean> history = diaryBean.getAncestors();
					final StoryLineTab tabCon = new StoryLineTab();
					loadTab.accept(resources.getString("tab.ancestors"), tabCon);
					tabCon.setHistory(history);
				}
			}
			catch (GNDBException ex)
			{
				PanicHandler.panic(ex);
			}
		}
	}

}

