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