001package jmri.jmrit.logixng.actions;
002
003import java.util.*;
004
005import javax.annotation.Nonnull;
006
007import jmri.*;
008import jmri.jmrit.logixng.*;
009import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean;
010import jmri.jmrit.logixng.util.ReferenceUtil;
011import jmri.jmrit.logixng.util.parser.*;
012import jmri.jmrit.logixng.util.parser.ExpressionNode;
013import jmri.jmrit.logixng.util.parser.RecursiveDescentParser;
014import jmri.util.TypeConversionUtil;
015
016/**
017 * This action triggers a reporter.
018 *
019 * @author Daniel Bergqvist Copyright 2021
020 * @author Dave Sand Copyright 2021
021 */
022public class ActionReporter extends AbstractDigitalAction {
023
024    private final LogixNG_SelectNamedBean<Reporter> _selectNamedBean =
025            new LogixNG_SelectNamedBean<>(
026                    this, Reporter.class, InstanceManager.getDefault(ReporterManager.class));
027
028    private final LogixNG_SelectNamedBean<Memory> _selectMemoryNamedBean =
029            new LogixNG_SelectNamedBean<>(
030                    this, Memory.class, InstanceManager.getDefault(MemoryManager.class));
031
032    private ReporterValue _reporterValue = ReporterValue.CopyCurrentReport;
033
034    private NamedBeanAddressing _dataAddressing = NamedBeanAddressing.Direct;
035    private String _dataReference = "";
036    private String _dataLocalVariable = "";
037    private String _dataFormula = "";
038    private ExpressionNode _dataExpressionNode;
039
040//    private NamedBeanHandle<Memory> _memoryHandle;
041
042    public ActionReporter(String sys, String user)
043            throws BadUserNameException, BadSystemNameException {
044        super(sys, user);
045    }
046
047    @Override
048    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException {
049        DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class);
050        String sysName = systemNames.get(getSystemName());
051        String userName = userNames.get(getSystemName());
052        if (sysName == null) sysName = manager.getAutoSystemName();
053        ActionReporter copy = new ActionReporter(sysName, userName);
054        copy.setComment(getComment());
055        _selectNamedBean.copy(copy._selectNamedBean);
056        _selectMemoryNamedBean.copy(copy._selectMemoryNamedBean);
057
058        copy.setReporterValue(_reporterValue);
059
060        copy.setDataAddressing(_dataAddressing);
061        copy.setDataReference(_dataReference);
062        copy.setDataLocalVariable(_dataLocalVariable);
063        copy.setDataFormula(_dataFormula);
064//        if (_memoryHandle != null) copy.setMemory(_memoryHandle);
065
066        return manager.registerAction(copy);
067    }
068
069    public LogixNG_SelectNamedBean<Reporter> getSelectNamedBean() {
070        return _selectNamedBean;
071    }
072
073    public LogixNG_SelectNamedBean<Memory> getSelectMemoryNamedBean() {
074        return _selectMemoryNamedBean;
075    }
076
077    public void setReporterValue(ReporterValue value) {
078        _reporterValue = value;
079    }
080
081    public ReporterValue getReporterValue() {
082        return _reporterValue;
083    }
084
085    public void setDataAddressing(NamedBeanAddressing addressing) throws ParserException {
086        _dataAddressing = addressing;
087        parseDataFormula();
088    }
089
090    public NamedBeanAddressing getDataAddressing() {
091        return _dataAddressing;
092    }
093
094    public void setDataReference(@Nonnull String reference) {
095        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
096            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
097        }
098        _dataReference = reference;
099    }
100
101    public String getDataReference() {
102        return _dataReference;
103    }
104
105    public void setDataLocalVariable(@Nonnull String localVariable) {
106        _dataLocalVariable = localVariable;
107    }
108
109    public String getDataLocalVariable() {
110        return _dataLocalVariable;
111    }
112
113    public void setDataFormula(@Nonnull String formula) throws ParserException {
114        _dataFormula = formula;
115        parseDataFormula();
116    }
117
118    public String getDataFormula() {
119        return _dataFormula;
120    }
121
122    private void parseDataFormula() throws ParserException {
123        if (_dataAddressing == NamedBeanAddressing.Formula) {
124            Map<String, Variable> variables = new HashMap<>();
125
126            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
127            _dataExpressionNode = parser.parseExpression(_dataFormula);
128        } else {
129            _dataExpressionNode = null;
130        }
131    }
132
133
134    /** {@inheritDoc} */
135    @Override
136    public LogixNG_Category getCategory() {
137        return LogixNG_Category.ITEM;
138    }
139
140    Object getReporterData(Reporter reporter) throws JmriException {
141        Object obj;
142        ReporterValue value = _reporterValue;
143
144        switch (value) {
145            case CopyCurrentReport:
146                obj = reporter.getCurrentReport();
147                break;
148            case CopyLastReport:
149                obj = reporter.getLastReport();
150                break;
151            case CopyState:
152                obj = reporter.getState();
153                break;
154            default:
155                throw new IllegalArgumentException("invalid value name: " + value.name());
156        }
157
158        return obj;
159    }
160
161    void updateDestination(Object data) throws JmriException {
162        switch (_dataAddressing) {
163            case Direct:
164                Memory memory = _selectMemoryNamedBean.evaluateNamedBean(getConditionalNG());
165                if (memory != null) {
166                    memory.setValue(data);
167                }
168                break;
169
170            case Reference:
171                String refName = ReferenceUtil.getReference(
172                        getConditionalNG().getSymbolTable(), _dataReference);
173                log.debug("ref ref = {}, name = {}", _dataReference, refName);
174                Memory refMem = InstanceManager.getDefault(MemoryManager.class).getMemory(refName);
175                if (refMem == null) {
176                    throw new IllegalArgumentException("invalid memory reference: " + refName);
177                }
178                refMem.setValue(data);
179                break;
180
181            case LocalVariable:
182                log.debug("LocalVariable: lv = {}", _dataLocalVariable);
183                getConditionalNG().getSymbolTable().setValue(_dataLocalVariable, data);
184                break;
185
186            case Formula:
187                String formulaName = _dataExpressionNode != null
188                        ? TypeConversionUtil.convertToString(
189                                _dataExpressionNode.calculate(
190                                        getConditionalNG().getSymbolTable()), false)
191                        : null;
192                if (formulaName == null) {
193                    throw new IllegalArgumentException("invalid memory formula, name is null");
194                }
195
196                Memory formulaMem = InstanceManager.getDefault(MemoryManager.class).getMemory(formulaName);
197                if (formulaMem == null) {
198                    throw new IllegalArgumentException("invalid memory formula: " + formulaName);
199                }
200                formulaMem.setValue(data);
201                break;
202
203            default:
204                throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name());
205        }
206    }
207
208    /** {@inheritDoc} */
209    @Override
210    public void execute() throws JmriException {
211        // Get the reporter bean
212        Reporter reporter = _selectNamedBean.evaluateNamedBean(getConditionalNG());
213        if (reporter == null) return;
214        log.debug("reporter = {}", reporter.getDisplayName());
215
216        // Get the reporter data
217        Object data = getReporterData(reporter);
218        log.debug("data = {}", data);
219
220        // Update the destination
221        updateDestination(data);
222    }
223
224    @Override
225    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
226        throw new UnsupportedOperationException("Not supported.");
227    }
228
229    @Override
230    public int getChildCount() {
231        return 0;
232    }
233
234    @Override
235    public String getShortDescription(Locale locale) {
236        return Bundle.getMessage(locale, "ActionReporter_Short");
237    }
238
239    @Override
240    public String getLongDescription(Locale locale) {
241        String bean = _selectNamedBean.getDescription(locale);
242        String dest = "";
243
244        switch (_dataAddressing) {
245            case Direct:
246                String memoryName = _selectMemoryNamedBean.getDescription(locale);
247                dest = Bundle.getMessage(locale, "AddressByDirect", memoryName);
248                break;
249
250            case Reference:
251                dest = Bundle.getMessage(locale, "AddressByReference", _dataReference);
252                break;
253
254            case LocalVariable:
255                dest = Bundle.getMessage(locale, "AddressByLocalVariable", _dataLocalVariable);
256                break;
257
258            case Formula:
259                dest = Bundle.getMessage(locale, "AddressByFormula", _dataFormula);
260                break;
261
262            default:
263                throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name());
264        }
265
266        String item = getReporterValue().toString();
267
268        return Bundle.getMessage(locale, "ActionReporter_Long", item, bean, dest);
269    }
270
271    /** {@inheritDoc} */
272    @Override
273    public void setup() {
274        // Do nothing
275    }
276
277    /** {@inheritDoc} */
278    @Override
279    public void disposeMe() {
280    }
281
282    public enum ReporterValue {
283        CopyCurrentReport(Bundle.getMessage("ActionReporter_CopyCurrentReport")),
284        CopyLastReport(Bundle.getMessage("ActionReporter_CopyLastReport")),
285        CopyState(Bundle.getMessage("ActionReporter_CopyState"));
286
287        private final String _text;
288
289        private ReporterValue(String text) {
290            this._text = text;
291        }
292
293        @Override
294        public String toString() {
295            return _text;
296        }
297
298    }
299
300    /** {@inheritDoc} */
301    @Override
302    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
303        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action);
304        _selectMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action);
305    }
306
307    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionReporter.class);
308
309}