001package jmri.jmrit.logixng.actions;
002
003import java.util.*;
004
005import jmri.*;
006import jmri.jmrit.logixng.*;
007import jmri.jmrit.roster.Roster;
008import jmri.jmrit.roster.RosterEntry;
009
010/**
011 * Executes an action when the expression is True.
012 *
013 * @author Daniel Bergqvist Copyright 2026
014 */
015public class ForEachRoster extends AbstractDigitalAction
016        implements FemaleSocketListener {
017
018    private String _variableName = "";
019    private String _socketSystemName;
020    private final FemaleDigitalActionSocket _socket;
021
022    public ForEachRoster(String sys, String user) {
023        super(sys, user);
024        _socket = InstanceManager.getDefault(DigitalActionManager.class)
025                .createFemaleSocket(this, this, "A");
026    }
027
028    @Override
029    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
030        DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class);
031        String sysName = systemNames.get(getSystemName());
032        String userName = userNames.get(getSystemName());
033        if (sysName == null) sysName = manager.getAutoSystemName();
034        ForEachRoster copy = new ForEachRoster(sysName, userName);
035        copy.setComment(getComment());
036        copy.setLocalVariableName(_variableName);
037        return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames);
038    }
039
040    /**
041     * Get name of local variable
042     * @return name of local variable
043     */
044    public String getLocalVariableName() {
045        return _variableName;
046    }
047
048    /**
049     * Set name of local variable
050     * @param localVariableName name of local variable
051     */
052    public void setLocalVariableName(String localVariableName) {
053        _variableName = localVariableName;
054    }
055
056    /** {@inheritDoc} */
057    @Override
058    public LogixNG_Category getCategory() {
059        return LogixNG_Category.FLOW_CONTROL;
060    }
061
062    /** {@inheritDoc} */
063    @Override
064    public void execute() throws JmriException {
065        SymbolTable symbolTable = getConditionalNG().getSymbolTable();
066
067        var roster = Roster.getDefault();
068
069        for (RosterEntry rosterEntry : roster.getAllEntries()) {
070            symbolTable.setValue(_variableName, rosterEntry);
071            try {
072                _socket.execute();
073            } catch (BreakException e) {
074                break;
075            } catch (ContinueException e) {
076                // Do nothing, just catch it.
077            }
078        }
079    }
080
081    @Override
082    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
083        switch (index) {
084            case 0:
085                return _socket;
086
087            default:
088                throw new IllegalArgumentException(
089                        String.format("index has invalid value: %d", index));
090        }
091    }
092
093    @Override
094    public int getChildCount() {
095        return 1;
096    }
097
098    @Override
099    public void connected(FemaleSocket socket) {
100        if (socket == _socket) {
101            _socketSystemName = socket.getConnectedSocket().getSystemName();
102        } else {
103            throw new IllegalArgumentException("unkown socket");
104        }
105    }
106
107    @Override
108    public void disconnected(FemaleSocket socket) {
109        if (socket == _socket) {
110            _socketSystemName = null;
111        } else {
112            throw new IllegalArgumentException("unkown socket");
113        }
114    }
115
116    @Override
117    public String getShortDescription(Locale locale) {
118        return Bundle.getMessage(locale, "ForEachRoster_Short");
119    }
120
121    @Override
122    public String getLongDescription(Locale locale) {
123        return Bundle.getMessage(locale, "ForEachRoster_Long",
124                _variableName, _socket.getName());
125    }
126
127    public FemaleDigitalActionSocket getSocket() {
128        return _socket;
129    }
130
131    public String getSocketSystemName() {
132        return _socketSystemName;
133    }
134
135    public void setSocketSystemName(String systemName) {
136        _socketSystemName = systemName;
137    }
138
139    /** {@inheritDoc} */
140    @Override
141    public void setup() {
142        try {
143            if ( !_socket.isConnected()
144                    || !_socket.getConnectedSocket().getSystemName()
145                            .equals(_socketSystemName)) {
146
147                String socketSystemName = _socketSystemName;
148                _socket.disconnect();
149                if (socketSystemName != null) {
150                    MaleSocket maleSocket =
151                            InstanceManager.getDefault(DigitalActionManager.class)
152                                    .getBySystemName(socketSystemName);
153                    _socket.disconnect();
154                    if (maleSocket != null) {
155                        _socket.connect(maleSocket);
156                        maleSocket.setup();
157                    } else {
158                        log.error("cannot load digital action {}", socketSystemName);
159                    }
160                }
161            } else {
162                _socket.getConnectedSocket().setup();
163            }
164        } catch (SocketAlreadyConnectedException ex) {
165            // This shouldn't happen and is a runtime error if it does.
166            throw new RuntimeException("socket is already connected");
167        }
168    }
169
170
171    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ForEachRoster.class);
172
173}