001package jmri.jmrit.operations.automation.actions;
002
003import java.text.MessageFormat;
004import javax.swing.JComboBox;
005import javax.swing.JOptionPane;
006import jmri.beans.PropertyChangeSupport;
007import jmri.jmrit.operations.automation.Automation;
008import jmri.jmrit.operations.automation.AutomationItem;
009import jmri.jmrit.operations.routes.RouteLocation;
010import jmri.jmrit.operations.trains.Train;
011import jmri.jmrit.operations.trains.schedules.TrainSchedule;
012
013public abstract class Action extends PropertyChangeSupport {
014
015    public static final String ACTION_COMPLETE_CHANGED_PROPERTY = "actionComplete"; // NOI18N
016    public static final String ACTION_HALT_CHANGED_PROPERTY = "actionHalt"; // NOI18N
017    public static final String ACTION_RUNNING_CHANGED_PROPERTY = "actionRunning"; // NOI18N
018    public static final String ACTION_GOTO_CHANGED_PROPERTY = "actionGoto"; // NOI18N
019
020    public static final int HALT = 0; // halt is the first button
021    public static final int OKAY = 1;
022    public static final int CLOSED = JOptionPane.CLOSED_OPTION; // -1
023    public static final int NO_MESSAGE_SENT = -2;
024    public static final int FINISH_FAILED = -3;
025
026    protected AutomationItem _automationItem = null;
027
028    abstract public int getCode();
029
030    abstract public String getName();
031
032    abstract public void doAction();
033
034    abstract public void cancelAction();
035
036    /**
037     * For combo boxes.
038     * <p>
039     * {@inheritDoc}
040     */
041    @Override
042    public String toString() {
043        return getName();
044    }
045
046    /**
047     * Mask off menu bits.
048     *
049     * @param code the integer to be modified by masking off menu bits.
050     *
051     * @return code and ActionCodes.CODE_MASK
052     */
053    protected int getCode(int code) {
054        return code & ActionCodes.CODE_MASK;
055    }
056
057    public boolean isTrainMenuEnabled() {
058        return (getCode() & ActionCodes.ENABLE_TRAINS) == ActionCodes.ENABLE_TRAINS;
059    }
060
061    public boolean isRouteMenuEnabled() {
062        return (getCode() & ActionCodes.ENABLE_ROUTES) == ActionCodes.ENABLE_ROUTES;
063    }
064
065    public boolean isMessageOkEnabled() {
066        return (getCode() & ActionCodes.OK_MESSAGE) == ActionCodes.OK_MESSAGE;
067    }
068
069    public boolean isMessageFailEnabled() {
070        return (getCode() & ActionCodes.FAIL_MESSAGE) == ActionCodes.FAIL_MESSAGE;
071    }
072
073    public boolean isAutomationMenuEnabled() {
074        return (getCode() & ActionCodes.ENABLE_AUTOMATION) == ActionCodes.ENABLE_AUTOMATION;
075    }
076
077    public boolean isGotoMenuEnabled() {
078        return (getCode() & ActionCodes.ENABLE_GOTO) == ActionCodes.ENABLE_GOTO;
079    }
080
081    public boolean isOtherMenuEnabled() {
082        return (getCode() & ActionCodes.ENABLE_OTHER) == ActionCodes.ENABLE_OTHER;
083    }
084
085    /**
086     * Used to determine if this action can run concurrently with other actions.
087     *
088     * @return true if a concurrent action
089     */
090    public boolean isConcurrentAction() {
091        return false; // override if concurrent action
092    }
093
094    public void setAutomationItem(AutomationItem item) {
095        _automationItem = item;
096    }
097
098    public AutomationItem getAutomationItem() {
099        return _automationItem;
100    }
101
102    public String getActionString() {
103        return getFormatedMessage("{0}{1}{2}{3}{4}{5}"); // NOI18N
104    }
105
106    public String getActionSuccessfulString() {
107        return Bundle.getMessage("ButtonOK");
108    }
109
110    public String getActionFailedString() {
111        return Bundle.getMessage("FAILED");
112    }
113
114    public void setRunning(boolean running) {
115        if (getAutomationItem() != null) {
116            boolean old = getAutomationItem().isActionRunning();
117            getAutomationItem().setActionRunning(running);
118            if (old != running) {
119                firePropertyChange(ACTION_RUNNING_CHANGED_PROPERTY, old, running);
120            }
121        }
122    }
123
124    /**
125     * Completes the action by displaying the correct message if there's one.
126     * Will halt if the option to halt the automation is enabled or the user
127     * requested the automation to halt.
128     *
129     * @param success true if action succeeded
130     * @return OKAY, HALT, CLOSED, NO_MESSAGE_SENT, FINISH_FAILED
131     */
132    public int finishAction(boolean success) {
133        return finishAction(success, new Object[]{Bundle.getMessage("HALT"), Bundle.getMessage("ButtonOK")});
134    }
135
136    /**
137     * Completes the action by displaying the correct message if there's one.
138     * Will halt if the option to halt the automation is enabled or the user
139     * requested the automation to halt.
140     *
141     * @param success true if action succeeded
142     * @param buttons buttons to display in message
143     * @return OKAY, HALT, CLOSED, NO_MESSAGE_SENT, FINISH_FAILED
144     */
145    public int finishAction(boolean success, Object[] buttons) {
146        int response = FINISH_FAILED;
147        if (getAutomationItem() != null) {
148            setRunning(true);
149            getAutomationItem().setActionSuccessful(success);
150            setRunning(false);
151            String message = getAutomationItem().getMessage();
152            if (!success) {
153                message = getAutomationItem().getMessageFail();
154                if (getAutomationItem().isHaltFailureEnabled()) {
155                    buttons = new Object[]{Bundle.getMessage("HALT")}; // Must halt, only the HALT button shown
156                }
157            }
158            response = sendMessage(message, buttons, success);
159            if (response == HALT && buttons[0].equals(Bundle.getMessage("HALT"))
160                    || (!success && getAutomationItem().isHaltFailureEnabled())) {
161                firePropertyChange(ACTION_HALT_CHANGED_PROPERTY, !success, success);
162            } else {
163                firePropertyChange(ACTION_COMPLETE_CHANGED_PROPERTY, !success, success);
164            }
165        }
166        return response;
167    }
168
169    /**
170     * Displays message if there's one.
171     *
172     * @param buttons the buttons to display, if success and two or more
173     *                buttons, the second button becomes the default
174     * @param success true if action succeeded
175     * @param message the text to be displayed
176     * @return which button was pressed, NO_MESSAGE_SENT, CLOSED
177     */
178    public int sendMessage(String message, Object[] buttons, boolean success) {
179        int response = NO_MESSAGE_SENT;
180        if (getAutomationItem() != null && !message.equals(AutomationItem.NONE)) {
181            String title = getAutomationItem().getId() + " "
182                    + (success ? "" : Bundle.getMessage("Failed")) + " " + getActionString();
183            Object intialValue = buttons[0]; // normally HALT
184            if (buttons.length > 1 && success) {
185                intialValue = buttons[1]; // normally OK
186            }
187            response = JOptionPane.showOptionDialog(null, getFormatedMessage(message), title,
188                    JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, null, buttons,
189                    intialValue);
190        }
191        return response;
192    }
193
194    /**
195     * Formats a message using fixed arguments in the following order:
196     * <p>
197     * action name, train name, route location name, automation name, goto item
198     * id, train schedule day.
199     *
200     * @param message the string to be formated
201     *
202     * @return formated message
203     */
204    public String getFormatedMessage(String message) {
205        String trainName = "";
206        Train train = getAutomationItem().getTrain();
207        if (train != null) {
208            trainName = " " + train.getName();
209        }
210        String routeLocationName = "";
211        RouteLocation rl = getAutomationItem().getRouteLocation();
212        if (rl != null) {
213            routeLocationName = " " + rl.getName();
214        }
215        String automationName = "";
216        Automation automation = getAutomationItem().getAutomationToRun();
217        if (automation != null) {
218            automationName = " " + automation.getName();
219        }
220        String itemId = "";
221        AutomationItem item = getAutomationItem().getGotoAutomationItem();
222        if (item != null) {
223            itemId = " " + item.getId();
224        }
225        String day = "";
226        TrainSchedule trainSchedule = getAutomationItem().getTrainSchedule();
227        if (trainSchedule != null) {
228            day = " " + trainSchedule.getName();
229        }
230        return MessageFormat.format(message, new Object[]{getName(), trainName, routeLocationName, automationName, itemId, day});
231    }
232
233    // to be overridden if action needs a ComboBox
234    public JComboBox<?> getComboBox() {
235        JComboBox<?> cb = new JComboBox<>();
236        cb.setEnabled(false);
237        return cb;
238    }
239
240}