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