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