/**
 * 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
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.apache.cxf.javascript;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import org.apache.cxf.common.xmlschema.SchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaAttribute;

public class NamespacePrefixAccumulator {
    private StringBuilder attributes;
    private Set<String> prefixes;
    private Map<String, String> fallbackNamespacePrefixMap;
    private int nsCounter;
    private SchemaCollection schemaCollection;

    public NamespacePrefixAccumulator(SchemaCollection schemaCollection) {
        attributes = new StringBuilder();
        prefixes = new HashSet<>();
        fallbackNamespacePrefixMap = new HashMap<>();
        nsCounter = 0;
        this.schemaCollection = schemaCollection;
    }

    public void collect(String prefix, String uri) {
        if (!("".equals(uri)) && !prefixes.contains(prefix)) {
            attributes.append("xmlns:").append(prefix).append("='").append(uri).append("' ");
            prefixes.add(prefix);
        }
    }

    public String getAttributes() {
        return attributes.toString();
    }

    private String getPrefix(String namespaceURI) {
        if ("".equals(namespaceURI)) {
            throw new RuntimeException("Prefix requested for default namespace.");
        }
        String schemaPrefix = schemaCollection.getNamespaceContext().getPrefix(namespaceURI);
        // there could also be a namespace context on an individual schema info.
        // perhaps SchemaCollection should be enforcing some discipline there.
        if (schemaPrefix == null || "tns".equals(schemaPrefix)) {
            schemaPrefix = fallbackNamespacePrefixMap.get(namespaceURI);
            if (schemaPrefix == null) {
                schemaPrefix = "jns" + nsCounter;
                nsCounter++;
                fallbackNamespacePrefixMap.put(namespaceURI, schemaPrefix);
            }
        }
        return schemaPrefix;
    }

    /**
     * This function obtains a name, perhaps namespace-qualified, for an element.
     * @param elementQName the element.
     * @param qualified whether to qualify.
     * @return
     */
    public String xmlElementString(QName elementQName, boolean qualified) {
        if (qualified) {
            // What if there were a prefix in the element's qname? This is not apparently
            // something that happens in this environment.
            String prefix = getPrefix(elementQName.getNamespaceURI());
            collect(prefix, elementQName.getNamespaceURI());
            return prefix + ":" + elementQName.getLocalPart();
        }
        return elementQName.getLocalPart(); // use the non-qualified name.
    }

    /**
     * Obtain a suitable name for use in Javascript for an attribute. This function
     * is purely a tribute to the awful modularity of XmlSchema.
     * @param attribute
     * @param qualified
     * @return
     */
    public String xmlAttributeString(XmlSchemaAttribute attribute, boolean qualified) {
        if (qualified) {
            // What if there were a prefix in the element's qname? This is not apparently
            // something that happens in this environment.
            String prefix = getPrefix(attribute.getQName().getNamespaceURI());
            collect(prefix, attribute.getQName().getNamespaceURI());
            return prefix + ":" + attribute.getName();
        }
        return attribute.getName(); // use the non-qualified name.
    }

    public String xmlElementString(QName name) { // used with part concrete names
        if ("".equals(name.getNamespaceURI())) {
            return name.getLocalPart();
        }

        String prefix = getPrefix(name.getNamespaceURI());
        collect(prefix, name.getNamespaceURI());
        return prefix + ":" + name.getLocalPart();
    }
}