package org.xbib.io.webdav.common;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xbib.io.webdav.api.DavConstants;
import org.xbib.io.webdav.api.Namespace;
import org.xbib.io.webdav.api.XMLizable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <code>OptionsResponse</code> encapsulates the DAV:options-response element
 * present in the response body of a successful OPTIONS request (with body).
 * <br>
 * The DAV:options-response element is defined to have the following format.
 *
 * <pre>
 * &lt;!ELEMENT options-response ANY&gt;
 * ANY value: A sequence of elements
 * </pre>
 * <p>
 * Please note, that <code>OptionsResponse</code> represents a simplified implementation
 * of the given structure. We assume, that there may only entries that consist
 * of a qualified name and a set of href child elements.
 *
 */
public class OptionsResponse implements XMLizable {

    private final Map<String, Entry> entries = new HashMap<>();

    /**
     * Add a new entry to this <code>OptionsResponse</code> and make each
     * href present in the String array being a separate {@link DavConstants#XML_HREF DAV:href}
     * element within the entry.
     *
     * @param localName
     * @param namespace
     * @param hrefs
     */
    public void addEntry(String localName, Namespace namespace, String[] hrefs) {
        Entry entry = new Entry(localName, namespace, hrefs);
        entries.put(DomUtil.getExpandedName(localName, namespace), entry);
    }

    /**
     * @param localName
     * @param namespace
     * @return
     */
    public String[] getHrefs(String localName, Namespace namespace) {
        String key = DomUtil.getExpandedName(localName, namespace);
        if (entries.containsKey(key)) {
            return entries.get(key).hrefs;
        } else {
            return new String[0];
        }
    }

    /**
     * Return the Xml representation.
     *
     * @param document
     * @return Xml representation.
     * @see XMLizable#toXml(Document)
     */
    public Element toXml(Document document) {
        Element optionsResponse = DomUtil.createElement(document, "options-response", DavConstants.NAMESPACE);
        for (Entry entry : entries.values()) {
            Element elem = DomUtil.addChildElement(optionsResponse, entry.localName, entry.namespace);
            for (String href : entry.hrefs) {
                elem.appendChild(DomUtil.hrefToXml(href, document));
            }
        }
        return optionsResponse;
    }

    /**
     * Build a new <code>OptionsResponse</code> object from the given xml element.
     *
     * @param orElem
     * @return a new <code>OptionsResponse</code> object
     * @throws IllegalArgumentException if the specified element is <code>null</code>
     *                                  or if its name is other than 'DAV:options-response'.
     */
    public static OptionsResponse createFromXml(Element orElem) {
        if (!DomUtil.matches(orElem, "options-response", DavConstants.NAMESPACE)) {
            throw new IllegalArgumentException("DAV:options-response element expected");
        }
        OptionsResponse oResponse = new OptionsResponse();
        ElementIterator it = DomUtil.getChildren(orElem);
        while (it.hasNext()) {
            Element el = it.nextElement();
            List<String> hrefs = new ArrayList<String>();
            ElementIterator hrefIt = DomUtil.getChildren(el, DavConstants.XML_HREF, DavConstants.NAMESPACE);
            while (hrefIt.hasNext()) {
                hrefs.add(DomUtil.getTextTrim(hrefIt.nextElement()));
            }
            oResponse.addEntry(el.getLocalName(), DomUtil.getNamespace(el), hrefs.toArray(new String[hrefs.size()]));
        }
        return oResponse;
    }

    private static class Entry {

        private final String localName;
        private final Namespace namespace;
        private final String[] hrefs;

        private Entry(String localName, Namespace namespace, String[] hrefs) {
            this.localName = localName;
            this.namespace = namespace;
            this.hrefs = hrefs;
        }
    }
}
