001package jmri.jmrix.can.cbus.node;
002
003import java.util.ArrayList;
004import java.util.Date;
005import java.util.Objects;
006import jmri.jmrix.can.CanMessage;
007import jmri.jmrix.can.CanReply;
008import jmri.jmrix.can.CanSystemConnectionMemo;
009import jmri.jmrix.can.cbus.node.CbusNodeConstants.BackupType;
010
011/**
012 * Class to represent a node imported from FCU file or CbusNodeXml.
013 *
014 * @author Steve Young Copyright (C) 2019
015 */
016public class CbusNodeFromBackup extends CbusNode implements Comparable<CbusNodeFromBackup> {
017    
018    private Date _timeStamp;
019    private String _backupComment;
020    private BackupType _backupType;
021    
022    /**
023     * Create a new CbusNodeFrommBackup by connection type and Node Number
024     * 
025     * @param connmemo CAN Connection
026     * @param nodenumber Node Number between 1 and 65535
027     */  
028    public CbusNodeFromBackup ( CanSystemConnectionMemo connmemo, int nodenumber ){
029        super( connmemo, nodenumber );  
030        _backupComment = "";
031    }
032    
033    /**
034     * Create a new CbusNodeFrommBackup from an existing Node
035     * 
036     * @param node The Node to make a copy of
037     * @param timeStamp to set the Backup TimeStamp
038     */  
039    public CbusNodeFromBackup ( CbusNode node, Date timeStamp) {
040        super( null, node.getNodeNumber() ); 
041        _backupComment = "";
042        setBackupResult(BackupType.INCOMPLETE);
043        _timeStamp = timeStamp;
044        if (node.getNodeParamManager().getParameters()!=null) {
045            super.getNodeParamManager().setParameters(node.getNodeParamManager().getParameters());
046        } else {
047            setBackupResult(BackupType.COMPLETEDWITHERROR);
048        }
049        if (node.getNodeNvManager().getNvArray()!=null) {
050            super.getNodeNvManager().setNVs(node.getNodeNvManager().getNvArray());
051        } else {
052            setBackupResult(BackupType.COMPLETEDWITHERROR);
053        }
054        // copy events
055        ArrayList<CbusNodeEvent> _tmpArr = node.getNodeEventManager().getEventArray();
056        if (_tmpArr !=null) {
057            _tmpArr.forEach((ndEv) -> {
058                getNodeEventManager().addNewEvent(new CbusNodeEvent( ndEv ));
059            });
060        } else {
061            setBackupResult(BackupType.COMPLETEDWITHERROR);
062        }
063        if (getBackupResult() == BackupType.INCOMPLETE) {
064            setBackupResult(BackupType.COMPLETE);
065        }
066    }
067    
068    /**
069     * Ignores incoming and outgoing CAN Frames
070     * {@inheritDoc}
071     */
072    @Override
073    public CbusNodeCanListener getNewCanListener(){
074        return new DoNothingCanListener();
075    }
076    
077    /**
078     * Set the Backup DateTime
079     * @param thisDate Timestamp
080     */
081    protected void setBackupTimeStamp( Date thisDate){
082        _timeStamp = thisDate;
083    }
084    
085    /**
086     * Get the Backup DateTime
087     * @return DateTime in format
088     */  
089    public Date getBackupTimeStamp(){
090        return _timeStamp;
091    }
092
093    /**
094     * Set the Backup Result
095     * @param type Backup Type Enum
096     */  
097    protected final void setBackupResult(BackupType type) {
098        _backupType = type;
099    }
100
101    /**
102     * Get the Backup Result
103     * @return enum
104     */  
105    public final BackupType getBackupResult() {
106        return _backupType;
107    }
108    
109    /**
110     * Set the backup comment
111     * @param backupComment  text representation of the single backup state
112     */  
113    public void setBackupComment(String backupComment) {
114        _backupComment = backupComment;
115    }
116    
117    /**
118     * Get the Backup Comment
119     * eg. Completed No Issues, 9 NVs, 12 Events with 4 EVs
120     * 
121     * @return index number, -1 if unset
122     */  
123    public String getBackupComment() {
124        return _backupComment;
125    }
126    
127    /**
128     * Add an event to the Node in backup format
129     * 
130     * @param nn Event Node Number
131     * @param en Event Event Number
132     * @param evVars Event Variable Hex String eg. "0102DC3AFF"
133     */
134    public void addBupEvent(int nn, int en, String evVars){
135        CbusNodeEvent buildEv = new CbusNodeEvent( nn, en , getNodeNumber(), evVars);
136        getNodeEventManager().addNewEvent(buildEv);
137    }
138    
139    /**
140     * Get a String comparison with another CbusNodeFromBackup
141     * 
142     * @param toTest The CbusNodeFromBackup to test against
143     * @return eg. "Parameters Changed"
144     */
145    public String compareWithString( CbusNodeFromBackup toTest) {
146        
147        if (toTest==null){
148            return ("");
149        }
150        
151        if (equals(toTest)) {
152            return Bundle.getMessage("NoChanges");
153        }
154        
155        StringBuilder text = new StringBuilder();
156        
157        if (!(getNodeParamManager().getParameterHexString().equals(toTest.getNodeParamManager().getParameterHexString()))){
158            text.append("Parameters Changed"+" ");
159        }
160        
161        if (!(getNodeNvManager().getNvHexString().equals(toTest.getNodeNvManager().getNvHexString()))){
162            text.append("NV's Changed"+" ");
163        }
164        
165        if ( getNodeEventManager().getTotalNodeEvents() != toTest.getNodeEventManager().getTotalNodeEvents() ) {
166            text.append("Number Events Changed"+" ");
167        } else if (getEventArrayHash()!=toTest.getEventArrayHash()){
168            text.append("Events Changed"+" ");
169        }
170        
171        if (text.toString().isEmpty()) {
172            text.append(Bundle.getMessage("NoChanges"));
173        }
174        
175        return text.toString();
176        
177    }
178    
179    /** 
180     * {@inheritDoc}
181     * Compares to the Time Date Stamp of the Backup
182     */
183    @Override
184    public int compareTo(CbusNodeFromBackup o) {
185        return this.getBackupTimeStamp().compareTo(o.getBackupTimeStamp());
186    }
187    
188    /** 
189     * {@inheritDoc} 
190     * <p>
191     * Used for highlighting changes to Node Backups,
192     * so the Date Time Stamp does NOT need to be equal.
193     * checking for Node Number, Parameters, NVs, Events.
194     * Events can be in any order, are sorted mid comparison.
195     */
196    @Override
197    public boolean equals(Object obj) {
198        if (obj == this) {
199            return true;
200        }
201        if (!(obj instanceof CbusNodeFromBackup)) {
202            return false;
203        }
204        CbusNodeFromBackup t = (CbusNodeFromBackup) obj;
205        if (this.getNodeNumber() != t.getNodeNumber()) {
206            return false;
207        }
208        if (!(this.getNodeParamManager().getParameterHexString().equals(t.getNodeParamManager().getParameterHexString()))){
209            return false;
210        }
211        if (!(this.getNodeNvManager().getNvHexString().equals(t.getNodeNvManager().getNvHexString()))){
212            return false;
213        }
214        if ( this.getNodeEventManager().getTotalNodeEvents() != t.getNodeEventManager().getTotalNodeEvents() ) {
215            return false;
216        }
217        
218        ArrayList<CbusNodeEvent> thisEvs = this.getNodeEventManager().getEventArray();
219        ArrayList<CbusNodeEvent> otherEvs = t.getNodeEventManager().getEventArray();
220        
221        if ( thisEvs !=null && otherEvs !=null) {
222            java.util.Collections.sort(thisEvs);
223            java.util.Collections.sort(otherEvs);
224        }
225        return this.getEventArrayHash() == t.getEventArrayHash();
226    }
227    
228    /** 
229     * {@inheritDoc}
230     */
231    @Override public int hashCode() {
232        return Objects.hash(getNodeNumber(),getNodeParamManager().getParameterHexString(),
233            getNodeNvManager().getNvHexString(),getEventArrayHash());
234    }
235    
236    /** 
237     * Get a Hashcode for the Event Array
238     * @return 0 if event array null
239     */
240    public int getEventArrayHash(){
241        ArrayList<CbusNodeEvent> _tmpArr = getNodeEventManager().getEventArray();
242        if ( _tmpArr == null ) {
243            return 0;
244        } else {
245            return _tmpArr.hashCode();
246        }
247    }
248    
249    /**
250     * toString reports the Node Number Name and backup timestamp
251     * @return string eg "1234 UserName Backup Sun Jul 07 22:41:22".
252     * {@inheritDoc} 
253     */
254    @Override
255    public String toString(){
256        return super.toString()+ " Backup " + getBackupTimeStamp();
257    }
258    
259    /**
260     * Ignores Incoming and Outgoing CAN Frames.
261     */
262    protected static class DoNothingCanListener extends CbusNodeCanListener {
263
264        public DoNothingCanListener(){
265            super(null,null);
266        }
267        
268        /**
269         * Ignores outgoing CAN Frames.
270         * {@inheritDoc}
271         */
272        @Override
273        public void message(CanMessage m) {}
274
275        /**
276         * Ignores incoming CAN Frames.
277         * {@inheritDoc}
278         */
279        @Override
280        public void reply(CanReply m) {}
281    
282}
283    
284    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusNodeFromBackup.class);
285    
286}