package be.iminds.ilabt.jfed.experimenter_gui.bugreporting;

import be.iminds.ilabt.jfed.bugreport.*;

import be.iminds.ilabt.jfed.call_log_output.LogOutput;
import be.iminds.ilabt.jfed.call_log_output.SerializableApiCallDetails;
import be.iminds.ilabt.jfed.call_log_output.SerializableApiCallDetailsReader;
import be.iminds.ilabt.jfed.connectivity_tester.ConnectivityTest;
import be.iminds.ilabt.jfed.experiment.tasks.ExperimentTaskStatus;
import be.iminds.ilabt.jfed.highlevel.controller.TaskExecution;
import be.iminds.ilabt.jfed.log.cache.ApiCallDetailsCache;
import be.iminds.ilabt.jfed.log.cache.ApiCallDetailsRef;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.ErrorDetails;
import be.iminds.ilabt.jfed.util.GeniUrn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.Callable;

import static be.iminds.ilabt.jfed.experimenter_gui.bugreporting.BugReportConstants.*;
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;

/**
 * User: twalcari
 * Date: 4/3/2015
 * Time: 14:54
 */
public class BugReportReader implements Callable<BugReport> {
    private static final Logger LOG = LoggerFactory.getLogger(BugReportReader.class);

    private final StreamSource streamSource;

    private final ApiCallDetailsCache cache;

    private final Map<Integer, ApiCallDetailsRef> newRefsMap = new HashMap<>();

    public BugReportReader(InputStream inputStream, ApiCallDetailsCache cache) {
        this.streamSource = new StreamSource(inputStream);
        this.cache = cache;
    }

    public BugReportReader(Reader reader, ApiCallDetailsCache cache) {
        this.streamSource = new StreamSource(reader);
        this.cache = cache;
    }


    @Override
    public BugReport call() throws Exception {
        throw new RuntimeException("no longer implemented");
//        XMLInputFactory xif = XMLInputFactory.newFactory();
//        XMLEventReader eventReader = xif.createXMLEventReader(streamSource);
//
//        XMLEvent e = eventReader.nextEvent();
//        assert e.isStartDocument();
//
//
//        BugReport res = null; //new BugReport();
//
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//
//                if (Objects.equals(e.asStartElement().getName(), Q_BUGREPORT)) {
//                    res = new BugReport();
//                } else if (Objects.equals(e.asStartElement().getName(), Q_DESCRIPTION)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at description", e.getLocation());
//
//                    res.setDescription(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_EMAIL)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at mail", e.getLocation());
//
//                    res.setMail(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_VERSION)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at version", e.getLocation());
//
//                    res.setVersion(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_ENVIRONMENT)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at environment", e.getLocation());
//
//                    res.setEnvironment(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_REPORTER_CREDENTIAL)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at reporter credential", e.getLocation());
//
//                    res.setReporterCredential(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_LOGGEDIN_USER_INFO)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at loggedInUserInfo", e.getLocation());
//
//                    res.setLoggedInUserInfo(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_PREFERENCES)) {
//                    assert (res != null);
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (e.isCharacters())
//                        res.setPreferences(e.asCharacters().getData());
//                    else
//                        assert e.isEndElement();
//                } else if (Objects.equals(e.asStartElement().getName(), Q_CALLS)) {
//                    assert res != null;
//                    res.setCalls(readCalls(eventReader, e));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_STITCHING_REPORTS)) {
//                    assert res != null;
//                    res.setJobReports(readStitchingReports(eventReader, e));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_LOG)) {
//                    assert res != null;
//                    res.setLogLines(readLogLines(eventReader, e));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_CONNECTIVITY_TESTS)) {
//                    assert res != null;
//                    res.setConnectivityTests(readConnectivityTests(eventReader, e));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_SLICES)) {
//                    assert res != null;
//                    res.setSlices(readSlices(eventReader, e));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_SCREENSHOT)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (e.isCharacters())
//                        res.setScreenshot(e.asCharacters().getData());
//                    else if (!e.isEndElement())
//                        throw new BugReportParseException("Expected characters or end-element at " + Q_SCREENSHOT, e.getLocation());
//
//                } else {
//                    logUnexpectedElement(Q_BUGREPORT, e);
//                }
//
//            } else if (e.isEndElement()) {
//                if (Objects.equals(e.asEndElement().getName(), Q_BUGREPORT)) {
//                    return res;
//                } else {
//                    logUnexpectedElement(Q_BUGREPORT, e);
//                }
//
//            } else {
//                logUnexpectedElement(Q_BUGREPORT, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_BUGREPORT, e.getLocation());
    }

//    private List<SerializableApiCallDetails> readCalls(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_CALLS));
//
//
//        List<SerializableApiCallDetails> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_CALL)) {
//                try {
//                    final SerializableApiCallDetails apiCallDetails =
//                            SerializableApiCallDetailsReader.readCall(eventReader, e);
//
//                    res.add(apiCallDetails);
//
//                    ApiCallDetailsRef apiCallDetailsRef = cache.registerApiCallDetails(apiCallDetails);
//                    newRefsMap.put(apiCallDetails.getId(), apiCallDetailsRef);
//                } catch (SerializableApiCallDetailsReader.ApiCallDetailsParseException ex) {
//                    throw new BugReportParseException("Error while reading call", ex, e.getLocation());
//                }
//
//
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_CALLS)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_CALLS, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_CALLS, e.getLocation());
//
//
//    }
//
//    private List<SerializableJobReport> readStitchingReports(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_STITCHING_REPORTS));
//
//        List<SerializableJobReport> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_JOB_REPORT)) {
//                res.add(readJobReport(eventReader, e));
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_STITCHING_REPORTS)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_STITCHING_REPORTS, e);
//            }
//
//        }
//        throw createUnexpectedEndException(Q_STITCHING_REPORTS, e.getLocation());
//    }
//
//    private List<SerializableJobReport> readJobReports(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_JOB_REPORTS));
//
//        List<SerializableJobReport> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_JOB_REPORT)) {
//                res.add(readJobReport(eventReader, e));
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_JOB_REPORTS)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_JOB_REPORTS, e);
//            }
//
//        }
//        throw createUnexpectedEndException(Q_JOB_REPORTS, e.getLocation());
//    }
//
//    private SerializableJobReport readJobReport(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_JOB_REPORT));
//
//        SerializableJobReport res = new SerializableJobReport();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_TITLE)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_TITLE, e.getLocation());
//                    res.setName(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_STATES)) {
//                    res.setJobStateReports(readJobStateReports(eventReader, e));
//                } else {
//                    logUnexpectedElement(Q_JOB_REPORT, e);
//                }
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_JOB_REPORT)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_JOB_REPORT, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_JOB_REPORT, e.getLocation());
//    }
//
//
//    private List<SerializableJobStateReport> readJobStateReports(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_STATES));
//
//        List<SerializableJobStateReport> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_STATE)) {
//                res.add(readJobStateReport(eventReader, e));
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_STATES)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_JOB_REPORTS, e);
//            }
//
//        }
//        throw createUnexpectedEndException(Q_STATES, e.getLocation());
//    }
//
//    private SerializableJobStateReport readJobStateReport(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_STATE));
//
//        SerializableJobStateReport res = new SerializableJobStateReport();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_NAME)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_TITLE, e.getLocation());
//                    res.setName(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_STATESTATE)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_STATESTATE, e.getLocation());
//                    res.setStatus(ExperimentTaskStatus.valueOf(e.asCharacters().getData()));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_START)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_START, e.getLocation());
//                    res.setStart(Instant.from(ISO_OFFSET_DATE_TIME.parse(e.asCharacters().getData())));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_END)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (e.isCharacters()) {
//                        res.setEnd(Instant.from(ISO_OFFSET_DATE_TIME.parse(e.asCharacters().getData())));
//                    } else if (!e.isEndElement()) {
//                        throw new BugReportParseException("Expected characters at " + Q_END, e.getLocation());
//                    }
//                } else if (Objects.equals(e.asStartElement().getName(), Q_LOGENTRIES)) {
//                    res.setLogEntries(readLogEntries(eventReader, e));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_TASK_EXECUTIONS)) {
//                    res.setTaskExecutions(readTaskExecutions(eventReader, e));
//                } else {
//                    logUnexpectedElement(Q_STATE, e);
//                }
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_STATE)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_STATE, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_STATE, e.getLocation());
//    }
//
//    private List<SerializableTaskExecution> readTaskExecutions(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_TASK_EXECUTIONS));
//
//        List<SerializableTaskExecution> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_TASK_EXECUTION)) {
//                res.add(readTaskExecution(eventReader, e));
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_TASK_EXECUTIONS)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_TASK_EXECUTIONS, e);
//            }
//
//        }
//        throw createUnexpectedEndException(Q_TASK_EXECUTIONS, e.getLocation());
//    }
//
//    private SerializableTaskExecution readTaskExecution(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_TASK_EXECUTION));
//
//        SerializableTaskExecution res = new SerializableTaskExecution();
//
//        Attribute idAtt = e.asStartElement().getAttributeByName(Q_ID);
//        Attribute nameAtt = e.asStartElement().getAttributeByName(Q_NAME);
//        Attribute statusAtt = e.asStartElement().getAttributeByName(Q_STATUS);
//        Attribute taskClassAtt = e.asStartElement().getAttributeByName(Q_TASK_CLASS);
//        Attribute startAtt = e.asStartElement().getAttributeByName(Q_START);
//        Attribute endAtt = e.asStartElement().getAttributeByName(Q_END);
//
//        if (idAtt != null)
//            res.setId(idAtt.getValue());
//        if (nameAtt != null)
//            res.setName(nameAtt.getValue());
//        if (statusAtt != null)
//            res.setState(statusAtt.getValue());
////            res.setState(TaskExecution.TaskState.valueOf(statusAtt.getValue()));
//        if (taskClassAtt != null)
//            res.setTaskClass(taskClassAtt.getValue());
//        if (startAtt != null)
//            res.setStart(Instant.from(ISO_OFFSET_DATE_TIME.parse(startAtt.getValue())));
//        if (endAtt != null)
//            res.setEnd(Instant.from(ISO_OFFSET_DATE_TIME.parse(endAtt.getValue())));
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_CALL_REF)) {
//                    int oldId = Integer.parseInt(e.asStartElement().getAttributeByName(Q_CALL_REF_ID).getValue());
//
//                    ApiCallDetailsRef newRef = newRefsMap.get(oldId);
//
//                    if (newRef == null) {
//                        LOG.warn("Could not retrieve ApiCallDetails with ref {}", oldId);
//                    }
//
//                    res.getApiCallDetails().add(newRef);
//
//                } else {
//                    logUnexpectedElement(Q_TASK_EXECUTION, e);
//                }
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_TASK_EXECUTION)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_TASK_EXECUTION, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_TASK_EXECUTION, e.getLocation());
//    }
//
//    private LogLine readLogLine(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_LOGLINE));
//
//        LogLine res = new LogLine();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_LEVEL)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_LEVEL, e.getLocation());
//                    res.setLevel(e.asCharacters().getData());
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_CLASSNAME)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_CLASSNAME, e.getLocation());
//                    res.setClassName(e.asCharacters().getData());
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_DATETIME)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_CLASSNAME, e.getLocation());
//                    res.setDateTime(Date.from(ZonedDateTime.from(ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault()).parse(e.asCharacters().getData())).toInstant()));
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_TIME)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_TIME, e.getLocation());
//
//                    res.setTime(Long.parseLong(e.asCharacters().getData()));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_THREADNAME)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_THREADNAME, e.getLocation());
//                    res.setThreadName(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_MESSAGE)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (e.isCharacters())
//                        res.setMessage(e.asCharacters().getData());
//                    else
//                        assert (e.isEndElement());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_EXCEPTION)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_EXCEPTION, e.getLocation());
//                    res.setException(e.asCharacters().getData());
//                } else {
//                    logUnexpectedElement(Q_LOGLINE, e);
//                }
//            } else if (e.isEndElement()) {
//                if (Objects.equals(e.asEndElement().getName(), Q_LOGLINE)) {
//                    return res;
//                } else {
//                    logUnexpectedElement(Q_LOGLINE, e);
//                }
//            } else {
//                logUnexpectedElement(Q_LOGLINE, e);
//            }
//
//
//        }
//
//        throw createUnexpectedEndException(Q_LOGLINE, e.getLocation());
//    }
//
//    private List<LogLine> readLogLines(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_LOG));
//
//        List<LogLine> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_LOGLINE)) {
//
//                res.add(readLogLine(eventReader, e));
//
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_LOG)) {
//                return res;
//
//            } else {
//                logUnexpectedElement(Q_LOG, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_LOG, e.getLocation());
//    }
//
//    private List<LogOutput.LogEntry> readLogEntries(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_LOGENTRIES));
//
//        List<LogOutput.LogEntry> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_LOGENTRY)) {
//
//                res.add(readLogEntry(eventReader, e));
//
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_LOGENTRIES)) {
//                return res;
//
//            } else {
//                logUnexpectedElement(Q_LOGENTRIES, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_LOGENTRIES, e.getLocation());
//    }
//
//    private LogOutput.LogEntry readLogEntry(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_LOGENTRY));
//        LogOutput.LogEntry res = new LogOutput.LogEntry();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//
//                if (Objects.equals(e.asStartElement().getName(), Q_TYPE)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_TYPE, e.getLocation());
//                    res.setType(LogOutput.LogLineType.valueOf(e.asCharacters().getData()));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_TEXT)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_TEXT, e.getLocation());
//                    res.setText(e.asCharacters().getData());
//                } else if (Objects.equals(e.asStartElement().getName(), Q_TIME_IN_MS)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_TEXT, e.getLocation());
//                    res.setTimeMs(Long.parseLong(e.asCharacters().getData()));
//                } else if (Objects.equals(e.asStartElement().getName(), Q_EXCEPTION)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_EXCEPTION, e.getLocation());
//                    res.setException(e.asCharacters().getData());
//                }
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_LOGENTRY)) {
//                return res;
//            } else {
//                logUnexpectedElement(Q_LOGENTRY, e);
//            }
//
//
//        }
//        throw createUnexpectedEndException(Q_LOGENTRY, e.getLocation());
//
//
//    }
//
//    private BRConnectivityTest readConnectivityTest(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_CONNECTIVITY_TEST));
//
//        String name = null;
//        ConnectivityTest.Status status = null;
//        String message = null;
//        String exceptionString = null;
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_NAME)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_NAME, e.getLocation());
//                    name = e.asCharacters().getData();
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_STATUS)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_STATUS, e.getLocation());
//                    try {
//                        status = ConnectivityTest.Status.valueOf(e.asCharacters().getData());
//                    } catch (IllegalArgumentException ex) {
//                        throw new BugReportParseException("Could not parse connectivity status '" + e.asCharacters().getData() + "'", ex, e.getLocation());
//                    }
//                } else if (Objects.equals(e.asStartElement().getName(), Q_MESSAGE)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_MESSAGE, e.getLocation());
//                    message = e.asCharacters().getData();
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_EXCEPTION)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_EXCEPTION, e.getLocation());
//
//                    exceptionString = e.asCharacters().getData();
//                } else {
//                    logUnexpectedElement(Q_CONNECTIVITY_TEST, e);
//                }
//            } else if (e.isEndElement()) {
//                if (Objects.equals(e.asEndElement().getName(), Q_CONNECTIVITY_TEST)) {
//                    return new BRConnectivityTest(name, status, message, exceptionString);
//                } else {
//                    logUnexpectedElement(Q_CONNECTIVITY_TEST, e);
//                }
//            } else {
//                logUnexpectedElement(Q_CONNECTIVITY_TEST, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_CONNECTIVITY_TEST, e.getLocation());
//    }
//
//    private List<BRConnectivityTest> readConnectivityTests(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_CONNECTIVITY_TESTS));
//
//        List<BRConnectivityTest> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_CONNECTIVITY_TEST)) {
//
//                res.add(readConnectivityTest(eventReader, e));
//
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_CONNECTIVITY_TESTS)) {
//                return res;
//
//            } else {
//                logUnexpectedElement(Q_CONNECTIVITY_TESTS, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_CONNECTIVITY_TESTS, e.getLocation());
//    }
//
//
//    private BRSlice readSlice(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_SLICE));
//
//        String sliceUrn = null;
//        String status = null;
//        List<BRSliver> slivers = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_SLICE_URN)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_SLICE_URN, e.getLocation());
//                    sliceUrn = e.asCharacters().getData();
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_CURRENT_STATUS)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_CURRENT_STATUS, e.getLocation());
//                    status = e.asCharacters().getData();
//                } else if (Objects.equals(e.asStartElement().getName(), Q_SLIVER)) {
//                    assert (eventReader.hasNext());
//                    slivers.add(readSliver(eventReader, e));
//                } else {
//                    logUnexpectedElement(Q_SLICE, e);
//                }
//            } else if (e.isEndElement()) {
//                if (Objects.equals(e.asEndElement().getName(), Q_SLICE)) {
//                    return new BRSlice(sliceUrn, status, !slivers.isEmpty() ? slivers : null);
//                } else {
//                    logUnexpectedElement(Q_SLICE, e);
//                }
//            } else {
//                logUnexpectedElement(Q_SLICE, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_SLICE, e.getLocation());
//    }
//
//    private List<BRSlice> readSlices(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_SLICES));
//
//        List<BRSlice> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_SLICE)) {
//
//                res.add(readSlice(eventReader, e));
//
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_SLICES)) {
//                return res;
//
//            } else {
//                logUnexpectedElement(Q_SLICES, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_SLICES, e.getLocation());
//    }
//
//    private BRSliver readSliver(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_SLIVER));
//
//        List<ErrorDetails> errorDetails = new ArrayList<>();
//        BRSliver res = new BRSliver();
//        res.setErrorDetails(errorDetails);
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement()) {
//                if (Objects.equals(e.asStartElement().getName(), Q_SLIVER_URN)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_SLICE_URN, e.getLocation());
//                    res.setSliverUrn(e.asCharacters().getData());
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_CURRENT_STATUS)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_SLICE_URN, e.getLocation());
//                    res.setCurrentStatus(e.asCharacters().getData());
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_COMPONENTS)) {
//                    res.setComponents(readComponents(eventReader, e));
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_SLIVER_STATUS)) {
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_SLICE_URN, e.getLocation());
//                    res.setSliverStatus(e.asCharacters().getData());
//
//                } else if (Objects.equals(e.asStartElement().getName(), Q_ERROR)) {
//
//                    long timestamp =
//                            ZonedDateTime.from(ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault()).parse(e.asStartElement().getAttributeByName(Q_TIMESTAMP).getValue())).toEpochSecond();
//
//                    GeniUrn sliverUrn = null;
//
//                    if (e.asStartElement().getAttributeByName(Q_SLIVER_URN) != null) {
//                        sliverUrn = GeniUrn.parse(e.asStartElement().getAttributeByName(Q_SLIVER_URN).getValue());
//
//                        if (sliverUrn == null) {
//                            LOG.warn("Could not parse {} into a URN", e.asStartElement().getAttributeByName(Q_SLIVER_URN).getValue());
//                        }
//                    }
//                    GeniUrn componentUrn = null;
//
//                    if (e.asStartElement().getAttributeByName(Q_COMPONENT_URN) != null) {
//                        componentUrn = GeniUrn.parse(e.asStartElement().getAttributeByName(Q_COMPONENT_URN).getValue());
//                        if (componentUrn == null) {
//                            LOG.warn("Could not parse {} into a URN", e.asStartElement().getAttributeByName(Q_COMPONENT_URN).getValue());
//                        }
//                    }
//
//
//                    assert (eventReader.hasNext());
//                    e = eventReader.nextEvent();
//
//                    if (!e.isCharacters())
//                        throw new BugReportParseException("Expected characters at " + Q_SLICE_URN, e.getLocation());
//                    String error = e.asCharacters().getData();
//
//
//                    errorDetails.add(new ErrorDetails(error, sliverUrn, componentUrn, timestamp));
//
//                } else {
//                    logUnexpectedElement(Q_SLIVER, e);
//                }
//            } else if (e.isEndElement()) {
//                if (Objects.equals(e.asEndElement().getName(), Q_SLIVER)) {
//                    return res;
//                } else {
//                    logUnexpectedElement(Q_SLIVER, e);
//                }
//            } else {
//                logUnexpectedElement(Q_SLIVER, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_SLIVER, e.getLocation());
//    }
//
//    private List<String> readComponents(XMLEventReader eventReader, XMLEvent e) throws XMLStreamException, BugReportParseException {
//        assert (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_COMPONENTS));
//
//        List<String> res = new ArrayList<>();
//
//        while (eventReader.hasNext()) {
//            e = eventReader.nextEvent();
//
//            if (e.isStartElement() && Objects.equals(e.asStartElement().getName(), Q_COMPONENT)) {
//                assert (eventReader.hasNext());
//                e = eventReader.nextEvent();
//
//                if (!e.isCharacters())
//                    throw new BugReportParseException("Expected characters at " + Q_COMPONENT, e.getLocation());
//
//                res.add(e.asCharacters().getData());
//
//            } else if (e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_COMPONENTS)) {
//                return res;
//
//            } else {
//                logUnexpectedElement(Q_COMPONENTS, e);
//            }
//        }
//
//        throw createUnexpectedEndException(Q_COMPONENTS, e.getLocation());
//    }
//
//
//    public static class BugReportParseException extends Exception {
//        private final Location location;
//
//        public BugReportParseException(Location location) {
//            this.location = location;
//        }
//
//        public BugReportParseException(Throwable cause, Location location) {
//            super(cause);
//            this.location = location;
//        }
//
//        public BugReportParseException(String message, Location location) {
//            super(message);
//            this.location = location;
//        }
//
//        public BugReportParseException(String message, Throwable cause, Location location) {
//            super(message, cause);
//            this.location = location;
//        }
//
//        public BugReportParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Location location) {
//            super(message, cause, enableSuppression, writableStackTrace);
//            this.location = location;
//        }
//
//
//        public Location getLocation() {
//            return location;
//        }
//
//        @Override
//        public String getMessage() {
//            if (location != null) {
//                return super.getMessage() + " @ " + location;
//            } else
//                return super.getMessage();
//        }
//    }
//
//
//    private static BugReportParseException createUnexpectedEndException(QName name, Location location) {
//        return new BugReportParseException("Expected to encounter end-tag of " + name.getLocalPart() + ", but never found it before end of stream.", location);
//
//    }
//
//    private void logUnexpectedElement(QName parent, XMLEvent event) {
//        if (event.isCharacters() && event.asCharacters().getData().trim().isEmpty())
//            return; //this is to be expected!
//        if (event.isEndElement())
//            return; //we also want to ignore these!
//
//        StringWriter stringWriter = new StringWriter();
//        try {
//            event.writeAsEncodedUnicode(stringWriter);
//        } catch (XMLStreamException e) {
//            e.printStackTrace();
//        }
//        String eventContents = stringWriter.toString().trim();
//
//        LOG.warn("Encountered unexpected event {} ({}) while parsing {}@{}", event, eventContents, parent.getLocalPart(), event.getLocation());
//    }
}
