/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.installer.core;

import org.jboss.installer.auto.AutomaticInstallationParsingException;
import org.jboss.installer.auto.InstallationDataSerializer;
import org.jboss.installer.auto.ListXMLEventReader;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;

public abstract class FlatListPostInstallConfig implements InstallationData.PostInstallConfig {
    @Override
    public XMLEventReader serialize(XMLEventFactory eventFactory, Set<String> variables) {
        List<XMLEvent> events = new ArrayList<>();
        events.add(eventFactory.createStartElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "attribute-list"));
        for (Map.Entry<String, String> entry : listAttributes().entrySet()) {
            events.addAll(createAttribute(eventFactory, entry.getKey(), entry.getValue()));
        }
        events.add(eventFactory.createEndElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "attribute-list"));
        variables.addAll(listVariables());
        return new ListXMLEventReader(events);
    }

    protected abstract Map<String, String> listAttributes();

    protected Set<String> listVariables() {
        return Collections.emptySet();
    }

    @Override
    public void deserialize(XMLEventReader reader, BiFunction<String, String, String> variableResolver) throws AutomaticInstallationParsingException {
        try {
            Map<String, String> attributes = new HashMap<>();
            String attrName = null;
            String attrValue = null;
            while (reader.hasNext()) {
                final XMLEvent xmlEvent = reader.nextEvent();
                if (xmlEvent.isStartElement()) {
                    final StartElement elem = xmlEvent.asStartElement();
                    if (elem.getName().getLocalPart().equals("attribute")) {
                        attrName = null;
                        attrValue = null;
                    }
                    if (elem.getName().getLocalPart().equals("name")) {
                        attrName = reader.nextEvent().asCharacters().getData();
                    }
                    if (elem.getName().getLocalPart().equals("value")) {
                        XMLEvent e;
                        StringBuilder sb = new StringBuilder();
                        // concat all characters until end of value
                        while ((e = reader.nextEvent()).isCharacters()) {
                            sb.append(e.asCharacters().getData());
                        }
                        attrValue = sb.toString();
                    }
                }
                if (xmlEvent.isEndElement()) {
                    final EndElement elem = xmlEvent.asEndElement();
                    if (elem.getName().getLocalPart().equals("attribute")) {
                        attributes.put(attrName, attrValue);
                    }
                }
            }
            acceptAttributes(attributes, variableResolver);
        } catch (XMLStreamException e) {
            throw InstallationDataSerializer.unableToParse(e);
        }
    }

    protected abstract void acceptAttributes(Map<String, String> attributes, BiFunction<String, String, String> variableResolver) throws AutomaticInstallationParsingException;

    protected List<XMLEvent> createAttribute(XMLEventFactory eventFactory, String attrName, String attrValue) {
        if (attrValue == null) {
            return Collections.emptyList();
        }
        List<XMLEvent> events = new ArrayList<>();
        events.add(eventFactory.createStartElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "attribute"));
        events.add(eventFactory.createStartElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "name"));
        events.add(eventFactory.createCharacters(attrName));
        events.add(eventFactory.createEndElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "name"));
        events.add(eventFactory.createStartElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "value"));
        events.add(eventFactory.createCharacters(attrValue));
        events.add(eventFactory.createEndElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "value"));
        events.add(eventFactory.createEndElement(InstallationDataSerializer.PREFIX, InstallationDataSerializer.NS, "attribute"));
        return events;
    }
}
