001package jmri.jmrix.loconet.logixng;
002
003import java.util.*;
004
005import jmri.InstanceManager;
006import jmri.JmriException;
007import jmri.jmrit.logixng.*;
008import jmri.jmrit.logixng.expressions.*;
009import jmri.jmrix.loconet.*;
010
011/**
012 * This expression compares the number of slots that are currently in use with
013 * a threshold number.
014 *
015 * @author Daniel Bergqvist Copyright 2020
016 */
017public class ExpressionSlotUsage extends AbstractDigitalExpression
018        implements SlotListener {
019
020    private static final int MAX_NUM_LOCO_SLOTS = 119;
021
022    private LocoNetSystemConnectionMemo _memo;
023    private boolean _advanced = false;
024    private Has_HasNot _hasHasNot = Has_HasNot.Has;
025    private SimpleState _simpleState = SimpleState.InUse;
026    private final Set<AdvancedState> _advancedStates = new HashSet<>();
027    private Compare _compare = Compare.LessThan;
028    private int _number = 0;
029    private PercentPieces _percentPieces = PercentPieces.Pieces;
030    private int _totalSlots = 0;
031
032
033    public ExpressionSlotUsage(String sys, String user, LocoNetSystemConnectionMemo memo) {
034        super(sys, user);
035        _memo = memo;
036    }
037
038    @Override
039    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
040        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
041        String sysName = systemNames.get(getSystemName());
042        String userName = userNames.get(getSystemName());
043        if (sysName == null) sysName = manager.getAutoSystemName();
044        ExpressionSlotUsage copy = new ExpressionSlotUsage(sysName, userName, _memo);
045        copy.setComment(getComment());
046        copy.setAdvanced(_advanced);
047        copy.set_Has_HasNot(_hasHasNot);
048        copy.setSimpleState(_simpleState);
049        copy.setAdvancedStates(_advancedStates);
050        copy.setCompare(_compare);
051        copy.setNumber(_number);
052        copy.setPercentPieces(_percentPieces);
053        copy.setTotalSlots(_totalSlots);
054        return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames);
055    }
056
057    /** {@inheritDoc} */
058    @Override
059    public Category getCategory() {
060        return CategoryLocoNet.LOCONET;
061    }
062
063    public void setMemo(LocoNetSystemConnectionMemo memo) {
064        assertListenersAreNotRegistered(log, "setMemo");
065        _memo = memo;
066    }
067
068    public LocoNetSystemConnectionMemo getMemo() {
069        return _memo;
070    }
071
072    public void setAdvanced(boolean advanced) {
073        assertListenersAreNotRegistered(log, "setAdvanced");
074        _advanced = advanced;
075    }
076
077    public boolean getAdvanced() {
078        return _advanced;
079    }
080
081    public void set_Has_HasNot(Has_HasNot hasHasNot) {
082        assertListenersAreNotRegistered(log, "set_Has_HasNot");
083        _hasHasNot = hasHasNot;
084    }
085
086    public Has_HasNot get_Has_HasNot() {
087        return _hasHasNot;
088    }
089
090    public void setSimpleState(SimpleState simpleState){
091        assertListenersAreNotRegistered(log, "setSimpleState");
092        _simpleState = simpleState;
093    }
094
095    public SimpleState getSimpleState() {
096        return _simpleState;
097    }
098
099    public void setAdvancedStates(Set<AdvancedState> states) {
100        assertListenersAreNotRegistered(log, "setAdvancedStates");
101        _advancedStates.clear();
102        _advancedStates.addAll(states);
103    }
104
105    public Set<AdvancedState> getAdvancedStates() {
106        return Collections.unmodifiableSet(_advancedStates);
107    }
108
109    public void setCompare(Compare compare){
110        assertListenersAreNotRegistered(log, "setCompare");
111        _compare = compare;
112    }
113
114    public Compare getCompare() {
115        return _compare;
116    }
117
118    public void setNumber(int number) {
119        assertListenersAreNotRegistered(log, "setNumber");
120        _number = number;
121    }
122
123    public int getNumber() {
124        return _number;
125    }
126
127    public void setTotalSlots(int totalNumber) {
128        assertListenersAreNotRegistered(log, "setTotalNumber");
129        _totalSlots = totalNumber;
130    }
131
132    public int getTotalSlots() {
133        return _totalSlots;
134    }
135
136    public void setPercentPieces(PercentPieces percentPieces) {
137        assertListenersAreNotRegistered(log, "setPercentPieces");
138        _percentPieces = percentPieces;
139    }
140
141    public PercentPieces getPercentPieces() {
142        return _percentPieces;
143    }
144
145    private int getNumWithStatus() {
146        if (_memo == null) return 0;
147        int count = 0;
148        for (int i=1; i <= MAX_NUM_LOCO_SLOTS; i++) {
149            boolean match = false;
150            LocoNetSlot slot = _memo.getSlotManager().slot(i);
151            if (_advanced) {
152                for (AdvancedState s : _advancedStates) {
153                    if (s._state == slot.slotStatus()) match = true;
154                }
155            } else {
156                if (_simpleState.matches(slot.slotStatus())) match = true;
157            }
158            if (_hasHasNot == Has_HasNot.Has) {
159                if (match) count++;
160            } else {
161                if (!match) count++;
162            }
163        }
164        return count;
165    }
166
167    /** {@inheritDoc} */
168    @Override
169    public boolean evaluate() {
170        int count = getNumWithStatus();
171
172        int compareToNum = _percentPieces == PercentPieces.Percent
173                ? Math.round(((float)_number) / 100 * _totalSlots) : _number;
174
175        return _compare.compare(count, compareToNum);
176    }
177
178    @Override
179    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
180        throw new UnsupportedOperationException("Not supported.");
181    }
182
183    @Override
184    public int getChildCount() {
185        return 0;
186    }
187
188    @Override
189    public String getShortDescription(Locale locale) {
190        return Bundle.getMessage(locale, "ExpressionSlotUsage_Short");
191    }
192
193    @Override
194    public String getLongDescription(Locale locale) {
195        String stateStr;
196        if (_advanced) {
197            StringBuilder states = new StringBuilder();
198            for (AdvancedState state : _advancedStates) {
199                if (states.length() > 0) states.append(",");
200                states.append(state._text);
201            }
202            stateStr = states.length() > 0 ? states.toString() : Bundle.getMessage("NoState");
203        } else {
204            stateStr = _simpleState._text;
205        }
206
207        return Bundle.getMessage(locale, "ExpressionSlotUsage_LongConnection",
208                _hasHasNot.toString(),
209                stateStr,
210                _compare.toString(),
211                _number,
212                _percentPieces.toString(),
213                _memo != null ? _memo.getUserName() : Bundle.getMessage("MemoNotSet")
214                );
215    }
216
217    /** {@inheritDoc} */
218    @Override
219    public void setup() {
220        // Do nothing
221    }
222
223    /** {@inheritDoc} */
224    @Override
225    public void registerListenersForThisClass() {
226        if (!_listenersAreRegistered) {
227            _listenersAreRegistered = true;
228
229            if (_memo != null) {
230                SlotManager slotManager = _memo.getSlotManager();
231                slotManager.addSlotListener(this);
232
233//                slotManager.getInUseCount();
234            }
235        }
236    }
237
238    /** {@inheritDoc} */
239    @Override
240    public void unregisterListenersForThisClass() {
241        if (_memo != null) {
242            SlotManager slotManager = _memo.getSlotManager();
243            slotManager.removeSlotListener(this);
244        }
245        _listenersAreRegistered = false;
246    }
247
248    /** {@inheritDoc} */
249    @Override
250    public void disposeMe() {
251    }
252
253    @Override
254    public void notifyChangedSlot(LocoNetSlot s) {
255        if (_listenersAreRegistered) {
256            getConditionalNG().execute();
257        }
258    }
259
260
261
262    public enum Has_HasNot {
263        Has(Bundle.getMessage("HasHasNotType_Has")),
264        HasNot(Bundle.getMessage("HasHasNotType_HasNot"));
265
266        private final String _text;
267
268        private Has_HasNot(String text) {
269            this._text = text;
270        }
271
272        @Override
273        public String toString() {
274            return _text;
275        }
276
277    }
278
279
280    public enum SimpleState {
281        InUse(Bundle.getMessage("SimpleStateType_InUse"), new int[]{LnConstants.LOCO_IN_USE}),
282        Free(Bundle.getMessage("SimpleStateType_Free"), new int[]{LnConstants.LOCO_FREE});
283
284        private final String _text;
285        private final int[] _states;
286
287        private SimpleState(String text, int[] states) {
288            this._text = text;
289            this._states = states;
290        }
291
292        @Override
293        public String toString() {
294            return _text;
295        }
296
297        public int[] getStates() {
298            return _states;
299        }
300
301        public boolean matches(int state) {
302            for (int s : _states) {
303                if (s == state) return true;
304            }
305            return false;
306        }
307
308    }
309
310
311    public enum AdvancedState {
312        InUse(LnConstants.LOCO_IN_USE, Bundle.getMessage("AdvancedStateType_InUse")),
313        Idle(LnConstants.LOCO_IDLE, Bundle.getMessage("AdvancedStateType_Idle")),
314        Common(LnConstants.LOCO_COMMON, Bundle.getMessage("AdvancedStateType_Common")),
315        Free(LnConstants.LOCO_FREE, Bundle.getMessage("AdvancedStateType_Free"));
316
317        private final int _state;
318        private final String _text;
319
320        private AdvancedState(int state, String text) {
321            this._state = state;
322            this._text = text;
323        }
324
325        public int getState() {
326            return _state;
327        }
328
329        @Override
330        public String toString() {
331            return _text;
332        }
333
334    }
335
336
337    public enum Compare {
338        LessThan(Bundle.getMessage("CompareType_LessThan"), (int a, int b) -> a < b),
339        LessThanOrEqual(Bundle.getMessage("CompareType_LessThanOrEqual"), (int a, int b) -> a <= b),
340        Equal(Bundle.getMessage("CompareType_Equal"), (int a, int b) -> a == b),
341        NotEqual(Bundle.getMessage("CompareType_NotEqual"), (int a, int b) -> a != b),
342        GreaterThanOrEqual(Bundle.getMessage("CompareType_GreaterThanOrEqual"), (int a, int b) -> a >= b),
343        GreaterThan(Bundle.getMessage("CompareType_GreaterThan"), (int a, int b) -> a > b);
344
345        private final String _text;
346        private final CompareIntegers _compare;
347
348        private Compare(String text, CompareIntegers compare) {
349            this._text = text;
350            this._compare = compare;
351        }
352
353        @Override
354        public String toString() {
355            return _text;
356        }
357
358        public boolean compare(int a, int b) {
359            return _compare.compare(a, b);
360        }
361
362    }
363
364
365    public enum PercentPieces {
366        Percent(Bundle.getMessage("PercentPiecesType_Percent")),
367        Pieces(Bundle.getMessage("PercentPiecesType_Pieces"));
368
369        private final String _text;
370
371        private PercentPieces(String text) {
372            this._text = text;
373        }
374
375        @Override
376        public String toString() {
377            return _text;
378        }
379
380    }
381
382
383    private interface CompareIntegers {
384        public boolean compare(int a, int b);
385    }
386
387
388    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionSlotUsage.class);
389
390}