MoneyWiseSpotExchangeRate.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.views;

import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
import io.github.tonywasher.joceanus.metis.data.MetisDataEditState;
import io.github.tonywasher.joceanus.metis.data.MetisDataState;
import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionValues;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseExchangeRate;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency.MoneyWiseCurrencyList;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues;

import java.util.Iterator;
import java.util.ListIterator;

/**
 * Extension of ExchangeRate to cater for spot rates.
 */
public final class MoneyWiseSpotExchangeRate
        extends MoneyWiseExchangeRate {
    /**
     * Object name.
     */
    public static final String OBJECT_NAME = MoneyWiseSpotExchangeRate.class.getSimpleName();

    /**
     * List name.
     */
    public static final String LIST_NAME = OBJECT_NAME + "s";

    /**
     * Report fields.
     */
    @SuppressWarnings("rawtypes")
    private static final MetisFieldSet<MoneyWiseSpotExchangeRate> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSpotExchangeRate.class);

    /*
     * The fields.
     */
    static {
        FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTEVENT_PREVDATE, MoneyWiseSpotExchangeRate::getPrevDate);
        FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTRATE_PREVRATE, MoneyWiseSpotExchangeRate::getPrevRate);
    }

    /**
     * the previous date.
     */
    private OceanusDate thePrevDate;

    /**
     * the previous rate.
     */
    private OceanusRatio thePrevRate;

    /**
     * Constructor for a new SpotRate where no rate data exists.
     *
     * @param pList     the Spot Rate List
     * @param pCurrency the currency
     */
    private MoneyWiseSpotExchangeRate(final MoneyWiseSpotExchangeList pList,
                                      final MoneyWiseCurrency pCurrency) {
        super(pList);

        /* Store base values */
        setDate(pList.theDate);
        setFromCurrency(pList.getCurrency());
        setToCurrency(pCurrency);
    }

    @Override
    public MetisFieldSetDef getDataFieldSet() {
        return FIELD_DEFS;
    }

    /**
     * Obtain previous rate.
     *
     * @return the rate.
     */
    public OceanusRatio getPrevRate() {
        return thePrevRate;
    }

    /**
     * Obtain previous date.
     *
     * @return the date.
     */
    public OceanusDate getPrevDate() {
        return thePrevDate;
    }

    /**
     * Validate the line.
     */
    @Override
    public void validate() {
        setValidEdit();
    }

    /* Is this row locked */
    @Override
    public boolean isLocked() {
        return isDeleted();
    }

    /**
     * Note that this item has been validated.
     */
    @Override
    public void setValidEdit() {
        setEditState(hasHistory()
                ? MetisDataEditState.VALID
                : MetisDataEditState.CLEAN);
    }

    @Override
    public OceanusRatio getExchangeRate() {
        /* Switch on state */
        switch (getState()) {
            case NEW:
            case CHANGED:
            case RECOVERED:
            case CLEAN:
                return super.getExchangeRate();
            default:
                return null;
        }
    }

    @Override
    public MetisDataState getState() {
        final MetisFieldVersionValues myCurr = getValues();
        final MetisFieldVersionValues myBase = getOriginalValues();

        /* If we have no changes we are CLEAN */
        if (myCurr.getVersion() == 0) {
            return MetisDataState.CLEAN;
        }

        /* If the original rate is Null */
        if (myBase.getValue(MoneyWiseBasicResource.XCHGRATE_RATE) == null) {
            /* Return status */
            return myCurr.getValue(MoneyWiseBasicResource.XCHGRATE_RATE) == null
                    ? MetisDataState.DELNEW
                    : MetisDataState.NEW;
        }

        /* If we are deleted return so */
        return myCurr.getValue(MoneyWiseBasicResource.XCHGRATE_RATE) == null
                ? MetisDataState.DELETED
                : MetisDataState.CHANGED;
    }

    /**
     * The Spot Rates List class.
     */
    public static class MoneyWiseSpotExchangeList
            extends MoneyWiseExchangeRateBaseList<MoneyWiseSpotExchangeRate> {
        /**
         * Report fields.
         */
        @SuppressWarnings("rawtypes")
        private static final MetisFieldSet<MoneyWiseSpotExchangeList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSpotExchangeList.class);

        /*
         * The currency field Id.
         */
        static {
            FIELD_DEFS.declareLocalField(MoneyWiseStaticDataType.CURRENCY, MoneyWiseSpotExchangeList::getCurrency);
            FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, MoneyWiseSpotExchangeList::getDate);
            FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTEVENT_NEXTDATE, MoneyWiseSpotExchangeList::getNext);
            FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTEVENT_PREVDATE, MoneyWiseSpotExchangeList::getPrev);
        }

        /**
         * The date.
         */
        private final OceanusDate theDate;

        /**
         * The view.
         */
        private final MoneyWiseView theView;

        /**
         * The currency.
         */
        private final MoneyWiseCurrency theCurrency;

        /**
         * The next date.
         */
        private OceanusDate theNext;

        /**
         * The previous date.
         */
        private OceanusDate thePrev;

        /**
         * Constructor.
         *
         * @param pView the view
         * @param pDate the date
         */
        public MoneyWiseSpotExchangeList(final MoneyWiseView pView,
                                         final OceanusDate pDate) {
            /* Build initial list */
            super(pView.getData(), MoneyWiseSpotExchangeRate.class, MoneyWiseBasicDataType.SECURITYPRICE);
            setStyle(PrometheusListStyle.EDIT);
            ensureMap();

            /* Store parameters */
            theDate = pDate;
            theView = pView;

            /* Obtain the portfolio bucket */
            final MoneyWiseDataSet myData = theView.getData();
            theCurrency = myData.getReportingCurrency();
            final MoneyWiseCurrencyList myCurrencies = myData.getAccountCurrencies();

            /* Loop through the Currencies */
            final OceanusDate myDate = new OceanusDate(theDate);
            final Iterator<MoneyWiseCurrency> myCurIterator = myCurrencies.iterator();
            while (myCurIterator.hasNext()) {
                final MoneyWiseCurrency myCurrency = myCurIterator.next();

                /* Ignore deleted/disabled and default currency */
                boolean bIgnore = myCurrency.isDeleted() || myCurrency.isDisabled();
                bIgnore |= myCurrency.equals(theCurrency);
                if (bIgnore) {
                    continue;
                }

                /* Create a SpotRate entry */
                final MoneyWiseSpotExchangeRate mySpot = new MoneyWiseSpotExchangeRate(this, myCurrency);
                mySpot.setIndexedId(myCurrency.getIndexedId());
                mySpot.setDate(myDate);
                add(mySpot);
            }

            /* Set the base for this list */
            final MoneyWiseExchangeRateList myRates = myData.getExchangeRates();
            setBase(myRates);

            /* Loop through the rates */
            final ListIterator<MoneyWiseExchangeRate> myIterator = myRates.listIterator(myRates.size());
            while (myIterator.hasPrevious()) {
                final MoneyWiseExchangeRate myRate = myIterator.previous();

                /* Ignore deleted rates */
                if (myRate.isDeleted()) {
                    continue;
                }

                /* Test the Date */
                final int iDiff = theDate.compareTo(myRate.getDate());

                /* If we are past the date */
                if (iDiff < 0) {
                    /* Record the next date and break the loop */
                    theNext = myRate.getDate();
                    break;
                }

                /* Access the Spot Rate and ignore if not relevant */
                final MoneyWiseCurrency myCurrency = myRate.getToCurrency();
                final MoneyWiseSpotExchangeRate mySpot = findItemById(myCurrency.getIndexedId());
                if (mySpot == null) {
                    continue;
                }

                /* If we are exactly the date */
                if (iDiff == 0) {
                    /* Set rate */
                    mySpot.setValueExchangeRate(myRate.getExchangeRate());

                    /* Link to base and re-establish state */
                    mySpot.setBase(myRate);

                    /* else we are a previous date */
                } else {
                    /* Set previous date and value */
                    mySpot.thePrevDate = myRate.getDate();
                    mySpot.thePrevRate = myRate.getExchangeRate();

                    /* Record the latest previous date */
                    thePrev = myRate.getDate();
                }
            }
        }

        @SuppressWarnings("rawtypes")
        @Override
        public MetisFieldSet<MoneyWiseSpotExchangeList> getDataFieldSet() {
            return FIELD_DEFS;
        }

        @Override
        public String listName() {
            return MoneyWiseSpotExchangeList.class.getSimpleName();
        }

        @Override
        public MetisFieldSetDef getItemFields() {
            return MoneyWiseSpotExchangeRate.FIELD_DEFS;
        }

        @Override
        protected MoneyWiseSpotExchangeList getEmptyList(final PrometheusListStyle pStyle) {
            throw new UnsupportedOperationException();
        }

        @Override
        public MoneyWiseDataSet getDataSet() {
            return (MoneyWiseDataSet) super.getDataSet();
        }

        /**
         * Obtain the currency.
         *
         * @return the currency
         */
        private MoneyWiseCurrency getCurrency() {
            return theCurrency;
        }

        /**
         * Obtain the date.
         *
         * @return the date
         */
        private OceanusDate getDate() {
            return theDate;
        }

        /**
         * Obtain the next date.
         *
         * @return the date
         */
        public OceanusDate getNext() {
            return theNext;
        }

        /**
         * Obtain the previous date.
         *
         * @return the date
         */
        public OceanusDate getPrev() {
            return thePrev;
        }

        /* Is this list locked */
        @Override
        public boolean isLocked() {
            return false;
        }

        /* Disable Add a new item */
        @Override
        public MoneyWiseSpotExchangeRate addCopyItem(final PrometheusDataItem pElement) {
            throw new UnsupportedOperationException();
        }

        @Override
        public MoneyWiseSpotExchangeRate addNewItem() {
            throw new UnsupportedOperationException();
        }

        @Override
        public MoneyWiseSpotExchangeRate addValuesItem(final PrometheusDataValues pValues) {
            throw new UnsupportedOperationException();
        }
    }
}