001package jmri.jmrix.ecos;
002
003import java.util.Arrays;
004import java.util.List;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Carries the reply to an EcosMessage.
010 * <p>
011 * Some rudimentary support is provided for the "binary" option.
012 *
013 * @author Bob Jacobsen Copyright (C) 2001, 2008
014 * @author Daniel Boudreau Copyright (C) 2007
015 */
016public class EcosReply extends jmri.jmrix.AbstractMRReply {
017
018    // create a new one
019    public EcosReply() {
020        super();
021    }
022
023    public EcosReply(String s) {
024        super(s);
025    }
026
027    public EcosReply(EcosReply l) {
028        super(l);
029    }
030
031    // these can be very large
032    @Override
033    public int maxSize() {
034        return 10000;
035    }
036
037    // no need to do anything
038    @Override
039    protected int skipPrefix(int index) {
040        return index;
041    }
042
043    @Override
044    public int value() {
045        if (isBinary()) {
046            return getElement(0) & 0xFF;  // avoid stupid sign extension
047        } else {
048            return super.value();
049        }
050    }
051
052    //knowing where the end is we can then determine the error code
053    int endAtElement = -1;
054
055    /**
056     * Check for last line starts with {@code "<END "}.
057     * @return true if contains END, else false.
058     */
059    public boolean containsEnd() {
060        for (int i = 0; i < getNumDataElements() - 6; i++) {
061            if ((getElement(i) == 0x0A)
062                    && (getElement(i + 1) == '<')
063                    && (getElement(i + 2) == 'E')
064                    && (getElement(i + 3) == 'N')
065                    && (getElement(i + 4) == 'D')
066                    && (getElement(i + 5) == ' ')) {
067                endAtElement = i;
068                return true;
069            }
070        }
071        return false;
072    }
073
074    /**
075     * Get the Result Code.
076     * @return result code, else -1 if the end code has not been found.
077     */
078    public int getResultCode() {
079        if (!containsEnd()) {
080            log.error("Trying to get message end code before message is complete");
081            return -1;
082        }
083        if (endAtElement == -1) {
084            //just a double check incase endAtElement never got set.
085            return -1;
086        }
087        String resultCode = Character.toString((char) (getElement(endAtElement + 6)));
088        resultCode = resultCode + (char) (getElement(endAtElement + 7));
089        resultCode = resultCode.trim();
090
091        try {
092            return Integer.parseInt(resultCode);
093        } catch (java.lang.NumberFormatException ex) {
094            log.error("Unable to convert result code to a number {}", resultCode);
095            return -1;
096        }
097    }
098
099    /**
100     * Is this EcosReply actually an independent {@code <EVENT} message?
101     * @return true if it is an independent message
102     */
103    boolean isEvent() {
104        if (getNumDataElements() < 8) {
105            return false;
106        }
107        if (getElement(0) != '<') {
108            return false;
109        }
110        if (getElement(1) != 'E') {
111            return false;
112        }
113        if (getElement(2) != 'V') {
114            return false;
115        }
116        if (getElement(3) != 'E') {
117            return false;
118        }
119        if (getElement(4) != 'N') {
120            return false;
121        }
122        if (getElement(5) != 'T') {
123            return false;
124        }
125        if (getElement(6) != ' ') {
126            return false;
127        }
128        return true;
129    }
130
131    //An event message is Unsolicited
132    @Override
133    public boolean isUnsolicited() {
134        if (isEvent()) {
135            setUnsolicited();
136            return true;
137        } else {
138            return false;
139        }
140    }
141
142    public boolean isReply() {
143        if (getNumDataElements() < 8) {
144            return false;
145        }
146        if (getElement(0) != '<') {
147            return false;
148        }
149        if (getElement(1) != 'R') {
150            return false;
151        }
152        if (getElement(2) != 'E') {
153            return false;
154        }
155        if (getElement(3) != 'P') {
156            return false;
157        }
158        if (getElement(4) != 'L') {
159            return false;
160        }
161        if (getElement(5) != 'Y') {
162            return false;
163        }
164        if (getElement(6) != ' ') {
165            return false;
166        }
167        return true;
168    }
169
170    String replyType = null;
171
172    public String getReplyType() {
173        if (!isReply()) {
174            return "";
175        }
176        if (replyType != null) {
177            return replyType;
178        }
179        StringBuilder sb = new StringBuilder();
180        for (int i = 7; i < getNumDataElements(); i++) {
181            if (getElement(i) == '(') {
182                break;
183            }
184            sb.append((char) getElement(i));
185        }
186        replyType = sb.toString();
187        return sb.toString();
188    }
189
190    List<String> headerDetails = null;
191
192    public List<String> getReplyHeaderDetails() {
193        if (!isReply()) {
194            return Arrays.asList(new String[0]);
195        }
196        if (headerDetails != null) {
197            return headerDetails;
198        }
199        StringBuilder sb = new StringBuilder();
200        int iOffSet = 8 + getReplyType().length() + ("" + getEcosObjectId()).length();
201        if (getElement(iOffSet) == ',') {
202            iOffSet++;
203        }
204        if (getElement(iOffSet) == ' ') {
205            iOffSet++;
206        }
207        for (int i = iOffSet; i < getNumDataElements(); i++) {
208            if (getElement(i) == ')') {
209                break;
210            }
211            sb.append((char) getElement(i));
212        }
213        headerDetails = Arrays.asList(sb.toString().split(","));
214        return headerDetails;
215    }
216
217    int objectId = -1;
218
219    public int getEcosObjectId() {
220        if (objectId != -1) {
221            return objectId;
222        }
223
224        StringBuilder sb = new StringBuilder();
225        int iOffSet = 7 + getReplyType().length();
226        if (!isEvent()) {
227            iOffSet = iOffSet + 1;
228        }
229        for (int i = iOffSet; i < getNumDataElements(); i++) {
230            if (getElement(i) == '>') {
231                break;
232            } else if (getElement(i) == ',') {
233                break;
234            } else if (getElement(i) == ')') {
235                break;
236            }
237            sb.append((char) getElement(i));
238        }
239        objectId = Integer.parseInt(sb.toString());
240        return objectId;
241    }
242
243    /**
244     * get the contents of the reply, excluding the header and footer
245     * @return array with reply values.
246     */
247    public String[] getContents() {
248        String[] lines = toString().split("\n");
249        int length = lines.length - 2;
250
251        String[] returnval = new String[length];
252        for (int i = 1; i <= length; i++) {
253            returnval[i - 1] = lines[i];
254        }
255        return returnval;
256    }
257
258    static public String getContentDetail(String str) {
259        int start = str.indexOf("[") + 1;
260        int end = str.indexOf("]");
261        return str.substring(start, end);
262    }
263
264    static public String getContentDetails(String line, String item) {
265        int startval = line.indexOf(item) + item.length() + 1;
266        int endval = (line.substring(startval)).indexOf("]") + startval;
267        return line.substring(startval, endval);
268    }
269
270    private final static Logger log = LoggerFactory.getLogger(EcosReply.class);
271
272}