001package jmri.jmrix.lenz.messageformatters;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import jmri.jmrix.Message;
005import jmri.jmrix.lenz.FeedbackItem;
006import jmri.jmrix.lenz.XNetConstants;
007import jmri.jmrix.lenz.XNetReply;
008import jmri.jmrix.lenz.XPressNetMessageFormatter;
009
010import java.util.Optional;
011
012/**
013 * Format XPressNet feedback reply messages for display.
014 *
015 * @author Paul Bender Copyright (C) 2024
016 */
017public class XNetFeedbackReplyFormatter implements XPressNetMessageFormatter {
018    private static final String COLUMN_STATE = "ColumnState";
019    private static final String MAKE_LABEL = "MakeLabel";
020    private static final String POWER_STATE_ON = "PowerStateOn";
021    private static final String POWER_STATE_OFF = "PowerStateOff";
022    private static final String BEAN_NAME_TURNOUT = "BeanNameTurnout";
023    private static final String X_NET_REPLY_NOT_OPERATED = "XNetReplyNotOperated";
024    private static final String X_NET_REPLY_THROWN_LEFT = "XNetReplyThrownLeft";
025    private static final String X_NET_REPLY_THROWN_RIGHT = "XNetReplyThrownRight";
026    private static final String X_NET_REPLY_INVALID = "XNetReplyInvalid";
027    private static final String X_NET_REPLY_CONTACT_LABEL = "XNetReplyContactLabel";
028
029    @Override
030    public boolean handlesMessage(Message m) {
031        return m instanceof XNetReply && m.getElement(0) == XNetConstants.ACC_INFO_RESPONSE;
032    }
033
034    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "cast is checked in handlesMessage")
035    @Override
036    public String formatMessage(Message m) {
037        if (!handlesMessage(m)) {
038            throw new IllegalArgumentException("Message not supported");
039        }
040        StringBuilder text = new StringBuilder().append(Bundle.getMessage("XNetReplyFeedbackLabel")).append(" ");
041        XNetReply r = (XNetReply) m;
042        int numDataBytes = r.getElement(0) & 0x0f;
043        for (int i = 1; i < numDataBytes; i += 2) {
044            switch (r.getFeedbackMessageType(i)) {
045                case 0:
046                    text.append(getTurnoutReplyMonitorString(i, "TurnoutWoFeedback",r));
047                    break;
048                case 1:
049                    text.append(getTurnoutReplyMonitorString(i, "TurnoutWFeedback",r));
050                    break;
051                case 2:
052                    text.append(getSensorFeedBackReplyMonitorString(r, i));
053                    break;
054                default:
055                    text.append(r.getElement(i)).append(" ").append(r.getElement(i + 1));
056            }
057        }
058        return text.toString();
059    }
060
061    private String getSensorFeedBackReplyMonitorString(XNetReply r, int i) {
062        StringBuilder text = new StringBuilder();
063        text.append(Bundle.getMessage("XNetReplyFeedbackEncoder")).append(" ").append(r.getFeedbackEncoderMsgAddr(i));
064        boolean highnibble = ((r.getElement(i + 1) & 0x10) == 0x10);
065        text.append(" ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 5 : 1);
066
067        text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ")
068                .append(((r.getElement(i + 1) & 0x01) == 0x01) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF));
069        text.append("; ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 6 : 2);
070
071        text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ")
072                .append(((r.getElement(i + 1) & 0x02) == 0x02) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF));
073        text.append("; ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 7 : 3);
074
075        text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ")
076                .append(((r.getElement(i + 1) & 0x04) == 0x04) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF));
077        text.append("; ").append(Bundle.getMessage(X_NET_REPLY_CONTACT_LABEL)).append(" ").append(highnibble ? 8 : 4);
078
079        text.append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ")
080                .append(((r.getElement(i + 1) & 0x08) == 0x08) ? Bundle.getMessage(POWER_STATE_ON) : Bundle.getMessage(POWER_STATE_OFF));
081        text.append("; ");
082        return text.toString();
083    }
084
085    private String getTurnoutReplyMonitorString(int startByte, String typeBundleKey,XNetReply r) {
086        StringBuilder text = new StringBuilder();
087        int turnoutMsgAddr = r.getTurnoutMsgAddr(startByte);
088        Optional<FeedbackItem> feedBackOdd = r.selectTurnoutFeedback(turnoutMsgAddr);
089        if(feedBackOdd.isPresent()){
090            FeedbackItem feedbackItem = feedBackOdd.get();
091            text.append(singleTurnoutMonitorMessage(Bundle.getMessage(typeBundleKey), turnoutMsgAddr, feedbackItem));
092            text.append(";");
093            FeedbackItem pairedItem = feedbackItem.pairedAccessoryItem();
094            text.append(singleTurnoutMonitorMessage("", turnoutMsgAddr + 1, pairedItem));
095
096        }
097        return text.toString();
098    }
099
100    private String singleTurnoutMonitorMessage(String prefix, int turnoutMsgAddr, FeedbackItem feedbackItem) {
101        StringBuilder outputBuilder = new StringBuilder();
102        outputBuilder.append(prefix).append(" ")
103                .append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(BEAN_NAME_TURNOUT))).append(" ")
104                .append(turnoutMsgAddr).append(" ").append(Bundle.getMessage(MAKE_LABEL, Bundle.getMessage(COLUMN_STATE))).append(" ");
105        switch (feedbackItem.getAccessoryStatus()){
106            case 0:
107                outputBuilder.append(Bundle.getMessage(X_NET_REPLY_NOT_OPERATED)); // last items on line, no trailing space
108                break;
109            case 1:
110                outputBuilder.append(Bundle.getMessage(X_NET_REPLY_THROWN_LEFT));
111                break;
112            case 2:
113                outputBuilder.append(Bundle.getMessage(X_NET_REPLY_THROWN_RIGHT));
114                break;
115            default:
116                outputBuilder.append(Bundle.getMessage(X_NET_REPLY_INVALID));
117        }
118        if(feedbackItem.getType()==1){
119            outputBuilder.append(" ");
120            if(feedbackItem.isMotionComplete()){
121                outputBuilder.append(Bundle.getMessage("XNetReplyMotionComplete"));
122            } else {
123                outputBuilder.append(Bundle.getMessage("XNetReplyMotionIncomplete"));
124            }
125        }
126        return outputBuilder.toString();
127    }
128
129}
130