package be.iminds.ilabt.jfed.rspec.parser.extensions;

import be.iminds.ilabt.jfed.lowlevel.api.user_spec.UserSpec;
import be.iminds.ilabt.jfed.rspec.parser.RspecParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;

import static be.iminds.ilabt.jfed.rspec.parser.RspecXmlConstants.*;

/**
 * User: twalcari
 * Date: 3/9/2015
 * Time: 10:57
 */
public class UserSpecParser implements Callable<List<UserSpec>> {
    private static final Logger LOG = LoggerFactory.getLogger(UserSpecParser.class);
    private final String input;

    public UserSpecParser(String input) {
        this.input = input;
    }

    @Override
    public List<UserSpec> call() throws Exception {

        try {
            XMLInputFactory xif = XMLInputFactory.newFactory();
            StreamSource xml = new StreamSource(new StringReader("<fake-root>" + input + "</fake-root>"));
            XMLEventReader eventReader = xif.createXMLEventReader(xml);

            XMLEvent e = eventReader.nextEvent();
            assert e.isStartDocument();

            assert eventReader.hasNext();

            List<UserSpec> userSpecs = new ArrayList<>();
            while (eventReader.hasNext()) {
                e = eventReader.nextEvent();

                if (e.isStartElement()) {
                    if (Objects.equals(e.asStartElement().getName(), Q_JFEDSSH_USER_SSH_KEYS)) {
                        userSpecs.add(parseJfedSshUserSshKeys(e.asStartElement(), eventReader));
                    }
                }

            }
            return userSpecs;

        } catch (XMLStreamException e) {
            throw new RspecParseException(e.getLocation(), "Problem parsing RSpec: " + e.getMessage(), e);
        } catch (AssertionError | Exception e) {
            throw new RspecParseException(null, "Problem parsing RSpec", e);
        }

    }

    public static UserSpec parseJfedSshUserSshKeys(StartElement startElement, XMLEventReader eventReader) throws XMLStreamException, RspecParseException {
        assert Objects.equals(startElement.getName(), Q_JFEDSSH_USER_SSH_KEYS);


        Attribute attUser = startElement.getAttributeByName(Q_JFEDSSH_USER);

        if (attUser == null)
            LOG.warn("Expected attribute user in user-ssh-keys, but got null");

        String user = attUser != null ? attUser.getValue() : null;


        List<String> keys = new ArrayList<>();

        while (eventReader.hasNext()) {
            XMLEvent e = eventReader.nextEvent();

            if (e.isStartElement()) {
                if (Objects.equals(e.asStartElement().getName(), Q_JFEDSSH_KEY)) {
                    e = eventReader.nextEvent();

                    if (!e.isCharacters())
                        throw new RspecParseException(e.getLocation(), "Expected characters in sshkey");
                    keys.add(e.asCharacters().getData());
                }
            }

            if(e.isEndElement() && Objects.equals(e.asEndElement().getName(), Q_JFEDSSH_USER_SSH_KEYS)){
                return new UserSpec(user, keys);
            }
        }

        throw new RspecParseException(null, "Did not encounter end element for " + Q_JFEDSSH_USER_SSH_KEYS);
    }

}

