/*
 * Copyright 2004-2010 the Seasar Foundation and the Others.
 *
 * Licensed 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.seasar.mayaa.impl.builder;

import java.util.Iterator;

import org.seasar.mayaa.builder.PathAdjuster;
import org.seasar.mayaa.builder.SequenceIDGenerator;
import org.seasar.mayaa.builder.library.LibraryManager;
import org.seasar.mayaa.cycle.ServiceCycle;
import org.seasar.mayaa.cycle.scope.ApplicationScope;
import org.seasar.mayaa.engine.processor.TemplateProcessor;
import org.seasar.mayaa.engine.specification.Namespace;
import org.seasar.mayaa.engine.specification.NodeAttribute;
import org.seasar.mayaa.engine.specification.PrefixAwareName;
import org.seasar.mayaa.engine.specification.PrefixMapping;
import org.seasar.mayaa.engine.specification.QName;
import org.seasar.mayaa.engine.specification.SpecificationNode;
import org.seasar.mayaa.engine.specification.URI;
import org.seasar.mayaa.impl.CONST_IMPL;
import org.seasar.mayaa.impl.cycle.CycleUtil;
import org.seasar.mayaa.impl.engine.EngineUtil;
import org.seasar.mayaa.impl.engine.processor.TemplateProcessorSupport;
import org.seasar.mayaa.impl.engine.specification.SpecificationUtil;
import org.seasar.mayaa.impl.provider.ProviderUtil;
import org.seasar.mayaa.impl.util.StringUtil;

/**
 * @author Masataka Kurihara (Gluegent, Inc.)
 */
public class BuilderUtil implements CONST_IMPL {

    private BuilderUtil() {
        // no instantiation.
    }

    public static SpecificationNode createInjectedNode(QName qName,
            URI uri, SpecificationNode original, boolean mayaa) {
        if (qName == null || original == null) {
            throw new IllegalArgumentException();
        }
        String systemID = original.getSystemID();
        int lineNumber = original.getLineNumber();
        boolean onTemplate = original.isOnTemplate();
        int sequenceID = original.getSequenceID();
        SpecificationNode node = SpecificationUtil.createSpecificationNode(
                qName, systemID, lineNumber, onTemplate, sequenceID);
        if (StringUtil.hasValue(uri)) {
            PathAdjuster adjuster = ProviderUtil.getPathAdjuster();

            boolean needAdjust = adjuster.isTargetNode(original.getQName());
            String basePath = null;
            if (needAdjust) {
                String contextPath = CycleUtil.getRequestScope().getContextPath();
                String sourcePath = EngineUtil.getSourcePath(original);
                basePath = contextPath + sourcePath;
            }

            for (Iterator it = original.iterateAttribute(); it.hasNext();) {
                NodeAttribute attr = (NodeAttribute) it.next();
                String attrValue = attr.getValue();
                if (needAdjust
                        && adjuster.isTargetAttribute(
                                original.getQName(), attr.getQName())) {
                    attrValue =
                        adjuster.adjustRelativePath(basePath, attrValue);
                }
                String originalName = null;
                if (StringUtil.isEmpty(attr.getPrefix()) == false
                        && attr.getQName().getLocalName().indexOf(':') < 0) {
                    originalName =
                        attr.getPrefix() + ":"
                        + attr.getQName().getLocalName();
                }
                node.addAttribute(attr.getQName(), originalName, attrValue);
            }

            for (Iterator it = original.iteratePrefixMapping(false); it.hasNext();) {
                PrefixMapping prefixMapping = (PrefixMapping) it.next();
                node.addPrefixMapping(
                        prefixMapping.getPrefix(), prefixMapping.getNamespaceURI());
            }
        }
        //node.setParentSpace(SpecificationUtil.getFixedNamespace(original.getParentSpace()));
        node.setParentSpace(original.getParentSpace());
        node.setDefaultNamespaceURI(original.getDefaultNamespaceURI());
        return node;
    }

    /**
     * 指定したsystemIDからmimeTypeを見て、HTMLならHTML用のデフォルトPrefixMappingを、
     * それ以外ならXMLのデフォルトPrefixMappingを返します。
     *
     * @param systemID mime-typeを調べるsystemID
     * @return 適切なデフォルトPrefixMapping
     */
    static PrefixMapping getPrefixMapping(String systemID) {
        ServiceCycle cycle = CycleUtil.getServiceCycle();
        ApplicationScope application = cycle.getApplicationScope();
        String mimeType = application.getMimeType(systemID);
        if (mimeType != null) {
            if (mimeType.indexOf("xhtml") != -1) {
                return SpecificationUtil.XHTML_DEFAULT_PREFIX_MAPPING;
            } else if (mimeType.indexOf("html") != -1) {
                return SpecificationUtil.HTML_DEFAULT_PREFIX_MAPPING;
            }
        }
        return SpecificationUtil.XML_DEFAULT_PREFIX_MAPPING;

    }

    /**
     * 現在ServiceCycleからmimeTypeを見て、HTMLならHTML用のデフォルトPrefixMappingを、
     * それ以外ならXMLのデフォルトPrefixMappingを返します。
     *
     * @return 適切なデフォルトPrefixMapping
     */
    static PrefixMapping getDefaultPrefixMapping() {
        return getPrefixMapping(CycleUtil.getServiceCycle().getOriginalNode().getSystemID());
    }

    public static PrefixAwareName parseName(
            Namespace namespace, String qName) {
        String[] parsed = qName.split(":");
        String prefix = null;
        String localName = null;
        URI namespaceURI = null;
        PrefixMapping mapping = null;
        if (parsed.length == 2) {
            prefix = parsed[0];
            localName = parsed[1];
            mapping = namespace.getMappingFromPrefix(prefix, true);
            if (mapping == null) {
                if ("xml".equals(prefix)) {
                    mapping = SpecificationUtil.XML_DEFAULT_PREFIX_MAPPING;
                } else {
                    // HTA:Applicationのような無作法なものもあるので対応できるようにする。
                    mapping = getDefaultPrefixMapping();
                    localName = qName;  // プレフィクスも含めて要素名扱いとする。
                    prefix = "";
                }
            }
            namespaceURI = mapping.getNamespaceURI();
        } else if (parsed.length == 1) {
            localName = parsed[0];
            namespaceURI = namespace.getDefaultNamespaceURI();

            if (namespaceURI == null) {
                mapping = namespace.getMappingFromPrefix("", true);
                if (mapping == null) {
                    mapping = getDefaultPrefixMapping();
                }
                namespaceURI = mapping.getNamespaceURI();
            }
        } else {
            throw new IllegalNameException(qName);
        }
        if (mapping != null) {
            prefix = mapping.getPrefix();
        } else {
            prefix = "";
        }
        PrefixAwareName ret = SpecificationUtil.createPrefixAwareName(
                SpecificationUtil.createQName(namespaceURI, localName),
                prefix);
        return ret;
    }

    static SpecificationNode nodeCopyOf(SpecificationNode node, QName newQName,
            SequenceIDGenerator sequenceIDGenerator) {
        if (newQName == null) {
            newQName = node.getQName();
        }
        return SpecificationUtil.createSpecificationNode(newQName,
                node.getSystemID(), node.getLineNumber(),
                true, sequenceIDGenerator.nextSequenceID());
    }

    public static void characterProcessorCopy(TemplateProcessor from,
            TemplateProcessorSupport to, SequenceIDGenerator idGenerator) {
        to.setOriginalNode(from.getOriginalNode());
        to.setInjectedNode(from.getInjectedNode());
        to.setEvalBodyInclude(false);
        LibraryManager libraryManager = ProviderUtil.getLibraryManager();
        to.setProcessorDefinition(
                libraryManager.getProcessorDefinition(CONST_IMPL.QM_CHARACTERS));
    }

}
