001package jmri.jmrix.can.cbus.node;
002
003import java.util.ArrayList;
004import javax.annotation.CheckForNull;
005import javax.annotation.Nonnull;
006import jmri.jmrix.can.CanSystemConnectionMemo;
007import jmri.util.ThreadingUtil;
008
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Table data model for display of CBUS Nodes
014 *
015 * @author Steve Young (c) 2019
016 * 
017 */
018public class CbusBasicNodeTableOperations extends CbusBasicNodeTable {
019
020    public CbusBasicNodeTableOperations(@Nonnull CanSystemConnectionMemo memo, int row, int column) {
021        super(memo,row,column);        
022    }
023    
024    /**
025     * Register new node to table
026     * @param node The CbusNode to add to the table
027     */
028    public void addNode(CbusNode node) {
029        _mainArray.add(node);
030        
031        if (this instanceof CbusNodeTableDataModel) {
032            node.setTableModel( (CbusNodeTableDataModel)this);
033            node.addPropertyChangeListener((CbusNodeTableDataModel)this);
034            ((CbusNodeTableDataModel) this).startBackgroundFetch();
035        }
036        if (_mainArray.size()==1){
037            setRequestNodeDisplay(node.getNodeNumber());
038        }
039        // notify the JTable object that a row has changed; do that in the Swing thread!
040        fireTableDataChanged();
041    }
042    
043    /**
044     * Returns an existing command station by cs number, NOT node number
045     * @param csnum The Command Station Number ( the default in CBUS is 0 )
046     * @return the Node which has the command station number, else null
047     */
048    @CheckForNull
049    public CbusNode getCsByNum(int csnum) {
050        for (int i = 0; i < getRowCount(); i++) {
051            if ( _mainArray.get(i).getCsNum() == csnum ) {
052                return _mainArray.get(i);
053            }
054        }
055        return null;
056    }
057
058    /**
059     * Returns a new or existing command station by cs number, NOT node number
060     * 
061     * @param csnum The Command Station Number to provide by
062     * @param nodenum if existing CS sets node num to this, else node with this number and starts param lookup
063     * 
064     * @return the Node which has the command station number
065     */
066    @Nonnull
067    protected CbusNode provideCsByNum(int csnum, int nodenum) {
068        for (int i = 0; i < getRowCount(); i++) {
069            if ( _mainArray.get(i).getCsNum() == csnum ) {
070                _mainArray.get(i).setNodeNumber(nodenum);
071                return _mainArray.get(i);
072            }
073        }
074        CbusNode cs = provideNodeByNodeNum( nodenum);
075        cs.setCsNum(csnum);
076        return cs;
077    }
078    
079    /**
080     * Returns a new or existing node by node number
081     * 
082     * @param nodenum number to search nodes by, else creates node with this number and starts param lookup
083     * 
084     * @return the Node which has the node number
085     */
086    @Nonnull
087    public CbusNode provideNodeByNodeNum(int nodenum ) {
088        if ( nodenum < 1 || nodenum > 65535 ) {
089            throw new IllegalArgumentException("Node number should be between 1 and 65535");
090        }
091        for (int i = 0; i < getRowCount(); i++) {
092            if ( _mainArray.get(i).getNodeNumber() == nodenum ) {
093                return _mainArray.get(i);
094            }
095        }
096        CbusNode cs = new CbusNode(_memo, nodenum);
097        addNode(cs);
098        return cs;        
099    }
100    
101    /**
102     * Returns an existing node by table row number
103     * @param rowNum The Row Number
104     * @return the Node
105     */
106    public CbusNode getNodeByRowNum(int rowNum) {
107        return _mainArray.get(rowNum);
108    }
109    
110    /**
111     * Returns the table row number by node number
112     * @param nodenum The Node Number ( min 1, max 65535 )
113     * @return the Model Row which has the node number, else -1
114     */    
115    public int getNodeRowFromNodeNum(int nodenum) {
116        for (int i = 0; i < getRowCount(); i++) {
117            if ( _mainArray.get(i).getNodeNumber() == nodenum ) {
118                return i;
119            }
120        }
121        return -1;
122    }
123
124    /**
125     * For a given CAN ID, if in use, return formatted Node Name and number
126     * else returns zero length string
127     * @param canId the CAN ID to search the table for
128     * @return Node Number and name
129     */
130    public String getNodeNameFromCanId (int canId) {
131        for (int i = 0; i < getRowCount(); i++) {
132            if ( _mainArray.get(i).getNodeCanId() == canId ) {
133                return _mainArray.get(i).getNodeStats().getNodeNumberName();
134            }
135        }
136        return ("");
137    }
138
139    /**
140     * Returns Node number of any node currently in Learn Mode
141     * @return Node Num, else -1 if no nodes known to be in learn mode
142     */ 
143    public int getAnyNodeInLearnMode(){
144        for (int i = 0; i < getRowCount(); i++) {
145            if ( _mainArray.get(i).getNodeInLearnMode() ) {
146                return _mainArray.get(i).getNodeNumber();
147            }
148        }
149        return -1;
150    }
151    
152    /**
153     * Returns an existing node by node number
154     * @param nodenum The Node Number ( min 1, max 65535 )
155     * @return the Node which has the node number, else null
156     */
157    @CheckForNull
158    public CbusNode getNodeByNodeNum( int nodenum ) {
159        for (int i = 0; i < getRowCount(); i++) {
160            if ( _mainArray.get(i).getNodeNumber() == nodenum ) {
161                return _mainArray.get(i);
162            }
163        }
164        return null;        
165    }
166    
167    /**
168     * Remove Row from table and dispose of it
169     * @param row int row number
170     * @param removeXml true to also remove the Node xml file
171     */
172    public void removeRow(int row, boolean removeXml) {
173        CbusNode toRemove = getNodeByNodeNum( _mainArray.get(row).getNodeNumber() );
174        _mainArray.remove(row);
175        if (toRemove != null) {
176            if (this instanceof CbusNodeTableDataModel) {
177                toRemove.removePropertyChangeListener((CbusNodeTableDataModel)this );
178            }
179            if (removeXml) {
180                // delete xml file
181                if (!(toRemove.getNodeBackupManager().removeNode(true))){
182                    log.error("Unable to delete node xml file");
183                }
184            }
185            ThreadingUtil.runOnGUI( ()->{ fireTableRowsDeleted(row,row); });
186            toRemove.dispose();
187        }
188    }
189    
190    /**
191     * Returns the next available Node Number
192     * @param higherthan Node Number
193     * @return calculated next available number, else original value
194     */
195    public int getNextAvailableNodeNumber( int higherthan ) {
196        if ( getRowCount() > 0 ) {
197            for (int i = 0; i < getRowCount(); i++) {
198                // log.debug("get next available i {} rowcount {}",i,getRowCount() );
199                if ( _mainArray.get(i).getNodeNumber() < 65534 ) {
200                    if ( _mainArray.get(i).getNodeNumber() >= higherthan ) {
201                        higherthan = _mainArray.get(i).getNodeNumber() + 1;
202                    }
203                }
204            }
205        }
206        return higherthan;
207    }
208    
209    /**
210     * Returns a string ArrayList of all Node Number and User Names on the table
211     * @return Node Number + either node model or Username.
212     */
213    @Nonnull
214    public ArrayList<String> getListOfNodeNumberNames(){
215        ArrayList<String> list = new ArrayList<>();
216        for (int i = 0; i < getRowCount(); i++) {
217            list.add( _mainArray.get(i).getNodeStats().getNodeNumberName() );
218        }
219        return list;
220    }
221    
222    /**
223     * Returns formatted Node Number and User Name by node number
224     * @param nodenum The Node Number ( min 1, max 65535 )
225     * @return Node Number + either node model or Username.
226     */    
227    public String getNodeNumberName( int nodenum ) {
228        for (int i = 0; i < getRowCount(); i++) {
229            if ( _mainArray.get(i).getNodeNumber() == nodenum ) {
230                return _mainArray.get(i).getNodeStats().getNodeNumberName();
231            }
232        }
233        return ("");
234    }
235    
236    /**
237     * Single Node User Name
238     * @param nn Node Number, NOT row number
239     * @return Node Username, if unset returns node type name, else empty String
240     */
241    @Nonnull
242    public String getNodeName( int nn ) {
243        int rownum = getNodeRowFromNodeNum(nn);
244        if ( rownum < 0 ) {
245            return "";
246        }
247        if ( !_mainArray.get(rownum).getUserName().isEmpty() ) {
248            return _mainArray.get(rownum).getUserName();
249        }
250        if ( !_mainArray.get(rownum).getNodeStats().getNodeTypeName().isEmpty() ) {
251            return _mainArray.get(rownum).getNodeStats().getNodeTypeName();
252        }        
253        return "";
254    }
255    
256    private int requestNodeToDisplay = 0;
257    
258    public int getRequestNodeRowToDisplay(){
259        return getNodeRowFromNodeNum(requestNodeToDisplay);
260    }
261    
262    public void setRequestNodeDisplay(int nodeNumber){
263        requestNodeToDisplay = nodeNumber;
264    }
265
266    private final static Logger log = LoggerFactory.getLogger(CbusBasicNodeTableOperations.class);
267}