MoneyWiseReportTab.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.lethe.ui.panel;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
import io.github.tonywasher.joceanus.oceanus.event.OceanusEvent;
import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
import io.github.tonywasher.joceanus.metis.report.MetisReportEvent;
import io.github.tonywasher.joceanus.metis.report.MetisReportHTMLBuilder;
import io.github.tonywasher.joceanus.metis.report.MetisReportManager;
import io.github.tonywasher.joceanus.metis.ui.MetisErrorPanel;
import io.github.tonywasher.joceanus.metis.viewer.MetisViewerEntry;
import io.github.tonywasher.joceanus.metis.viewer.MetisViewerManager;
import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisManager;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
import io.github.tonywasher.joceanus.moneywise.lethe.reports.MoneyWiseReportBuilder;
import io.github.tonywasher.joceanus.moneywise.lethe.reports.MoneyWiseReportStyleSheet;
import io.github.tonywasher.joceanus.moneywise.lethe.reports.MoneyWiseReportType;
import io.github.tonywasher.joceanus.moneywise.lethe.ui.controls.MoneyWiseAnalysisSelect.MoneyWiseStatementSelect;
import io.github.tonywasher.joceanus.moneywise.lethe.ui.controls.MoneyWiseReportSelect;
import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter;
import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter.MoneyWiseAnalysisSecurityFilter;
import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseGoToId;
import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
import io.github.tonywasher.joceanus.prometheus.ui.PrometheusGoToEvent;
import io.github.tonywasher.joceanus.prometheus.views.PrometheusDataEvent;
import io.github.tonywasher.joceanus.prometheus.views.PrometheusViewerEntryId;
import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
import io.github.tonywasher.joceanus.tethys.api.button.TethysUIDateRangeSelector;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUIHTMLManager;
import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBorderPaneManager;
import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIPaneFactory;
import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIScrollPaneManager;
import org.w3c.dom.Document;

/**
 * Report panel.
 */
public class MoneyWiseReportTab
        implements OceanusEventProvider<PrometheusDataEvent>, TethysUIComponent {
    /**
     * Text for DataEntry Title.
     */
    private static final String NLS_DATAENTRY = MoneyWiseUIResource.REPORT_DATAENTRY.getValue();

    /**
     * The Event Manager.
     */
    private final OceanusEventManager<PrometheusDataEvent> theEventManager;

    /**
     * The Data View.
     */
    private final MoneyWiseView theView;

    /**
     * The Panel.
     */
    private final TethysUIBorderPaneManager thePanel;

    /**
     * The HTML pane.
     */
    private final TethysUIHTMLManager theHTMLPane;

    /**
     * The Report selection Panel.
     */
    private final MoneyWiseReportSelect theSelect;

    /**
     * The Spot Analysis Entry.
     */
    private final MetisViewerEntry theSpotEntry;

    /**
     * The Error Panel.
     */
    private final MetisErrorPanel theError;

    /**
     * The Report Manager.
     */
    private final MetisReportManager<MoneyWiseAnalysisFilter<?, ?>> theManager;

    /**
     * The ReportBuilder.
     */
    private final MoneyWiseReportBuilder theBuilder;

    /**
     * Constructor for Report Window.
     *
     * @param pView the data view
     * @throws OceanusException on error
     */
    public MoneyWiseReportTab(final MoneyWiseView pView) throws OceanusException {
        /* Store the view */
        theView = pView;

        /* Access GUI Factory */
        final TethysUIFactory<?> myFactory = pView.getGuiFactory();

        /* Create the event manager */
        theEventManager = new OceanusEventManager<>();

        /* Create the Panel */
        final TethysUIPaneFactory myPanes = myFactory.paneFactory();
        thePanel = myPanes.newBorderPane();

        /* Create the top level debug entry for this view */
        final MetisViewerManager myDataMgr = theView.getViewerManager();
        final MetisViewerEntry mySection = theView.getViewerEntry(PrometheusViewerEntryId.VIEW);
        final MetisViewerEntry myReport = myDataMgr.newEntry(mySection, NLS_DATAENTRY);
        theSpotEntry = myDataMgr.newEntry(myReport, PrometheusViewerEntryId.ANALYSIS.toString());
        theSpotEntry.setVisible(false);

        /* Create the HTML Pane */
        theHTMLPane = myFactory.controlFactory().newHTMLManager();

        /* Create Report Manager */
        theManager = new MetisReportManager<>(new MetisReportHTMLBuilder(pView.getDataFormatter()));

        /* Create the report builder */
        theBuilder = new MoneyWiseReportBuilder(theManager);

        /* Create the Report Selection panel */
        theSelect = new MoneyWiseReportSelect(myFactory);

        /* Create the error panel for this view */
        theError = theView.getToolkit().getToolkit().newErrorPanel(myReport);

        /* Create a scroll pane */
        final TethysUIScrollPaneManager myHTMLScroll = myPanes.newScrollPane();
        myHTMLScroll.setContent(theHTMLPane);

        /* Create the header panel */
        final TethysUIBorderPaneManager myHeader = myPanes.newBorderPane();
        myHeader.setCentre(theSelect);
        myHeader.setNorth(theError);

        /* Now define the panel */
        thePanel.setNorth(myHeader);
        thePanel.setCentre(myHTMLScroll);

        /* Load the CSS */
        theHTMLPane.setCSSContent(MoneyWiseReportStyleSheet.CSS_REPORT);

        /* Create listeners */
        theView.getEventRegistrar().addEventListener(e -> refreshData());
        theManager.getEventRegistrar().addEventListener(this::handleGoToRequest);
        theError.getEventRegistrar().addEventListener(e -> handleErrorPane());
        final OceanusEventRegistrar<PrometheusDataEvent> myRegistrar = theSelect.getEventRegistrar();
        myRegistrar.addEventListener(PrometheusDataEvent.SELECTIONCHANGED, e -> handleReportRequest());
        myRegistrar.addEventListener(PrometheusDataEvent.PRINT, e -> theHTMLPane.printIt());
        myRegistrar.addEventListener(PrometheusDataEvent.SAVETOFILE, e -> theHTMLPane.saveToFile());
        theHTMLPane.getEventRegistrar().addEventListener(TethysUIEvent.BUILDPAGE, e -> {
            theManager.processReference(e.getDetails(String.class), theHTMLPane);
            e.consume();
        });
    }

    @Override
    public TethysUIComponent getUnderlying() {
        return thePanel;
    }

    @Override
    public OceanusEventRegistrar<PrometheusDataEvent> getEventRegistrar() {
        return theEventManager.getEventRegistrar();
    }

    @Override
    public void setEnabled(final boolean pEnabled) {
        /* Pass on to important elements */
        theSelect.setEnabled(pEnabled);
        theError.setEnabled(pEnabled);
        theHTMLPane.setEnabled(pEnabled);
    }

    @Override
    public void setVisible(final boolean pVisible) {
        thePanel.setVisible(pVisible);
    }

    /**
     * Refresh views/controls after a load/update of underlying data.
     */
    private void refreshData() {
        /* Obtain the active profile */
        OceanusProfile myTask = theView.getActiveTask();
        myTask = myTask.startTask("Reports");

        /* Protect against exceptions */
        try {
            /* Hide the instant debug since it is now invalid */
            theSpotEntry.setVisible(false);

            /* Refresh the data */
            theSelect.setRange(theView.getRange());
            theSelect.setSecurities(theView.hasActiveSecurities());
            buildReport();

            /* Create SavePoint */
            theSelect.createSavePoint();
        } catch (OceanusException e) {
            /* Show the error */
            theView.addError(e);

            /* Restore SavePoint */
            theSelect.restoreSavePoint();
        }

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

    /**
     * Build the report.
     *
     * @throws OceanusException on error
     */
    private void buildReport() throws OceanusException {
        /* Access the values from the selection */
        final MoneyWiseReportType myReportType = theSelect.getReportType();
        final OceanusDateRange myRange = theSelect.getDateRange();
        final MoneyWiseAnalysisManager myManager = theView.getAnalysisManager();
        final MoneyWiseAnalysisSecurityBucket mySecurity = theSelect.getSecurity();

        /* set lockDown of selection */
        theSelect.setEnabled(true);

        /* Skip if we have no analysis */
        if (myManager.isIdle()) {
            theHTMLPane.setHTMLContent("", "");
            return;
        }

        /* Access the appropriate analysis */
        final MoneyWiseAnalysis myAnalysis = myReportType.isPointInTime()
                ? myManager.getDatedAnalysis(myRange.getEnd())
                : myManager.getRangedAnalysis(myRange);

        /* Record analysis and build report */
        theSelect.setAnalysis(myAnalysis);
        final Document myDoc = theBuilder.createReport(myAnalysis, myReportType, mySecurity);

        /* Declare to debugger */
        theSpotEntry.setObject(myAnalysis);
        theSpotEntry.setVisible(true);

        /* Declare the document */
        theManager.setDocument(myDoc);

        /* Create initial display version */
        final String myText = theManager.formatXML();
        theHTMLPane.setHTMLContent(myText, "");
    }

    /**
     * handleErrorPane.
     */
    private void handleErrorPane() {
        /* Determine whether we have an error */
        final boolean isError = theError.hasError();

        /* Hide selection panel on error */
        theSelect.setVisible(!isError);

        /* Lock HTML area */
        theHTMLPane.setEnabled(!isError);
    }

    /**
     * handleGoToRequest.
     *
     * @param pEvent the event
     */
    private void handleGoToRequest(final OceanusEvent<MetisReportEvent> pEvent) {
        /* Access the filter */
        final MoneyWiseAnalysisFilter<?, ?> myFilter = pEvent.getDetails(MoneyWiseAnalysisFilter.class);

        /* If we are currently showing Asset Gains */
        if (MoneyWiseReportType.ASSETGAINS.equals(theSelect.getReportType())) {
            /* Select the capital gains report */
            final MoneyWiseAnalysisSecurityBucket myBucket = ((MoneyWiseAnalysisSecurityFilter) myFilter).getBucket();
            theSelect.setSecurity(myBucket);

            /* else we are selecting a statement */
        } else {
            /* Create the details of the report */
            final TethysUIDateRangeSelector mySelect = theSelect.getDateRangeSelector();
            final MoneyWiseStatementSelect myStatement = new MoneyWiseStatementSelect(mySelect, myFilter);

            /* Request the action */
            theEventManager.fireEvent(PrometheusDataEvent.GOTOWINDOW, new PrometheusGoToEvent<>(MoneyWiseGoToId.STATEMENT, myStatement));
        }
    }

    /**
     * handleReportRequest.
     */
    private void handleReportRequest() {
        /* Protect against exceptions */
        try {
            /* build the report */
            buildReport();

            /* Create SavePoint */
            theSelect.createSavePoint();

            /* Catch Exceptions */
        } catch (OceanusException e) {
            /* Build the error */
            final OceanusException myError = new MoneyWiseDataException("Failed to change selection", e);

            /* Show the error */
            theError.addError(myError);
            handleErrorPane();

            /* Restore SavePoint */
            theSelect.restoreSavePoint();
        }
    }
}