001package jmri.jmrix.can.cbus.eventtable;
002
003import java.util.ArrayList;
004import javax.annotation.Nonnull;
005import jmri.jmrix.can.CanSystemConnectionMemo;
006import jmri.jmrix.can.cbus.CbusNameService;
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * Class to provide access to the EventTableData.xml file.
012 * @author Steve Young Copyright (C) 2019
013 */
014public class CbusBasicEventTableModel extends javax.swing.table.AbstractTableModel {
015
016    public static final int NODE_COLUMN = 0;
017    public static final int EVENT_COLUMN = 1;
018    public static final int NAME_COLUMN = 2;
019    public static final int NODENAME_COLUMN = 3;
020    public static final int COMMENT_COLUMN = 4;
021    public static final int STATE_COLUMN = 5;
022
023    public static final int TOGGLE_BUTTON_COLUMN = 6;
024    public static final int ON_BUTTON_COLUMN = 7;
025    public static final int OFF_BUTTON_COLUMN = 8;
026    public static final int CANID_COLUMN = 9;
027    public static final int LATEST_TIMESTAMP_COLUMN = 10;
028    public static final int STATUS_REQUEST_BUTTON_COLUMN = 11;
029
030    public static final int SESSION_TOTAL_COLUMN = 12;
031    public static final int SESSION_ON_COLUMN = 13;
032    public static final int SESSION_OFF_COLUMN = 14;
033    public static final int SESSION_IN_COLUMN = 15;
034    public static final int SESSION_OUT_COLUMN = 16;
035
036    public static final int ALL_TOTAL_COLUMN = 17;
037    public static final int ALL_ON_COLUMN = 18;
038    public static final int ALL_OFF_COLUMN = 19;
039    public static final int ALL_IN_COLUMN = 20;
040    public static final int ALL_OUT_COLUMN = 21;
041
042    public static final int DELETE_BUTTON_COLUMN = 22;
043    public static final int STLR_ON_COLUMN = 23;
044    public static final int STLR_OFF_COLUMN = 24;
045
046    public static final int EVENT_DAT_1 = 25;
047    public static final int EVENT_DAT_2 = 26;
048    public static final int EVENT_DAT_3 = 27;
049
050    public static final int MAX_COLUMN = 28;
051
052    protected final CanSystemConnectionMemo _memo;
053    protected ArrayList<CbusTableEvent> _mainArray;
054    public final CbusEventTableAction ta;
055    public static final int[] INITIAL_COLS = new int[]{ 0,1,2,4,5,6,23,24 };
056
057    // list of Columns which may update on receipt of CanFrame.
058    static final int[] canFrameCols = new int[]{
059        CANID_COLUMN, LATEST_TIMESTAMP_COLUMN, STATE_COLUMN, TOGGLE_BUTTON_COLUMN,
060        SESSION_TOTAL_COLUMN, SESSION_IN_COLUMN, SESSION_OUT_COLUMN, SESSION_OFF_COLUMN, SESSION_ON_COLUMN,
061        ALL_TOTAL_COLUMN, ALL_ON_COLUMN, ALL_OFF_COLUMN, ALL_IN_COLUMN, ALL_OUT_COLUMN,
062        EVENT_DAT_1, EVENT_DAT_2, EVENT_DAT_3 };
063
064    public CbusBasicEventTableModel(CanSystemConnectionMemo memo) {
065        this(memo, 1);
066    }
067
068    public CbusBasicEventTableModel(CanSystemConnectionMemo memo, int initialRowSize) {
069        super();
070        _memo = memo;
071        _mainArray = new ArrayList<>(initialRowSize);
072        ta = new CbusEventTableAction( this);
073    }
074
075    /**
076     * {@inheritDoc}
077     */
078    @Override
079    public int getColumnCount() {
080        return MAX_COLUMN;
081    }
082
083    /**
084     * {@inheritDoc}
085     */
086    @Override
087    public int getRowCount() {
088        return _mainArray.size();
089    }
090
091    /**
092     * {@inheritDoc}
093     */
094    @Override
095    public Object getValueAt(int row, int col) {
096        switch (col) {
097            case NODE_COLUMN:
098                return _mainArray.get(row).getNn();
099            case EVENT_COLUMN:
100                return _mainArray.get(row).getEn();
101            case NAME_COLUMN:
102                return _mainArray.get(row).getName();
103            case NODENAME_COLUMN:
104                return new CbusNameService(_memo).getNodeName( _mainArray.get(row).getNn() );
105            case COMMENT_COLUMN:
106                return _mainArray.get(row).getComment();
107            case STATE_COLUMN:
108                return _mainArray.get(row).getState();
109            default:
110                return getValueAtPt2(row,col);
111        }
112    }
113
114    private Object getValueAtPt2(int row, int col) {
115        switch (col) {
116            case TOGGLE_BUTTON_COLUMN:  // on or off event  1 is on, 0 is off, null unknown
117                if ( _mainArray.get(row).getState()==CbusTableEvent.EvState.OFF ) {
118                    return Bundle.getMessage("CbusSendOn"); // NOI18N
119                } else {
120                    return Bundle.getMessage("CbusSendOff"); // NOI18N
121                }
122            case ON_BUTTON_COLUMN:
123                return Bundle.getMessage("CbusSendOn"); // NOI18N
124            case OFF_BUTTON_COLUMN:
125                return Bundle.getMessage("CbusSendOff");
126            case CANID_COLUMN:
127                return _mainArray.get(row).getEventCanId();
128            case LATEST_TIMESTAMP_COLUMN:
129                return _mainArray.get(row).getDate();
130            default:
131                return getValueAtPt3(row,col);
132        }
133    }
134
135    private Object getValueAtPt3(int row, int col) {
136        switch (col) {
137            case SESSION_TOTAL_COLUMN:
138                return (_mainArray.get(row).getSessionOnOff(true) + _mainArray.get(row).getSessionOnOff(false) );
139            case SESSION_ON_COLUMN:
140                return _mainArray.get(row).getSessionOnOff(true);
141            case SESSION_OFF_COLUMN:
142                return _mainArray.get(row).getSessionOnOff(false);
143            case SESSION_IN_COLUMN:
144                return _mainArray.get(row).getSessionInOut(true);
145            case SESSION_OUT_COLUMN:
146                return _mainArray.get(row).getSessionInOut(false);
147            default:
148                return getValueAtPt4(row,col);
149        }
150    }
151
152    private Object getValueAtPt4(int row, int col) {
153        switch (col) {
154            case ALL_TOTAL_COLUMN:
155                return (_mainArray.get(row).getTotalInOut(true) + _mainArray.get(row).getTotalInOut(false) );
156            case ALL_ON_COLUMN:
157                return _mainArray.get(row).getTotalOnOff(true);
158            case ALL_OFF_COLUMN:
159                return _mainArray.get(row).getTotalOnOff(false);
160            case ALL_IN_COLUMN:
161                return _mainArray.get(row).getTotalInOut(true);
162            case ALL_OUT_COLUMN:
163                return _mainArray.get(row).getTotalInOut(false);
164            default:
165                return getValueAtPt5(row,col);
166        }
167    }
168
169    private Object getValueAtPt5(int row, int col) {
170        switch (col) {
171            case STATE_COLUMN:
172                return _mainArray.get(row).getState();
173            case STATUS_REQUEST_BUTTON_COLUMN:
174                return Bundle.getMessage("StatusButton"); // NOI18N
175            case DELETE_BUTTON_COLUMN:
176                return Bundle.getMessage("ButtonDelete"); // NOI18N
177            case STLR_ON_COLUMN:
178                return _mainArray.get(row).getBeans(CbusTableEvent.EvState.ON);
179            case STLR_OFF_COLUMN:
180                return _mainArray.get(row).getBeans(CbusTableEvent.EvState.OFF);
181            default:
182                return getValueAtPt6(row,col);
183        }
184    }
185
186    private Object getValueAtPt6(int row, int col) {
187        switch (col) {
188            case EVENT_DAT_1:
189                return _mainArray.get(row).getData(1);
190            case EVENT_DAT_2:
191                return _mainArray.get(row).getData(2);
192            case EVENT_DAT_3:
193                return _mainArray.get(row).getData(3);
194            default:
195                return null;
196        }
197    }
198
199    /**
200     * {@inheritDoc}
201     */
202    @Override
203    public void setValueAt(Object value, int row, int col) {
204        switch (col) {
205            case NAME_COLUMN:
206                _mainArray.get(row).setName( (String) value );
207                break;
208            case COMMENT_COLUMN:
209                _mainArray.get(row).setComment( (String) value );
210                break;
211            case ON_BUTTON_COLUMN:
212                _mainArray.get(row).sendEvent(CbusTableEvent.EvState.ON);
213                break;
214            case OFF_BUTTON_COLUMN:
215                _mainArray.get(row).sendEvent(CbusTableEvent.EvState.OFF);
216                break;
217            default:
218                setValueAt2( value,  row,  col);
219        }
220    }
221
222    public void setValueAt2(Object value, int row, int col) {
223        switch (col) {
224            case TOGGLE_BUTTON_COLUMN:
225                _mainArray.get(row).sendEvent(CbusTableEvent.EvState.TOGGLE);
226                ta.updateGuiCell(row,col);
227                break;
228            case STATUS_REQUEST_BUTTON_COLUMN:
229                _mainArray.get(row).sendEvent(CbusTableEvent.EvState.REQUEST);
230                break;
231            default:
232                log.error("Invalid Column {}",col);
233                break;
234        }
235    }
236
237
238
239    /**
240     * Provide a new Event and add to Table.
241     * @param nn Node Number
242     * @param en Event Number
243     * @return New or existing table event.
244     */
245    @Nonnull
246    public CbusTableEvent provideEvent(int nn, int en){
247        int row = getEventTableRow(nn,en);
248        if (row>-1){
249            return _mainArray.get(row);
250        }
251        // not existing so creating new
252        CbusTableEvent newtabev = new CbusTableEvent(_memo,nn,en );
253        _mainArray.add(newtabev);
254        int newRow = _mainArray.size() - 1;
255        _rowIndex.put(eventKey(nn, en), newRow);
256        fireTableRowsInserted(newRow, newRow);
257        return newtabev;
258    }
259
260    /**
261     * Cache of Node + Event to table row, so that every incoming event frame
262     * does not need a linear search of the table.
263     * <p>
264     * An entry is only used after checking that the row still holds that event,
265     * so the cache self-corrects after rows are removed or the table cleared.
266     */
267    private final java.util.Map<Integer, Integer> _rowIndex = new java.util.concurrent.ConcurrentHashMap<>();
268
269    private static int eventKey(int node, int event) {
270        return (node << 16) | (event & 0xFFFF);
271    }
272
273    /**
274     * Do Node + Event check, returns -1 if not on table, otherwise the row id
275     * @since 4.13.3
276     * @param event int
277     * @param node int
278     * @return int of row, otherwise -1
279     */
280    public int getEventTableRow( int node, int event) {
281        int key = eventKey(node, event);
282        Integer cached = _rowIndex.get(key);
283        if (cached != null) {
284            int row = cached;
285            if (row < _mainArray.size() && _mainArray.get(row).matches(node, event)) {
286                return row;
287            }
288            _rowIndex.remove(key); // stale, eg. after a row removal
289        }
290        for (int row = 0; row < _mainArray.size(); row++) {
291            if (_mainArray.get(row).matches(node, event)) {
292                _rowIndex.put(key, row);
293                return row;
294            }
295        }
296        return -1;
297    }
298
299    /**
300     * Get event name for an event in the table
301     * @param event int
302     * @param node int
303     * @return String of event name, empty string if not present.
304     */
305    @Nonnull
306    public String getEventName( int node, int event ) {
307        int row = getEventTableRow(node,event);
308        if (row > -1 ) {
309            return _mainArray.get(row).getName();
310        }
311        return "";
312    }
313
314    /**
315     * Get event String for an event in the table
316     * @param event int
317     * @param node int
318     * @return String of event name, empty string if not present.
319     */
320    @Nonnull
321    public String getEventString( int node, int event ) {
322        int row = getEventTableRow(node,event);
323        if (row > -1 ) {
324            return _mainArray.get(row).toString();
325        }
326        return("");
327    }
328
329    /**
330     * Get the core list containing all table events
331     * @return actual array of events
332     */
333    @Nonnull
334    public ArrayList<CbusTableEvent> getEvents() {
335        return new ArrayList<>(_mainArray);
336    }
337
338    private static final Logger log = LoggerFactory.getLogger(CbusBasicEventTableModel.class);
339
340}