/*
 * Copyright 2013-2015 Grzegorz Ligas <[email protected]> and other contributors
 * (see the CONTRIBUTORS file).
 *
 * 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.intellij.xquery.reference.namespace;

import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementResolveResult;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.ResolveState;
import com.intellij.psi.util.PsiTreeUtil;
import org.intellij.xquery.psi.XQueryAttrLocalName;
import org.intellij.xquery.psi.XQueryDirAttribute;
import org.intellij.xquery.psi.XQueryDirAttributeList;
import org.intellij.xquery.psi.XQueryDirAttributeName;
import org.intellij.xquery.psi.XQueryPsiElement;
import org.intellij.xquery.psi.XQueryXmlEmptyTag;
import org.intellij.xquery.psi.XQueryXmlFullTag;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static org.intellij.xquery.reference.namespace.XQuery30PredeclaredNamespaces.XMLNS;

public class XQueryXmlNamespaceReference<T extends XQueryPsiElement> extends XQueryPrefixReference<T> {
    public XQueryXmlNamespaceReference(T element, TextRange textRange) {
        super(element, textRange);
    }

    @Override
    protected Collection<ResolveResult> getPrimaryReferences() {
        XQueryAttrLocalName matchingNamespaceDeclaration = getMatchingInlineNamespaceDeclaration();
        if (matchingNamespaceDeclaration == null) {
            matchingNamespaceDeclaration = getReferenceFromXmlUpInTheHierarchy();
        }
        if (matchingNamespaceDeclaration != null) {
            return convertToResolveResults(matchingNamespaceDeclaration);
        } else {
            return Collections.emptyList();
        }
    }

    private XQueryAttrLocalName getMatchingInlineNamespaceDeclaration() {
        List<XQueryDirAttributeName> attributeNameList = getAttributeNames();
        XQueryAttrLocalName matchingAttribute = null;
        for (XQueryDirAttributeName attributeName : attributeNameList) {
            if (isInlineNamespaceDeclaration(attributeName, myElement.getText())) {
                matchingAttribute = attributeName.getAttrLocalName();
                break;
            }
        }
        return matchingAttribute;
    }

    private List<XQueryDirAttributeName> getAttributeNames() {
        XQueryDirAttributeList attributeList = getAttributeList();
        List<XQueryDirAttributeName> names = new ArrayList<XQueryDirAttributeName>();
        for (XQueryDirAttribute dirAttribute : attributeList.getDirAttributeList()) {
            names.add(dirAttribute.getDirAttributeName());
        }
        return names;
    }

    private XQueryDirAttributeList getAttributeList() {
        PsiElement grandParent = myElement.getParent().getParent();
        XQueryDirAttributeList attributeList;
        if (grandParent instanceof XQueryXmlFullTag) {
            attributeList = ((XQueryXmlFullTag) grandParent).getDirAttributeList();
        } else if (grandParent instanceof XQueryXmlEmptyTag) {
            attributeList = ((XQueryXmlEmptyTag) grandParent).getDirAttributeList();
        } else {
            attributeList = (XQueryDirAttributeList) grandParent.getParent();
        }
        return attributeList;
    }

    private boolean isInlineNamespaceDeclaration(XQueryDirAttributeName attributeName, String namespaceName) {
        return attributeName.getAttrNamespace() != null
                && XMLNS.getPrefix().equals(attributeName.getAttrNamespace().getText())
                && namespaceName.equals(attributeName.getAttrLocalName().getText());
    }

    private Collection<ResolveResult> convertToResolveResults(XQueryAttrLocalName localName) {
        Collection<ResolveResult> result = new ArrayList<ResolveResult>();
        result.add(new PsiElementResolveResult(localName));
        return result;
    }

    private XQueryAttrLocalName getReferenceFromXmlUpInTheHierarchy() {
        XmlTagNamespaceReferenceScopeProcessor processor = new XmlTagNamespaceReferenceScopeProcessor<T>(myElement);
        PsiTreeUtil.treeWalkUp(processor, myElement, null, ResolveState.initial());
        if (processor.getResult() != null) {
            return processor.getResult();
        } else {
            return null;
        }
    }

    @Override
    protected boolean includeOnlyPrimaryReferencesIfPresent() {
        return true;
    }
}