MoneyWiseStaticTable.java

/*
 * MoneyWise: Finance Application
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.moneywise.ui.panel;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
import io.github.tonywasher.joceanus.metis.ui.MetisAction;
import io.github.tonywasher.joceanus.metis.ui.MetisErrorPanel;
import io.github.tonywasher.joceanus.metis.ui.MetisIcon;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
import io.github.tonywasher.joceanus.moneywise.ui.base.MoneyWiseBaseTable;
import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList.PrometheusListStyle;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusStaticDataClass;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusStaticDataItem;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusStaticDataItem.PrometheusStaticList;
import io.github.tonywasher.joceanus.prometheus.ui.PrometheusIcon;
import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
import io.github.tonywasher.joceanus.tethys.api.base.TethysUIGenericWrapper;
import io.github.tonywasher.joceanus.tethys.api.button.TethysUIScrollButtonManager;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControl.TethysUIIconMapSet;
import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableColumn;
import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableManager;

/**
 * MoneyWise Static Table.
 *
 * @param <L> the list type
 * @param <T> the data type
 */
public class MoneyWiseStaticTable<L extends PrometheusStaticList<T>, T extends PrometheusStaticDataItem>
        extends MoneyWiseBaseTable<T> {
    /**
     * Class column width.
     */
    private static final int WIDTH_CLASS = 90;

    /**
     * The list class.
     */
    private final Class<L> theClass;

    /**
     * The enabled column.
     */
    private final TethysUITableColumn<Boolean, MetisDataFieldId, T> theEnabledColumn;

    /**
     * The new button.
     */
    private final TethysUIScrollButtonManager<TethysUIGenericWrapper> theNewButton;

    /**
     * The edit list.
     */
    private L theStatic;

    /**
     * show disabled.
     */
    private boolean showAll;

    /**
     * Constructor.
     *
     * @param pView      the view
     * @param pEditSet   the editSet
     * @param pError     the error panel
     * @param pDataType  the dataType
     * @param pListClass the listClass
     */
    MoneyWiseStaticTable(final MoneyWiseView pView,
                         final PrometheusEditSet pEditSet,
                         final MetisErrorPanel pError,
                         final MoneyWiseStaticDataType pDataType,
                         final Class<L> pListClass) {
        /* Store parameters */
        super(pView, pEditSet, pError, pDataType);
        theClass = pListClass;

        /* Access the gui factory */
        final TethysUIFactory<?> myGuiFactory = pView.getGuiFactory();
        final TethysUITableManager<MetisDataFieldId, T> myTable = getTable();

        /* Create new button */
        theNewButton = myGuiFactory.buttonFactory().newScrollButton(TethysUIGenericWrapper.class);
        MetisIcon.configureNewScrollButton(theNewButton);

        /* Set table configuration */
        myTable.setDisabled(PrometheusStaticDataItem::isDisabled)
                .setComparator(PrometheusStaticDataItem::compareTo);

        /* Create the class column */
        myTable.declareStringColumn(PrometheusDataResource.STATICDATA_CLASS)
                .setCellValueFactory(r -> r.getStaticClass().toString())
                .setEditable(false)
                .setColumnWidth(WIDTH_CLASS);

        /* Create the name column */
        myTable.declareStringColumn(PrometheusDataResource.DATAITEM_FIELD_NAME)
                .setValidator(this::isValidName)
                .setCellValueFactory(PrometheusStaticDataItem::getName)
                .setEditable(true)
                .setColumnWidth(WIDTH_NAME)
                .setOnCommit((r, v) -> updateField(PrometheusStaticDataItem::setName, r, v));

        /* Create the description column */
        myTable.declareStringColumn(PrometheusDataResource.DATAITEM_FIELD_DESC)
                .setValidator(this::isValidDesc)
                .setCellValueFactory(PrometheusStaticDataItem::getDesc)
                .setEditable(true)
                .setColumnWidth(WIDTH_DESC)
                .setOnCommit((r, v) -> updateField(PrometheusStaticDataItem::setDescription, r, v));

        /* Create the enabled column */
        final TethysUIIconMapSet<Boolean> myEnabledMapSet = PrometheusIcon.configureEnabledIconButton(myGuiFactory);
        theEnabledColumn = myTable.declareIconColumn(PrometheusDataResource.STATICDATA_ENABLED, Boolean.class)
                .setIconMapSet(r -> myEnabledMapSet)
                .setCellValueFactory(PrometheusStaticDataItem::getEnabled)
                .setVisible(false)
                .setEditable(true)
                .setCellEditable(r -> !r.isActive())
                .setColumnWidth(WIDTH_ICON)
                .setOnCommit((r, v) -> updateField(PrometheusStaticDataItem::setEnabled, r, v));

        /* Create the Active column */
        final TethysUIIconMapSet<MetisAction> myActionMapSet = MetisIcon.configureStatusIconButton(myGuiFactory);
        myTable.declareIconColumn(PrometheusDataResource.DATAITEM_TOUCH, MetisAction.class)
                .setIconMapSet(r -> myActionMapSet)
                .setCellValueFactory(r -> r.isActive() ? MetisAction.ACTIVE : MetisAction.DELETE)
                .setName(MoneyWiseUIResource.STATICDATA_ACTIVE.getValue())
                .setEditable(true)
                .setCellEditable(r -> !r.isActive())
                .setColumnWidth(WIDTH_ICON)
                .setOnCommit((r, v) -> updateField(this::deleteRow, r, v));

        /* Add listeners */
        final OceanusEventRegistrar<TethysUIEvent> myRegistrar = theNewButton.getEventRegistrar();
        myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewClass());
        theNewButton.setMenuConfigurator(e -> buildNewMenu());
        setShowAll(false);
    }

    /**
     * Obtain the new button.
     *
     * @return the new Button
     */
    TethysUIScrollButtonManager<TethysUIGenericWrapper> getNewButton() {
        return theNewButton;
    }

    @Override
    protected void refreshData() throws OceanusException {
        final MoneyWiseDataSet myData = getView().getData();
        final PrometheusStaticList<T> myStatic = myData.getDataList(theClass);
        theStatic = theClass.cast(myStatic.deriveList(PrometheusListStyle.EDIT));
        theStatic.mapData();
        getTable().setItems(theStatic.getUnderlyingList());
        getEditEntry().setDataList(theStatic);
        restoreSelected();
    }

    /**
     * handle new static class.
     */
    private void handleNewClass() {
        /* Create a new profile */
        final OceanusProfile myTask = getView().getNewProfile("addNewClass");

        /* Access the new class */
        cancelEditing();
        final PrometheusStaticDataClass myClass = (PrometheusStaticDataClass) theNewButton.getValue().getData();

        /* Protect the action */
        try {
            /* Look to find a deleted value */
            myTask.startTask("addToList");
            T myValue = theStatic.findItemByClass(myClass);

            /* If we found a deleted value */
            if (myValue != null) {
                /* reinstate it */
                myValue.setDeleted(false);

                /* else we have no existing value */
            } else {
                /* Create the new value */
                myValue = theStatic.addNewItem(myClass);
                myValue.setNewVersion();
                myValue.adjustMapForItem();
            }

            /* Update the table */
            myTask.startTask("incrementVersion");
            getEditSet().incrementVersion();
            updateTableData();
            selectItem(myValue);
            notifyChanges();

            /* Handle exceptions */
        } catch (OceanusException e) {
            setError(e);
        }

        /* End the task */
        myTask.end();
    }

    /**
     * Build the menu of available new items.
     */
    private void buildNewMenu() {
        /* Reset the menu popUp */
        final TethysUIScrollMenu<TethysUIGenericWrapper> myMenu = theNewButton.getMenu();
        myMenu.removeAllItems();

        /* Loop through the missing classes */
        for (PrometheusStaticDataClass myValue : theStatic.getMissingClasses()) {
            /* Create a new MenuItem and add it to the popUp */
            myMenu.addItem(new TethysUIGenericWrapper(myValue));
        }
    }

    /**
     * Select static data.
     *
     * @param pStatic the static data
     */
    @SuppressWarnings("unchecked")
    void selectStatic(final PrometheusStaticDataItem pStatic) {
        getTable().selectRow((T) pStatic);
    }

    /**
     * Is the static table full?
     *
     * @return true/false
     */
    boolean isFull() {
        return theStatic == null
                || theStatic.isFull();
    }

    /**
     * adjust showALL.
     *
     * @param pShow show disabled entries
     */
    void setShowAll(final boolean pShow) {
        showAll = pShow;
        cancelEditing();
        getTable().setFilter(this::isFiltered);
        theEnabledColumn.setVisible(showAll);
        restoreSelected();
    }

    @Override
    protected boolean isFiltered(final T pRow) {
        return super.isFiltered(pRow) && (pRow.getEnabled() || showAll);
    }
}