001package jmri.jmrit.logixng.actions;
002
003import java.util.Locale;
004import java.util.Map;
005
006import jmri.*;
007import jmri.jmrit.logixng.*;
008import jmri.jmrit.logixng.util.LogixNG_SelectEnum;
009import jmri.jmrit.logixng.util.LogixNG_SelectInteger;
010
011/**
012 * Sets a function on a throttle
013 *
014 * @author Daniel Bergqvist Copyright 2023
015 */
016public final class ActionThrottleFunction extends AbstractDigitalAction {
017
018    private SystemConnectionMemo _memo;
019    private ThrottleManager _throttleManager;
020    private ThrottleManager _oldThrottleManager;
021
022    // The throttle if we have one or if a request is sent, null otherwise
023    private DccThrottle _throttle;
024    private ThrottleListener _throttleListener;
025
026    private final LogixNG_SelectInteger _selectAddress = new LogixNG_SelectInteger(this);
027    private final LogixNG_SelectInteger _selectFunction = new LogixNG_SelectInteger(this);
028    private final LogixNG_SelectEnum<FunctionState> _selectOnOff =
029            new LogixNG_SelectEnum<>(this, FunctionState.values(), FunctionState.On);
030
031
032    public ActionThrottleFunction(String sys, String user) {
033        super(sys, user);
034
035        // Set the _throttleManager variable
036        setMemo(null);
037    }
038
039    @Override
040    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
041        DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class);
042        String sysName = systemNames.get(getSystemName());
043        String userName = userNames.get(getSystemName());
044        if (sysName == null) sysName = manager.getAutoSystemName();
045        ActionThrottleFunction copy = new ActionThrottleFunction(sysName, userName);
046        copy.setComment(getComment());
047        copy.setMemo(_memo);
048        _selectAddress.copy(copy._selectAddress);
049        _selectFunction.copy(copy._selectFunction);
050        _selectOnOff.copy(copy._selectOnOff);
051        return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames);
052    }
053
054    public LogixNG_SelectInteger getSelectAddress() {
055        return _selectAddress;
056    }
057
058    public LogixNG_SelectInteger getSelectFunction() {
059        return _selectFunction;
060    }
061
062    public LogixNG_SelectEnum<FunctionState> getSelectOnOff() {
063        return _selectOnOff;
064    }
065
066    public void setMemo(SystemConnectionMemo memo) {
067        assertListenersAreNotRegistered(log, "setMemo");
068        _memo = memo;
069        if (_memo != null) {
070            _throttleManager = _memo.get(jmri.ThrottleManager.class);
071            if (_throttleManager == null) {
072                throw new IllegalArgumentException("Memo "+memo.getUserName()+" doesn't have a ThrottleManager");
073            }
074        } else {
075            _throttleManager = InstanceManager.getDefault(ThrottleManager.class);
076        }
077    }
078
079    public SystemConnectionMemo getMemo() {
080        return _memo;
081    }
082
083    /** {@inheritDoc} */
084    @Override
085    public LogixNG_Category getCategory() {
086        return LogixNG_Category.ITEM;
087    }
088
089    /** {@inheritDoc} */
090    @Override
091    public void execute() throws JmriException {
092
093        ConditionalNG conditionalNG = this.getConditionalNG();
094
095        int currentLocoAddress = -1;
096        int newLocoAddress = -1;
097
098        if (_throttle != null) {
099            currentLocoAddress = _throttle.getLocoAddress().getNumber();
100        }
101
102        newLocoAddress = _selectAddress.evaluateValue(conditionalNG);
103
104        if (_throttleManager != _oldThrottleManager) {
105            currentLocoAddress = -1;    // Force request of new throttle
106            _oldThrottleManager = _throttleManager;
107        }
108
109        if (newLocoAddress != currentLocoAddress) {
110
111            if (_throttle != null) {
112                // Release the loco
113                _throttleManager.releaseThrottle(_throttle, _throttleListener);
114                _throttle = null;
115            }
116
117            if (newLocoAddress != -1) {
118
119                _throttleListener =  new ThrottleListener() {
120                    @Override
121                    public void notifyThrottleFound(DccThrottle t) {
122                        _throttle = t;
123                        executeConditionalNG();
124                    }
125
126                    @Override
127                    public void notifyFailedThrottleRequest(LocoAddress address, String reason) {
128                        log.warn("loco {} cannot be aquired", address.getNumber());
129                    }
130
131                    @Override
132                    public void notifyDecisionRequired(LocoAddress address, ThrottleListener.DecisionType question) {
133                        log.warn("Loco {} cannot be aquired. Decision required.", address.getNumber());
134                    }
135                };
136
137                boolean result = _throttleManager.requestThrottle(newLocoAddress, _throttleListener);
138
139                if (!result) {
140                    log.warn("loco {} cannot be aquired", newLocoAddress);
141                }
142            }
143
144        }
145
146        // We have a throttle if _throttle is not null
147        if (_throttle != null) {
148
149            int function = _selectFunction.evaluateValue(conditionalNG);
150            boolean isFunctionOn = _selectOnOff.evaluateEnum(conditionalNG)._value;
151
152            DccThrottle throttle = _throttle;
153            int func = function;
154            boolean funcState = isFunctionOn;
155            jmri.util.ThreadingUtil.runOnLayoutWithJmriException(() -> {
156                throttle.setFunction(func, funcState);
157            });
158        }
159    }
160
161    private void executeConditionalNG() {
162        if (_listenersAreRegistered) {
163            ConditionalNG c = getConditionalNG();
164            if (c != null) {
165                c.execute();
166            }
167        }
168    }
169
170    @Override
171    public String getShortDescription(Locale locale) {
172        return Bundle.getMessage(locale, "ActionThrottleFunction_Short");
173    }
174
175    @Override
176    public String getLongDescription(Locale locale) {
177        if (_memo != null) {
178            return Bundle.getMessage(locale, "ActionThrottleFunction_LongConnection",
179                    _selectAddress.getDescription(locale),
180                    _selectFunction.getDescription(locale),
181                    _selectOnOff.getDescription(locale),
182                    _memo.getUserName());
183        } else {
184            return Bundle.getMessage(locale, "ActionThrottleFunction_Long",
185                    _selectAddress.getDescription(locale),
186                    _selectFunction.getDescription(locale),
187                    _selectOnOff.getDescription(locale));
188        }
189    }
190
191    /** {@inheritDoc} */
192    @Override
193    public void setup() {
194        // Do nothing
195    }
196
197    /** {@inheritDoc} */
198    @Override
199    public void disposeMe() {
200        if (_throttle != null) {
201            _throttleManager.releaseThrottle(_throttle, _throttleListener);
202        }
203    }
204
205    public enum FunctionState {
206        Off(false, Bundle.getMessage("StateOff")),
207        On(true, Bundle.getMessage("StateOn"));
208
209        private final boolean _value;
210        private final String _text;
211
212        private FunctionState(boolean value, String text) {
213            this._value = value;
214            this._text = text;
215        }
216
217        public boolean getValue() {
218            return _value;
219        }
220
221        @Override
222        public String toString() {
223            return _text;
224        }
225
226    }
227
228    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionThrottleFunction.class);
229
230}