/*
 * Copyright (C) 2018, 2019, 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
	2.1.0   Remove Detail_2 support - it's not used.
    2.3.0   Display products as a tree by ProductCategory
    2.4.0   Fix bug - if there were no products, the name column was sized to zero
    2.9.5   Modifications for Maven
            Add code to set name column to a sensible initial size
            Fix bug in deletion
    3.0.4	Set first row of catalogue selected.
 */

package uk.co.gardennotebook;

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

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.text.Text;
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.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.AnchorPane;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.EntryMessage;
import uk.co.gardennotebook.fxbean.ProductBean;
import uk.co.gardennotebook.fxbean.ProductCategoryBean;
import uk.co.gardennotebook.spi.GNDBException;
import uk.co.gardennotebook.spi.NotebookEntryType;

/**
	*	Controller class for create/update of Product catalogue
	*
	*	@author Andy Gegg
	*	@version	3.0.4
	*	@since	1.1.0
 */
public class ProductCat extends AnchorPane implements INotebookLoadable
{

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

	@FXML
	private ResourceBundle resources;

	@FXML
	private TreeTableView<ProductTreeBean> tblCatalogue;
	@FXML
	private TreeTableColumn<ProductTreeBean, String> colProductBrand;
	@FXML
	private TreeTableColumn<ProductTreeBean, String> colName;
	@FXML
	private TreeTableColumn<ProductTreeBean, String> colNameDetail_1;
//	@FXML
//	private TableColumn<ProductBean, String> colNameDetail_2;
	@FXML
	private TreeTableColumn<ProductTreeBean, String> colDescription;
	@FXML
	private TreeTableColumn<ProductTreeBean, ProductTreeBean> 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;

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

        colName.setCellValueFactory(cdf -> cdf.getValue().getValue().nameProperty());
        
		colNameDetail_1.setCellValueFactory(cdf -> cdf.getValue().getValue().nameDetail_1Property());

		colDescription.setCellValueFactory(cdf -> cdf.getValue().getValue().descriptionProperty());
		colDescription.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
        
		colProductBrand.setCellValueFactory(cdf -> cdf.getValue().getValue().productBrandProperty());
        
		colComment.setCellValueFactory((e)-> e.getValue().valueProperty());
		colComment.setCellFactory(x -> new ProductTreeCommentCell(resources));
        
        loadProductTree();

		tblCatalogue.setColumnResizePolicy(NotebookResizer.using(tblCatalogue));
		tblCatalogue.getSortOrder().addAll( colName,
											colNameDetail_1, 
//											colNameDetail_2,
											colProductBrand);
		
		// 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()) );
				});

		//	2.9.5
		final ObservableList<TreeItem<ProductTreeBean>> items = tblCatalogue.getRoot().getChildren();
		if (!items.isEmpty() && items.size() < 50)
		{
			double nameWidth = new Text(colName.getText()).getLayoutBounds().getWidth();
			for (TreeItem<ProductTreeBean> b : items)
			{
				final Text t = new Text(b.
						getValue().
						nameProperty().
						getValue());
				final double wid = t.getLayoutBounds().getWidth();
				if (wid > nameWidth) nameWidth = wid;
			}
			colName.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 loadProductTree()
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("loadTree()");
    
        TreeItem<ProductTreeBean> root = new TreeItem<>();
        ObservableList<ProductCategoryBean> cats = FXCollections.emptyObservableList();
        try {
            cats = ProductCategoryBean.fetchAll();
        } catch (GNDBException ex) {
            PanicHandler.panic(ex);
        }
        for (var cat : cats)
        {
            TreeItem<ProductTreeBean> branch = new TreeItem<>(new ProductTreeBean(cat));
            root.getChildren().add(branch);
            ObservableList<ProductBean> prods = FXCollections.emptyObservableList();
            try {
                prods = ProductBean.fetchAll(cat);
            } catch (GNDBException ex) {
                PanicHandler.panic(ex);
            }
            for (var prod : prods)
            {
                TreeItem<ProductTreeBean> leaf = new TreeItem<>(new ProductTreeBean(prod));
                branch.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 mnubtnAddProductCategoryOnAction(ActionEvent event)
	{
		ctxmnuAddProductCategoryOnAction(event);
	}

	@FXML
	private void mnubtnAddProductOnAction(ActionEvent event)
	{
		ctxmnuAddProductOnAction(event);
	}

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

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

	@FXML
	private void ctxmnuAddProductOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuAddProductOnAction()");
        
        //  get selected category, if any
        TreeItem<ProductTreeBean> selItem = tblCatalogue.getSelectionModel().getSelectedItem();
        ProductCategoryBean pcBean = null;
        if (selItem != null)
        {
            final NotebookEntryType ixType = selItem.getValue().getNodeType();
			switch (ixType)
			{
				case PRODUCTCATEGORY -> pcBean = selItem.getValue().getProductCategoryBean();
				case PRODUCT -> pcBean = selItem.getValue().getProductBean().getProductCategory();
				default -> {
					LOGGER.debug("ctxmnuAddProductOnAction(): Unknown node type: {}", ixType);
					LOGGER.traceExit(log4jEntryMsg);
					return;
				}
			}
        }
		ProductEditor tabCon = new ProductEditor(pcBean);
		loadTab.accept(resources.getString("tab.product"), tabCon);
		editorTabSetUp(tabCon);
		tabCon.newBeanProperty().addListener((obs, oldVal, newVal) -> {
            ProductCategoryBean newCat = newVal.getProductCategory();
            final TreeItem<ProductTreeBean> root = tblCatalogue.getRoot();
            //  the immediate children are the Categories
            for (var cat : root.getChildren())
            {
                if (cat.getValue().getProductCategoryBean().sameAs(newCat))
                {
                    var newItem = new TreeItem<>(new ProductTreeBean(newVal));
                    cat.getChildren().add(newItem);
                    tblCatalogue.sort();
                    tblCatalogue.scrollTo(tblCatalogue.getRow(newItem));
                    tblCatalogue.getSelectionModel().select(newItem);
                    break;
                }
            }
		});
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuAddOnAction()

	@FXML
	private void ctxmnuAddProductCategoryOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuAddProductCategoryOnAction()");
		ProductCategoryEditor tabCon = new ProductCategoryEditor();
		loadTab.accept(resources.getString("tab.productcategory"), tabCon);
		editorTabSetUp(tabCon);
		tabCon.newBeanProperty().addListener((obs, oldVal, newVal) -> { 
            var newItem = new TreeItem<>(new ProductTreeBean(newVal));
            tblCatalogue.getRoot().getChildren().add(newItem);
            tblCatalogue.sort();
            tblCatalogue.scrollTo(tblCatalogue.getRow(newItem));
            tblCatalogue.getSelectionModel().select(newItem);
		});
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuAddOnAction()

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

        final NotebookEntryType ixType = selItem.getValue().getNodeType();
		switch (ixType)
		{
			case PRODUCTCATEGORY -> {
				final TreeItem<ProductTreeBean> root = tblCatalogue.getRoot();
				ProductCategoryEditor tabConCat = new ProductCategoryEditor(selItem.getValue().getProductCategoryBean());
				loadTab.accept(resources.getString("tab.productcategory"), tabConCat);
				editorTabSetUp(tabConCat);
				tabConCat.deletedBeanProperty().addListener((obs, oldVal, newVal) -> {
//					root.getChildren().remove(newVal);
					root.getChildren().remove(selItem);
				});
			}
			case PRODUCT -> {
				final TreeItem<ProductTreeBean> parentCat = selItem.getParent();
				ProductEditor tabCon = new ProductEditor(selItem.getValue().getProductBean());
				loadTab.accept(resources.getString("tab.product"), tabCon);
				editorTabSetUp(tabCon);
				tabCon.deletedBeanProperty().addListener((obs, oldVal, newVal) -> {
//					parentCat.getChildren().remove(newVal);
					parentCat.getChildren().remove(selItem);
				});
			}
			default -> {
				LOGGER.debug("ctxmnuChangeOnAction(): Unknown node type: {}", ixType);
				LOGGER.traceExit(log4jEntryMsg);
				return;
			}
		}
        
		LOGGER.traceExit(log4jEntryMsg);
	}	//	ctxmnuChangeOnAction()

	@FXML
	private void ctxmnuDeleteOnAction(ActionEvent event)
	{
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("ctxmnuDeleteOnAction()");
//		ProductTreeBean ixBean = tblCatalogue.getSelectionModel().getSelectedItem().getValue();
		final TreeItem<ProductTreeBean> treeBean = tblCatalogue.getSelectionModel().getSelectedItem();
		final ProductTreeBean 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.get());
		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);
	}

	
}

