001package jmri.jmrit.ctc;
002
003import java.beans.PropertyChangeListener;
004import java.util.ArrayList;
005
006import jmri.InstanceManager;
007import jmri.NamedBeanHandle;
008import jmri.NamedBeanHandleManager;
009import jmri.Turnout;
010import jmri.TurnoutManager;
011import jmri.jmrit.ctc.ctcserialdata.ProjectsCommonSubs;
012
013/**
014 * This object additionally supports "inverted feedback", so that when someone
015 * calls "getKnownState", (typically my own code in this CTC project) we return
016 * the "adjusted" value based upon "inverted feedback".
017 * @author Gregory J. Bedlek Copyright (C) 2018, 2019, 2020
018 * Prefix NBH = Named Bean Handler....
019 */
020
021public class NBHTurnout {
022
023//  Special case sane return values:
024    public static final int DEFAULT_TURNOUT_STATE_RV = Turnout.CLOSED;  // A valid state, just "B.S.".
025//  Standard sane return values for the types indicated:
026//  public static final Object DEFAULT_OBJECT_RV = null;       // For any function that returns something derived from Java's Object.
027//  public static final boolean DEFAULT_BOOLEAN_RV = false;    // For any function that returns boolean.
028    public static final int DEFAULT_INT_RV = 0;                // For any function that returns int.
029//  public static final long DEFAULT_LONG_RV = 0;              // For any function that returns long.
030//  public static final float DEFAULT_FLOAT_RV = (float)0.0;   // For any function that returns float.
031//  public static final String DEFAULT_STRING_RV = "UNKNOWN";  // NOI18N  For any function that returns String.
032
033//  The "thing" we're protecting:
034    private NamedBeanHandle<Turnout> _mNamedBeanHandleTurnout;
035    private final String _mUserIdentifier;
036    private final String _mParameter;
037    private final boolean _mFeedbackDifferent;
038    private final ArrayList<PropertyChangeListener> _mArrayListOfPropertyChangeListeners = new ArrayList<>();
039
040    public NBHTurnout(String module, String userIdentifier, String parameter, String turnout, boolean FeedbackDifferent) {
041        _mUserIdentifier = userIdentifier;
042        _mParameter = parameter;
043        _mFeedbackDifferent = FeedbackDifferent;
044        Turnout tempTurnout = getSafeExistingJMRITurnout(module, _mUserIdentifier, _mParameter, turnout);
045        if (tempTurnout != null) {
046            _mNamedBeanHandleTurnout = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(turnout, tempTurnout);
047        } else {
048            _mNamedBeanHandleTurnout = null;
049        }
050        if (valid()) InstanceManager.getDefault(CtcManager.class).putNBHTurnout(turnout, this);
051    }
052
053// Special constructor to create a NBHTurnout with a null NamedBeanHandle.  Used to initialize turnout fields.
054    public NBHTurnout(String module, String userIdentifier, String parameter) {
055        _mUserIdentifier = userIdentifier;
056        _mParameter = parameter;
057        _mFeedbackDifferent = false;
058        _mNamedBeanHandleTurnout = null;
059        if (valid()) InstanceManager.getDefault(CtcManager.class).putNBHTurnout("dummy", this);
060    }
061
062    public boolean valid() { return _mNamedBeanHandleTurnout != null; }  // For those that want to know the internal state.
063
064    public Turnout getBean() {
065        if (valid()) return _mNamedBeanHandleTurnout.getBean();
066        return null;
067    }
068
069    public NamedBeanHandle<?> getBeanHandle() {
070        if (valid()) return _mNamedBeanHandleTurnout;
071        return null;
072    }
073
074
075    private static Turnout getSafeExistingJMRITurnout(String module, String userIdentifier, String parameter, String turnout) {
076        try { return getExistingJMRITurnout(module, userIdentifier, parameter, turnout); } catch (CTCException e) { e.logError(); }
077        return null;
078    }
079//  turnout is NOT optional and cannot be null.  Raises Exception in ALL error cases.
080    static private Turnout getExistingJMRITurnout(String module, String userIdentifier, String parameter, String turnout) throws CTCException {
081        if (!ProjectsCommonSubs.isNullOrEmptyString(turnout)) {
082            // Cannot use a constant Instance manager reference due to the dynamic nature of tests.
083            Turnout returnValue = InstanceManager.getDefault(TurnoutManager.class).getTurnout(turnout);
084            if (returnValue == null) { throw new CTCException(module, userIdentifier, parameter, Bundle.getMessage("NBHTurnoutDoesNotExist") + " " + turnout); }    // NOI18N
085            return returnValue;
086        } else { throw new CTCException(module, userIdentifier, parameter, Bundle.getMessage("RequiredTurnoutMissing")); }    // NOI18N
087    }
088
089    public int getKnownState() {
090        if (_mNamedBeanHandleTurnout == null) return DEFAULT_TURNOUT_STATE_RV;
091        int knownState = _mNamedBeanHandleTurnout.getBean().getKnownState();
092        if (!_mFeedbackDifferent) { // Normal:
093            return knownState;
094        } else { // Reversed:
095            return knownState == Turnout.CLOSED ? Turnout.THROWN : Turnout.CLOSED;
096        }
097    }
098
099    public void setCommandedState(int s) {
100        if (_mNamedBeanHandleTurnout == null) return;
101        _mNamedBeanHandleTurnout.getBean().setCommandedState(s);
102    }
103
104    public int getFeedbackMode() {
105        if (_mNamedBeanHandleTurnout == null) return DEFAULT_INT_RV;
106        return _mNamedBeanHandleTurnout.getBean().getFeedbackMode();
107    }
108
109    public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
110        if (_mNamedBeanHandleTurnout == null) return;
111        _mNamedBeanHandleTurnout.getBean().addPropertyChangeListener(propertyChangeListener);
112        _mArrayListOfPropertyChangeListeners.add(propertyChangeListener);
113    }
114
115    public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) {
116        if (_mNamedBeanHandleTurnout == null) return;
117        _mNamedBeanHandleTurnout.getBean().removePropertyChangeListener(propertyChangeListener);
118        _mArrayListOfPropertyChangeListeners.remove(propertyChangeListener);
119    }
120
121    /**
122     * @return The turnout's handle name.
123     */
124    public String getHandleName() {
125        return valid() ? _mNamedBeanHandleTurnout.getName() : "";
126    }
127
128    /**
129     * For Unit testing only.
130     * @return Returns the present number of property change listeners registered with us so far.
131     */
132    public int testingGetCountOfPropertyChangeListenersRegistered() {
133        return _mArrayListOfPropertyChangeListeners.size();
134    }
135
136}