/*
 * Copyright (C) 2021, 2022 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>.
 */

/*
	Change log
	3.0.0	First version
    3.0.4	Set first row of catalogue selected.
 */

package uk.co.gardennotebook;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text;
import javafx.stage.WindowEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.EntryMessage;
import uk.co.gardennotebook.fxbean.ReviewBean;
import uk.co.gardennotebook.spi.GNDBException;
import uk.co.gardennotebook.spi.NotebookEntryType;

import java.io.IOException;
import java.time.Year;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
	*	Controller class for create/update of Review catalogue
	*
	*	@author Andy Gegg
	*	@version	3.0.4
	*	@since	3.0.0
 */
public class ReviewCat extends AnchorPane implements INotebookLoadable
{

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

	@FXML
	private ResourceBundle resources;

	@FXML
	private TreeTableView<ReviewTreeBean> tblCatalogue;
	@FXML
	private TreeTableColumn<ReviewTreeBean, String> colYear;
	@FXML
	private TreeTableColumn<ReviewTreeBean, String> colTitle;
	@FXML
	private TreeTableColumn<ReviewTreeBean, String> colDescription;
	@FXML
	private TreeTableColumn<ReviewTreeBean, ReviewTreeBean> colComment;
	@FXML
	private Button btnChange;
	@FXML
	private Button btnDelete;

	@FXML
	private MenuItem ctxmnuDelete;

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

	ReviewCat()
	{
		FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/ReviewCat.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()");
		tblCatalogue.setColumnResizePolicy(NotebookResizer.using(tblCatalogue));

		colYear.setCellValueFactory(cdf -> cdf.getValue().getValue().yearInReviewProperty());

        colTitle.setCellValueFactory(cdf -> cdf.getValue().getValue().titleProperty());
		colTitle.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());

//		colDescription.setCellValueFactory(cdf -> cdf.getValue().getValue().descriptionProperty());
//		colDescription.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
		colDescription.setCellValueFactory(cdf -> cdf.getValue().getValue().summaryProperty());

        
		colComment.setCellValueFactory((e)-> e.getValue().valueProperty());
		colComment.setCellFactory(x -> new ReviewTreeCommentCell(resources));
        
        loadReviewTree();

		tblCatalogue.setColumnResizePolicy(NotebookResizer.using(tblCatalogue));
		tblCatalogue.getSortOrder().addAll(List.of(colYear,	colTitle));
		
		// 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) ->{
					btnDelete.setDisable( (nval == null) || !(nval.getValue().canDelete()) );
				});

		ObservableList<ReviewBean> items = FXCollections.emptyObservableList();
		try {
			items = ReviewBean.fetchAll();
		} catch (GNDBException ex) {
			PanicHandler.panic(ex);
		}
		if (!items.isEmpty() && items.size() < 50)
		{
			double nameWidth = new Text(colTitle.getText()).getLayoutBounds().getWidth();
			for (ReviewBean b : items)
			{
				final Text t = new Text(b.getTitle());
				final double wid = t.getLayoutBounds().getWidth();
				if (wid > nameWidth) nameWidth = wid;
			}
			colTitle.setPrefWidth(nameWidth+30);
		}

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

		LOGGER.traceExit(log4jEntryMsg);
	}	//	initialize()
    
    private void loadReviewTree()
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("loadTree()");
    
        TreeItem<ReviewTreeBean> root = new TreeItem<>();
        ObservableList<ReviewBean> cats = FXCollections.emptyObservableList();
        try {
            cats = ReviewBean.fetchAll();
        } catch (GNDBException ex) {
            PanicHandler.panic(ex);
        }

        Year currentYear = null;
		TreeItem<ReviewTreeBean> currentBranch = null;
		for (var cat : cats)
        {
        	if (!cat.getYearInReview().equals(currentYear))
			{
				currentYear = cat.getYearInReview();
				currentBranch = new TreeItem<ReviewTreeBean>(new ReviewTreeBean(currentYear));
				root.getChildren().add(currentBranch);
			}
            TreeItem<ReviewTreeBean> leaf = new TreeItem<>(new ReviewTreeBean(cat));
			currentBranch.getChildren().add(leaf);
        }
		tblCatalogue.setRoot(root);
    }

	@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()");
        ctxmnuDelete.setDisable( !(tblCatalogue.getSelectionModel().getSelectedItem().getValue().canDelete()) );
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuOnShowing()

//	@FXML
//	private void mnubtnAddYearOnAction(ActionEvent event)
//	{
//		ctxmnuAddYearOnAction(event);
//	}

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

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

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

	@FXML
	private void ctxmnuAddReviewOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuAddReviewOnAction()");
        
        //  get selected category, if any
        TreeItem<ReviewTreeBean> selItem = tblCatalogue.getSelectionModel().getSelectedItem();
        Year selectedYear = null;
        if (selItem != null)
        {
            final NotebookEntryType ixType = selItem.getValue().getNodeType();
            if (ixType == NotebookEntryType.REVIEW)
			{
				selectedYear = selItem.getValue().getItem().getYearInReview();
			}
            else if (ixType == NotebookEntryType.UNKNOWN)
			{
				selectedYear = Year.parse(selItem.getValue().yearInReviewProperty().getValue());
			}
        }
		ReviewEditor tabCon = new ReviewEditor(selectedYear);
		loadTab.accept(resources.getString("tab.review"), tabCon);
		editorTabSetUp(tabCon);
		tabCon.newBeanProperty().addListener((obs, oldVal, newVal) -> {
            Year newYear = newVal.getYearInReview();
            LOGGER.debug("newYear string value: {}", newYear.toString());
            final TreeItem<ReviewTreeBean> root = tblCatalogue.getRoot();
			var newItem = new TreeItem<>(new ReviewTreeBean(newVal));
            //  the immediate children are the years
			boolean foundYear = false;
            for (var yearNode : root.getChildren())
            {
            	if (yearNode.getValue().getNodeType() == NotebookEntryType.UNKNOWN)
				{
					if (yearNode.getValue().yearInReviewProperty().getValue().equals(newYear.toString()))
					{
						yearNode.getChildren().add(newItem);
						yearNode.setExpanded(true);
						foundYear = true;
						break;
					}
				}
            }
            if (!foundYear)
			{
				var newYearNode = new TreeItem<>(new ReviewTreeBean(newYear));
				root.getChildren().add(newYearNode);
				newYearNode.getChildren().add(newItem);
				newYearNode.setExpanded(true);
			}
			tblCatalogue.sort();
			tblCatalogue.scrollTo(tblCatalogue.getRow(newItem));
			tblCatalogue.getSelectionModel().select(newItem);
		});
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuAddOnAction()

//	@FXML
//	private void ctxmnuAddYearOnAction(ActionEvent event)
//	{
//		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuAddYearOnAction()");
//		TextInputDialog tid = new TextInputDialog();
//		tid.setContentText("Please enter new year value");
//		String possYear = tid.showAndWait().orElse("");
//		System.out.println("possYear: "+possYear);
//		if (possYear.isEmpty()) return;
//		Integer year;
//		try
//		{
//			year = Integer.parseInt(possYear);
//		}
//		catch (NumberFormatException ex)
//		{
//			return;
//		}
//		System.out.println("year: "+year);
//		if (year == null) return;
//		System.out.println("year not null: "+year);
//		if (year < Year.now().getValue() - 10 ||
//			year > Year.now().getValue() + 10)
//		{
//			return;
//		}
//		System.out.println("valid year: "+year);
//		LOGGER.traceExit(log4jEntryMsg);
//	}	//	ctxmnuAddOnAction()

	@FXML
	private void ctxmnuChangeOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuChangeOnAction()");
        TreeItem<ReviewTreeBean> selItem = tblCatalogue.getSelectionModel().getSelectedItem();
		if (selItem == null)
		{
			LOGGER.debug("no selected item");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}

        final NotebookEntryType ixType = selItem.getValue().getNodeType();
		if (ixType == NotebookEntryType.REVIEW)
		{
				final TreeItem<ReviewTreeBean> parentCat = selItem.getParent();
				ReviewEditor tabCon = new ReviewEditor(selItem.getValue().getItem());
				loadTab.accept(resources.getString("tab.review"), tabCon);
				editorTabSetUp(tabCon);
				tabCon.deletedBeanProperty().addListener((obs, oldVal, newVal) -> {
					parentCat.getChildren().remove(selItem);
				});
		}
        
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuChangeOnAction()

	@FXML
	private void ctxmnuDeleteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuDeleteOnAction()");
		final TreeItem<ReviewTreeBean> treeBean = tblCatalogue.getSelectionModel().getSelectedItem();
		final ReviewTreeBean ixBean = treeBean.getValue();
		if (ixBean == null)
		{
			LOGGER.debug("no item selected");
			LOGGER.traceExit(log4jEntryMsg);
			return;
		}

		boolean canDelete = false;
		canDelete = ixBean.canDelete();

		if (!canDelete)
		{
			Alert checkDelete = new Alert(Alert.AlertType.INFORMATION, resources.getString("alert.cannotdelete"), ButtonType.OK);
			Optional<ButtonType> result = checkDelete.showAndWait();
			LOGGER.traceExit(log4jEntryMsg);
			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.orElse(null));
		if (result.isPresent() && result.get() == ButtonType.YES)
		{
			LOGGER.debug("after delete confirmed");
			ixBean.delete();
			treeBean.getParent().getChildren().remove(treeBean);
			tblCatalogue.refresh();
		}

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

	private void editorTabSetUp(INotebookLoadable tabCon)
	{
		tabCon.setLoadSplit(loadSplit);
		tabCon.setClearSplit(clearSplit);
		tabCon.setLoadTab(loadTab);
	}

	
}

