/*
 * Copyright (C) 2020, 2022, 2023 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.0   First version
    3.0.2   Fix bug when trying to link items without a PlantSpecies
    3.1.0	Change constructor for new descendant entries (to get the date)
            Remove 'no ancestors' restriction when D&D'ing for a Sale
 */

package uk.co.gardennotebook;

import java.time.LocalDate;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TableRow;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.EntryMessage;
import uk.co.gardennotebook.fxbean.AfflictionEventBean;
import uk.co.gardennotebook.fxbean.GroundworkBean;
import uk.co.gardennotebook.fxbean.HusbandryBean;
import uk.co.gardennotebook.fxbean.PlantSpeciesBean;
import uk.co.gardennotebook.fxbean.PlantVarietyBean;
import uk.co.gardennotebook.fxbean.ProductBean;
import uk.co.gardennotebook.fxbean.PurchaseItemBean;
import uk.co.gardennotebook.fxbean.SaleBean;
import uk.co.gardennotebook.fxbean.SaleItemBean;
import uk.co.gardennotebook.spi.GNDBException;
import uk.co.gardennotebook.spi.NotebookEntryType;

/**
 *  Support for Drag&Drop functionality in the DiaryTab, specifically to retro-fit a Storyline to extant entries.
 * 
*	@author	Andy Gegg
*	@version	3.1.0
*	@since	2.6.0
 */
public class DiaryTabDDTableRow extends TableRow<IndexedDiaryBean>
{
	private static final Logger LOGGER = LogManager.getLogger();

    final DiaryTabDDTableRow row;
    
    // these are needed to pop up the SaleEditor tab
	private Consumer<Node> loadSplit;
	private Consumer<Node> clearSplit;
	private BiConsumer<String, Node> loadTab;
	private Consumer<Node> clearTab;

	final private ResourceBundle resources;

    DiaryTabDDTableRow(ResourceBundle resources)
    {
        
        this.resources = resources;
        
        setOnDragDetected(eh -> {
            onDragDetectedImpl();
            eh.consume();
        });
        
        setOnDragOver(eh -> {
            if (onDragOverImpl(eh))
            {
                eh.acceptTransferModes(TransferMode.LINK);
            }
            eh.consume();
        });
        
        setOnDragDropped(eh -> {
            boolean success = onDragDroppedImpl(eh);
            eh.setDropCompleted(success);

            eh.consume();
        });
        
        row = this;
    }
    
	public void setLoadSplit(Consumer<Node> code)
	{
		loadSplit = code;
	}

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

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

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

    /**
     * Handle start of Drag action
     * 
     * @since 2.6.0
     */
    private void onDragDetectedImpl()
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("onDragDetectedImpl()");

        //  in here, row is the Drag SOURCE - what is being dragged
        if (row.isEmpty()) return;
        final IndexedDiaryBean bean = row.getItem();
        if (bean == null) return;
        final DiaryBean item = bean.getItem();
        if (item == null) return;
        LOGGER.debug("row valid");

        // Weather and Wildlife cannot be in a Storyline
        if (item.getItemType() == NotebookEntryType.WEATHER || item.getItemType() == NotebookEntryType.WILDLIFE)
            return;
        // Purchase per se is not Threadable; all PurchaseItems are managed through the PurchaseEditor
        if (item.getItemType() == NotebookEntryType.PURCHASE)
            return;
        LOGGER.debug("type valid");

        Dragboard db = startDragAndDrop(TransferMode.LINK);
        db.setDragView(row.snapshot(null, null));
        ClipboardContent content = new ClipboardContent();
        content.putString("diaryTable:"+row.getIndex());
        db.setContent(content);
        LOGGER.debug("dragBoard content: {}", content.getString());
        
    }   //  onDragDetectedImpl()
    
    /**
     * Determine if the current row (the potential drop-site) is a valid link.
     * 
     * @param eh    the drag event
     * @return  true means item can be dropped here
     * 
     * @since 2.6.0
     */
    private boolean onDragOverImpl(DragEvent eh)
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("onDragOverImpl()");

        // in here, row is the drop TARGET
        if (row.isEmpty()) return false;
        final IndexedDiaryBean bean = row.getItem();
        if (bean == null) return false;
        final DiaryBean targetItem = bean.getItem();
        if (targetItem == null) return false;
        LOGGER.debug("onDragOverImpl(): row valid");

        final NotebookEntryType targetType = targetItem.getItemType();

        // Weather and Wildlife cannot be in a Storyline
        if ( targetType == NotebookEntryType.WEATHER || targetType == NotebookEntryType.WILDLIFE )
                return false;
        // Purchase per se is not Threadable; all PurchaseItems are managed through the PurchaseEditor
        if ( targetType == NotebookEntryType.PURCHASE )
                return false;
        LOGGER.debug("onDragOverImpl(): type valid");

        Dragboard db = eh.getDragboard();
        if ( db.hasString() && 
            (db.getString() != null) &&
            (!db.getString().isEmpty()) &&
            (db.getString().startsWith("diaryTable:")))
        {
            final int srcRowIx = Integer.parseInt(db.getString().substring(11));
            final DiaryBean srcItem = (row.getTableView().getItems().get(srcRowIx)).getItem();
            final NotebookEntryType srcType = srcItem.getItemType();
            LOGGER.debug("onDragOverImpl(): srcType: {}, targetType: {}", srcType, targetType);

            // reject certain combinations
            // cannot link Weather and Wildlife into a Storyline
            if ( srcType == NotebookEntryType.WEATHER || srcType == NotebookEntryType.WILDLIFE )
                return false;
            // Purchase per se is not Threadable; all PurchaseItems are managed through the PurchaseEditor
            if ( srcType == NotebookEntryType.PURCHASE )
                    return false;
            // cannot directly link a Sale and a Purchase
            //  if srcType or targetType is a Purchase, we've already dropped out.
            if (srcType == NotebookEntryType.PURCHASEITEM && (targetType == NotebookEntryType.SALEITEM || targetType == NotebookEntryType.SALE))
                return false;
            if (targetType == NotebookEntryType.PURCHASEITEM && (srcType == NotebookEntryType.SALEITEM || srcType == NotebookEntryType.SALE))
                return false;
            // cannot link Purchase to Purchase nor Sale to Sale
            if (srcType == NotebookEntryType.PURCHASEITEM && targetType == NotebookEntryType.PURCHASEITEM)
                return false;
            if ( (targetType == NotebookEntryType.SALEITEM || targetType == NotebookEntryType.SALE) &&
                    (srcType == NotebookEntryType.SALEITEM || srcType == NotebookEntryType.SALE) )
                return false;
            LOGGER.debug("onDragOverImpl(): type combination valid");

            // if src or target is a SALE we can just accept the drop if the Sale date is not before the link
            // this does not apply to SaleItems
            final LocalDate srcDate = srcItem.getDate();
            final LocalDate targetDate = targetItem.getDate();
            if (srcType == NotebookEntryType.SALE)
            {// targetDate must not be AFTER srcDate
                if (targetDate.isAfter(srcDate))
                    return false;
            }
            else if (targetType == NotebookEntryType.SALE)
            {
                if (srcDate.isAfter(targetDate))
                    return false;
            }
            LOGGER.debug("onDragOverImpl(): after check SALE dates");

            boolean srcAnc = false;
            boolean srcDesc = false;
            boolean targetAnc = false;
            boolean targetDesc = false;
            try {
                srcAnc = srcItem.hasAncestor();
                srcDesc = srcItem.hasDescendant();
                targetAnc = targetItem.hasAncestor();
                targetDesc = targetItem.hasDescendant();
            } catch (GNDBException ex) {
                PanicHandler.panic(ex);
            }

            final PlantSpeciesBean srcPS = getPlantSpecies(srcItem);
            final PlantSpeciesBean targetPS = getPlantSpecies(targetItem);

            // cannot link a Sale to an item with a descendant or no PlantSpecies (e.g. Groundwork), but otherwise we accept a SALE
            //  3.1.0   The 'no descendants' rule makes no sense.  I was probably thinking of a Sale of produce post-harvest but even then
            //  it would only allow a single instance of a Sale.  I routinely give away spare plants, particularly tomatoes, after the 'pot up'
            //  stage to multiple recipients even after other plants have been planted out and so the 'donating' entry has multiple descendants
            //  logically.
            if (srcType == NotebookEntryType.SALE)
//                return !targetDesc;
//                return !targetDesc && (targetPS != null);
                return (targetPS != null);
            if (targetType == NotebookEntryType.SALE)
//                return !srcDesc;
//                return !srcDesc && (srcPS != null);
                return (srcPS != null);
            LOGGER.debug("onDragOverImpl(): after check SALE history");

            // linked items MUST be for same PlantSpecies and PlantVariety (if present) - Groundwork & AfflictionEvent need not have a PS
            // (remember the src or target can be a SALE; for SaleItem the rule still applies.  SALEs have already been handled.)
            //  Since this is checking Diary entries, we can assume the entries are valid - they have PS if they need them!
//            final PlantSpeciesBean srcPS = getPlantSpecies(srcItem);
//            if (srcPS == null)
//                return false;
//            final PlantSpeciesBean targetPS = getPlantSpecies(targetItem);
//            if (srcPS == null)
//                return false;
//            if (targetPS == null)
//                return false;
            if ( (srcPS == null) ^ (targetPS == null))  //  exclusive OR
                return false;
            final PlantVarietyBean srcPV = getPlantVariety(srcItem);
            final PlantVarietyBean targetPV = getPlantVariety(targetItem);
            LOGGER.debug("onDragOverImpl(): srcPS: {}, targetPS: {}", srcPS, targetPS);
            LOGGER.debug("onDragOverImpl(): srcPV: {}, targetPV: {}", srcPV, targetPV);

            if (srcPS != null && !srcPS.sameAs(targetPS))
                return false;
            // PlantVariety in src and target must either both be null or be the same
            if ( (srcPV == null && targetPV != null) ||
                    (srcPV != null && targetPV == null) )
                return false;
            if (srcPV != null && !srcPV.sameAs(targetPV))
                return false;
            LOGGER.debug("onDragOverImpl(): plants valid");

            // cannot link a SaleItem which already has an ancestor; SaleItems cannot have descendants
            if (srcType == NotebookEntryType.SALEITEM && srcAnc)
                return false;
            if (targetType == NotebookEntryType.SALEITEM && targetAnc)
                return false;
            LOGGER.debug("onDragOverImpl(): after check SALEITEM history");
            
            // if either is a SaleItem we can now accept the drop if the dates are OK
            if (srcType == NotebookEntryType.SALEITEM)
            {
                return !targetDate.isAfter(srcDate);
            }
            else if (targetType == NotebookEntryType.SALEITEM)
            {
                return !srcDate.isAfter(targetDate);
            }

            //  one of the two MUST be freestanding
            if (srcAnc && srcDesc && targetAnc && targetDesc)
                return false;
            LOGGER.debug("onDragOverImpl(): leaf detected");

            // now decide if it is possible to decide which is supposed to be the new mother and which the newly adopted daughter
            if (srcDate.isBefore(targetDate))
            {// src must be the new mother, so target must be freestanding
                if (targetAnc || targetDesc)
                    return false;
                // Purchase cannot be AFTER any other event
                if (targetType == NotebookEntryType.PURCHASEITEM)
                    return false;
            }
            else if (srcDate.isAfter(targetDate))
            {// target must be the new mother
                if (srcAnc || srcDesc)
                    return false;
                // Purchase cannot be AFTER any other event
                if (srcType == NotebookEntryType.PURCHASEITEM)
                    return false;
            }
            else
            {// they are on the same date
                // cannot determine a sequence of like events!
                if (srcType == targetType)
                    return false;
                if (srcType == NotebookEntryType.PURCHASEITEM)
                {// PI is always the mother
                    if (targetAnc || targetDesc)
                        return false;                    
                }
                if (targetType == NotebookEntryType.PURCHASEITEM)
                {// PI is always the mother
                    if (srcAnc || srcDesc)
                        return false;                    
                }
            }
            LOGGER.debug("onDragOverImpl(): ordering possible");
            return true;
        }
        return false;
    }   //  onDragOverImpl()
    
    private boolean onDragDroppedImpl(DragEvent eh)
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("onDragDroppedImpl()");

        // in here, row is the drop TARGET
        if (row.isEmpty()) return false;
        final IndexedDiaryBean bean = row.getItem();
        if (bean == null) return false;
        final DiaryBean targetItem = bean.getItem();
        if (targetItem == null) return false;
        LOGGER.debug("onDragDroppedImpl(): row valid");

        final NotebookEntryType targetType = targetItem.getItemType();

        Dragboard db = eh.getDragboard();
        if ( db.hasString() && 
            (db.getString() != null) &&
            (!db.getString().isEmpty()) &&
            (db.getString().startsWith("diaryTable:")))
        {
            final int srcRowIx = Integer.parseInt(db.getString().substring(11));
            final DiaryBean srcItem = (row.getTableView().getItems().get(srcRowIx)).getItem();
            final NotebookEntryType srcType = srcItem.getItemType();
            Alert checkLink = new Alert(Alert.AlertType.CONFIRMATION, resources.getString("alert.confirmlink"), ButtonType.NO, ButtonType.YES);
            Optional<ButtonType> result = checkLink.showAndWait();
            LOGGER.debug("after link dialog: result:{}, result.get:{}",result, result.get());
            if (result.isPresent() && result.get() == ButtonType.YES)
            {
                LOGGER.debug("after link confirmed: bean type: {}", targetItem.getItemType());
//            }
                final LocalDate srcDate = srcItem.getDate();
                final LocalDate targetDate = targetItem.getDate();

                //  if src or target is a SALE, special handling is needed
                if (targetType == NotebookEntryType.SALE || srcType == NotebookEntryType.SALE)
                {
                    SaleBean sale = null;
                    PlantSpeciesBean psBean = null;
                    PlantVarietyBean pvBean = null;
                    DiaryBean mother = null;
                    if (targetType == NotebookEntryType.SALE)
                    {
                        mother = srcItem;
                        sale = (SaleBean)targetItem.getItem();
                        if (targetDate.isBefore(srcDate))
                            return false;
//                        psBean = getPlantSpecies(srcItem);
//                        if ( psBean == null)
//                            return false;
//                        pvBean = getPlantVariety(srcItem);
                    }
                    else
                    {// src is Sale
                        mother = targetItem;
                        sale = (SaleBean)srcItem.getItem();
                        if (srcDate.isBefore(targetDate))
                            return false;
//                        psBean = getPlantSpecies(targetItem);
//                        if ( psBean == null)
//                            return false;
//                        pvBean = getPlantVariety(targetItem);
                    }

                    final SaleBean saleX = sale;
//                    final PlantSpeciesBean psBeanX = psBean;
//                    final PlantVarietyBean pvBeanX = pvBean;
                    final DiaryBean motherX = mother;
                    DiaryBean finalMother = mother;
                    Platform.runLater(() -> {
//                        final SaleEditor tabCon = new SaleEditor(saleX, psBeanX, pvBeanX);
                        final SaleEditor tabCon = new SaleEditor(saleX, finalMother.getItem());
                        loadTab.accept(resources.getString("tab.sales"), tabCon);
                        tabCon.newLinkedItemBeanProperty().addListener((obs, oldVal, newVal) -> {
                            LOGGER.debug("onDragDroppedImpl(): Sale: newBean listener: newVal: {}", newVal);
                            try {
                                switch (motherX.getItemType())
                                {
                                    case HUSBANDRY:
                                        newVal.setAncestor((HusbandryBean)(motherX.getItem()));
                                        break;
                                    case PURCHASEITEM:
                                        break;
                                    case GROUNDWORK:
                                        newVal.setAncestor((GroundworkBean)(motherX.getItem()));
                                        break;
                                    case AFFLICTIONEVENT:
                                        newVal.setAncestor((AfflictionEventBean)(motherX.getItem()));
                                        break;
                                }
                            } catch (GNDBException ex) {
                                PanicHandler.panic(ex);
                            }
                        });
                    });

                    return true;
                }
                LOGGER.trace("onDragDroppedImpl(): not a SALE");

                // we know the drop is legitimate, we just need to sort out mother and daughter
                DiaryBean mother = null;
                DiaryBean daughter = null;
                if (srcType == NotebookEntryType.PURCHASEITEM)
                {
                    mother = srcItem;
                    daughter = targetItem;
                }
                else if (targetType == NotebookEntryType.PURCHASEITEM)
                {
                    mother = targetItem;
                    daughter = srcItem;
                }
                else if (targetType == NotebookEntryType.SALEITEM)
                {
                    mother = srcItem;
                    daughter = targetItem;
                }
                else if (srcType == NotebookEntryType.SALEITEM)
                {
                    mother = targetItem;
                    daughter = srcItem;
                }
                // both rows are HU, GW or AE
                else if (srcDate.isAfter(targetDate))
                {//dragged later activity over earlier
                    mother = targetItem;
                    daughter = srcItem;
                }
                else if (targetDate.isAfter(srcDate))
                {//dragged earlier activity over later
                    mother = srcItem;
                    daughter = targetItem;
                }
                else
                {// HU before AE before GW - we know they're not both the same
                    if (srcType == NotebookEntryType.HUSBANDRY)
                    {
                        mother = srcItem;
                        daughter = targetItem;
                    }
                    else if (targetType == NotebookEntryType.HUSBANDRY)
                    {
                        mother = targetItem;
                        daughter = srcItem;
                    }
                    // both are GW or AE - and it's one of each
                    else if (srcType == NotebookEntryType.GROUNDWORK)                    
                    {
                        mother = srcItem;
                        daughter = targetItem;
                    }
                    else
                    {
                        mother = targetItem;
                        daughter = srcItem;
                    }                
                }
                if (mother == null || daughter == null)
                    return false;
                LOGGER.trace("onDragDroppedImpl(): mother: {}, daughter: {}", mother, daughter);

                // and make the link!
                adoptChild(mother, daughter);
            }
        }
        return true;
        
    }   //  onDragDroppedImpl()
    
    private PlantSpeciesBean getPlantSpecies(final DiaryBean item)
    {
        final NotebookEntryType type = item.getItemType();
        PlantSpeciesBean itemPS = null;
        switch (type)
        {
            case AFFLICTIONEVENT -> {
                AfflictionEventBean srcAE = ((AfflictionEventBean) item.getItem());
                itemPS = srcAE.getPlantSpecies();
            }
            case GROUNDWORK -> {
                GroundworkBean srcGW = ((GroundworkBean) item.getItem());
                itemPS = srcGW.getPlantSpecies();
            }
            case HUSBANDRY -> {
                HusbandryBean srcHU = ((HusbandryBean) item.getItem());
                itemPS = srcHU.getPlantSpecies();
            }
            case PURCHASEITEM -> {
                ProductBean srcProd = ((PurchaseItemBean) item.getItem()).getProduct();
                if (srcProd.hasPlantSpecies())
                {
                    itemPS = srcProd.getPlantSpecies();
                }
            }
            case SALEITEM -> {
                SaleItemBean srcSI = ((SaleItemBean) item.getItem());
                itemPS = srcSI.getPlantSpecies();
            }
        }
        return itemPS;
    }   //  getPlantSpecies()
    
    private PlantVarietyBean getPlantVariety(final DiaryBean item)
    {
        final NotebookEntryType type = item.getItemType();
        PlantVarietyBean itemPV = null;
        switch (type)
        {
            case AFFLICTIONEVENT -> {
                AfflictionEventBean srcAE = ((AfflictionEventBean) item.getItem());
                if (srcAE.hasPlantVariety())
                {
                    itemPV = srcAE.getPlantVariety();
                }
            }
            case GROUNDWORK -> {
                GroundworkBean srcGW = ((GroundworkBean) item.getItem());
                if (srcGW.hasPlantVariety())
                {
                    itemPV = srcGW.getPlantVariety();
                }
            }
            case HUSBANDRY -> {
                HusbandryBean srcHU = ((HusbandryBean) item.getItem());
                if (srcHU.hasPlantVariety())
                {
                    itemPV = srcHU.getPlantVariety();
                }
            }
            case PURCHASEITEM -> {
                ProductBean srcProd = ((PurchaseItemBean) item.getItem()).getProduct();
                if (srcProd.hasPlantSpecies())
                {
                    if (srcProd.hasPlantVariety())
                    {
                        itemPV = srcProd.getPlantVariety();
                    }
                }
            }
            case SALEITEM -> {
                SaleItemBean srcSI = ((SaleItemBean) item.getItem());
                if (srcSI.hasPlantVariety())
                {
                    itemPV = srcSI.getPlantVariety();
                }
            }
        }
        return itemPV;
    }   //  getPlantVariety
    
    /**
     *  This is messy; there must be a better way.
     * 
     * @param mother    the Bean wrapping the item which will become the daughter's immediate ancestor
     * @param daughter  the Bean wrapping a leaf node item which will be linked to the mother's storyline
     */
    private void adoptChild(DiaryBean mother, DiaryBean daughter)
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("adoptChild(): mother: {}, daughter: {}", mother, daughter);

        final NotebookEntryType motherType = mother.getItemType();
        final NotebookEntryType daughterType = daughter.getItemType();
        switch (daughterType)
        {
            case AFFLICTIONEVENT -> {
                final AfflictionEventBean sprogAE = (AfflictionEventBean) (daughter.getItem());
                try
                {
                    switch (motherType)
                    {
                        case HUSBANDRY -> sprogAE.setAncestor((HusbandryBean) (mother.getItem()));
                        case PURCHASEITEM -> sprogAE.setAncestor((PurchaseItemBean) (mother.getItem()));
                        case GROUNDWORK -> sprogAE.setAncestor((GroundworkBean) (mother.getItem()));
                        case AFFLICTIONEVENT -> sprogAE.setAncestor((AfflictionEventBean) (mother.getItem()));
                        default -> {
                            LOGGER.debug("adoptChild(): illegal parent type: {}", mother.getItem());
                            return;
                        }
                    }
                } catch (GNDBException ex)
                {
                    PanicHandler.panic(ex);
                }
            }
            case GROUNDWORK -> {
                final GroundworkBean sprogGW = (GroundworkBean) (daughter.getItem());
                try
                {
                    switch (motherType)
                    {
                        case HUSBANDRY -> sprogGW.setAncestor((HusbandryBean) (mother.getItem()));
                        case PURCHASEITEM -> sprogGW.setAncestor((PurchaseItemBean) (mother.getItem()));
                        case GROUNDWORK -> sprogGW.setAncestor((GroundworkBean) (mother.getItem()));
                        case AFFLICTIONEVENT -> sprogGW.setAncestor((AfflictionEventBean) (mother.getItem()));
                        default -> {
                            LOGGER.debug("adoptChild(): illegal parent type: {}", mother.getItem());
                            return;
                        }
                    }
                } catch (GNDBException ex)
                {
                    PanicHandler.panic(ex);
                }
            }
            case HUSBANDRY -> {
                final HusbandryBean sprogHU = (HusbandryBean) (daughter.getItem());
                try
                {
                    switch (motherType)
                    {
                        case HUSBANDRY -> sprogHU.setAncestor((HusbandryBean) (mother.getItem()));
                        case PURCHASEITEM -> sprogHU.setAncestor((PurchaseItemBean) (mother.getItem()));
                        case GROUNDWORK -> sprogHU.setAncestor((GroundworkBean) (mother.getItem()));
                        case AFFLICTIONEVENT -> sprogHU.setAncestor((AfflictionEventBean) (mother.getItem()));
                        default -> {
                            LOGGER.debug("adoptChild(): illegal parent type: {}", mother.getItem());
                            return;
                        }
                    }
                } catch (GNDBException ex)
                {
                    PanicHandler.panic(ex);
                }
            }
            case SALEITEM -> {
                final SaleItemBean sprogSI = (SaleItemBean) (daughter.getItem());
                try
                {
                    switch (motherType)
                    {
                        case HUSBANDRY -> sprogSI.setAncestor((HusbandryBean) (mother.getItem()));
                        case GROUNDWORK -> sprogSI.setAncestor((GroundworkBean) (mother.getItem()));
                        case AFFLICTIONEVENT -> sprogSI.setAncestor((AfflictionEventBean) (mother.getItem()));
                        default -> {
                            LOGGER.debug("adoptChild(): illegal parent type: {}", mother.getItem());
                            return;
                        }
                    }
                } catch (GNDBException ex)
                {
                    PanicHandler.panic(ex);
                }
            }
        }
    }   //  adoptChild()
    
}
