001package jmri.jmrit.logixng.expressions;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.*;
006
007import jmri.*;
008import jmri.jmrit.logixng.*;
009import jmri.util.TimerUtil;
010
011/**
012 * This expression returns the number of minutes since midnight for the fast
013 * clock or the system clock.
014 *
015 * @author Daniel Bergqvist Copyright 2020
016 * @author Dave Sand Copyright 2021
017 */
018public class TimeSinceMidnight extends AbstractAnalogExpression implements PropertyChangeListener {
019
020    private Type _type = Type.FastClock;
021    private Timebase _fastClock;
022
023    TimerTask timerTask = null;
024    private final int millisInAMinute = 60000;
025
026
027    public TimeSinceMidnight(String sys, String user) {
028        super(sys, user);
029    }
030
031    @Override
032    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
033        AnalogExpressionManager manager = InstanceManager.getDefault(AnalogExpressionManager.class);
034        String sysName = systemNames.get(getSystemName());
035        String userName = userNames.get(getSystemName());
036        if (sysName == null) sysName = manager.getAutoSystemName();
037        TimeSinceMidnight copy = new TimeSinceMidnight(sysName, userName);
038        copy.setComment(getComment());
039        copy.setType(_type);
040        return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames);
041    }
042
043    /** {@inheritDoc} */
044    @Override
045    public Category getCategory() {
046        return Category.ITEM;
047    }
048
049    public void setType(Type type) {
050        assertListenersAreNotRegistered(log, "setType");
051        _type = type;
052
053        if (_type == Type.FastClock) {
054            _fastClock = InstanceManager.getDefault(jmri.Timebase.class);
055        } else {
056            _fastClock = null;
057        }
058    }
059
060    public Type getType() {
061        return _type;
062    }
063
064    /** {@inheritDoc} */
065    @Override
066    public double evaluate() {
067        Calendar currentTime = null;
068
069        switch (_type) {
070            case SystemClock:
071                currentTime = Calendar.getInstance();
072                break;
073
074            case FastClock:
075                if (_fastClock == null) return 0;
076                currentTime = Calendar.getInstance();
077                currentTime.setTime(_fastClock.getTime());
078                break;
079
080            default:
081                throw new UnsupportedOperationException("_type has unknown value: " + _type.name());
082        }
083
084        return (currentTime.get(Calendar.HOUR_OF_DAY) * 60) + currentTime.get(Calendar.MINUTE);
085    }
086
087    @Override
088    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
089        throw new UnsupportedOperationException("Not supported.");
090    }
091
092    @Override
093    public int getChildCount() {
094        return 0;
095    }
096
097    @Override
098    public String getShortDescription(Locale locale) {
099        return Bundle.getMessage(locale, "TimeSinceMidnight_Short");
100    }
101
102    @Override
103    public String getLongDescription(Locale locale) {
104        switch (_type) {
105            case SystemClock:
106                return Bundle.getMessage(locale, "TimeSinceMidnight_Long_SystemClock");
107
108            case FastClock:
109                return Bundle.getMessage(locale, "TimeSinceMidnight_Long_FastClock");
110
111            default:
112                throw new RuntimeException("Unknown value of _timerType: "+_type.name());
113        }
114    }
115
116    /** {@inheritDoc} */
117    @Override
118    public void setup() {
119        // Do nothing
120    }
121
122    /** {@inheritDoc}
123     * The SystemClock listener creates a timer on the first call.  Subsequent calls
124     * enabled timer processing.
125     */
126    @Override
127    public void registerListenersForThisClass() {
128        if (!_listenersAreRegistered) {
129            switch (_type) {
130                case SystemClock:
131                    scheduleTimer();
132                    break;
133
134                case FastClock:
135                    _fastClock.addPropertyChangeListener("time", this);
136                    break;
137
138                default:
139                    throw new UnsupportedOperationException("_type has unknown value: " + _type.name());
140            }
141
142            _listenersAreRegistered = true;
143        }
144    }
145
146    /** {@inheritDoc}
147     * The SystemClock timer flag is set false to suspend processing of timer events.  The
148     * timer keeps running for the duration of the JMRI session.
149     */
150    @Override
151    public void unregisterListenersForThisClass() {
152        if (_listenersAreRegistered) {
153            switch (_type) {
154                case SystemClock:
155                    if (timerTask != null) timerTask.cancel();
156                    break;
157
158                case FastClock:
159                    if (_fastClock != null) _fastClock.removePropertyChangeListener("time", this);
160                    break;
161
162                default:
163                    throw new UnsupportedOperationException("_type has unknown value: " + _type.name());
164            }
165
166            _listenersAreRegistered = false;
167        }
168    }
169
170    private void scheduleTimer() {
171        timerTask = new TimerTask() {
172            @Override
173            public void run() {
174                propertyChange(null);
175            }
176        };
177        TimerUtil.schedule(timerTask, System.currentTimeMillis() % millisInAMinute, millisInAMinute);
178    }
179
180    /** {@inheritDoc} */
181    @Override
182    public void propertyChange(PropertyChangeEvent evt) {
183        getConditionalNG().execute();
184    }
185
186    /** {@inheritDoc} */
187    @Override
188    public void disposeMe() {
189        if (timerTask != null) timerTask.cancel();
190    }
191
192    public enum Type {
193        FastClock(Bundle.getMessage("ClockTypeFastClock")),
194        SystemClock(Bundle.getMessage("ClockTypeSystemClock"));
195
196        private final String _text;
197
198        private Type(String text) {
199            this._text = text;
200        }
201
202        @Override
203        public String toString() {
204            return _text;
205        }
206
207    }
208
209    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TimeSinceMidnight.class);
210
211}