/*******************************************************************************
 * Copyright (c) 2004, 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - Initial API and implementation
 *******************************************************************************/

package org.eclipse.wst.xml.core.internal.search.matching;

import org.eclipse.core.resources.IFile;
import org.eclipse.wst.common.core.search.SearchMatch;
import org.eclipse.wst.common.core.search.SearchParticipant;
import org.eclipse.wst.common.core.search.SearchRequestor;
import org.eclipse.wst.common.core.search.pattern.QualifiedName;
import org.eclipse.wst.common.core.search.pattern.SearchPattern;
import org.eclipse.wst.xml.core.internal.search.XMLComponentSearchPattern;
import org.eclipse.wst.xml.core.internal.search.XMLSearchPattern;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


public class XMLSearchPatternMatcher extends PatternMatcher{
	
   /**
    * The active search participant.
    */
    private SearchParticipant searchParticipant;
  
    /**
     * Constructs a pattern matcher given a search participant.
     * @param searchParticipant the active {@link SearchParticipant search participant}.
     */
    public XMLSearchPatternMatcher(SearchParticipant searchParticipant)
    {
      this.searchParticipant = searchParticipant;
    }
    
	protected String computeNamespaceForPrefix(Element element, String prefix)
	{
	  String result = null;
	  for (Node node = element; node != null; node = node.getParentNode())
	  {
		if (node.getNodeType() == Node.ELEMENT_NODE)
		{
		  Element e = (Element)node;
		  String attributeName = (prefix != null && prefix.length() > 0) ? ("xmlns:" + prefix) : "xmlns";  //$NON-NLS-1$ //$NON-NLS-2$
		  result = e.hasAttribute(attributeName) ? e.getAttribute(attributeName) : null;
		  if (result != null && result.length() > 0)
		  {
			 break;  
		  }	  
		}	
	  }	  
	  return result;
	}
	

	protected void initialize(XMLSearchPattern pattern, Element domElement) {
	
			pattern.setElementName(domElement.getLocalName());
			pattern.setElementNamespace(domElement.getNamespaceURI());
            // TODO (cs) set the depth attribute on the pattern
            //
			String actualValue = domElement.hasAttribute(pattern.getAttributeName()) ? domElement.getAttribute(pattern.getAttributeName()) : null;
			 if(actualValue != null && actualValue.length() > 0){
				 pattern.setSearchElement(domElement);
				 pattern.setSearchName(actualValue);
				 pattern.setSearchNamespace(domElement.getOwnerDocument().getDocumentElement().getAttribute("targetNamespace")); // set a default
			    }
		
	}
	
	protected void initialize(XMLSearchPattern pattern, SAXSearchElement saxElement) {
		
		pattern.setElementName(saxElement.getElementName());
		pattern.setElementNamespace(saxElement.getElementNamespace());
        pattern.setDepth(saxElement.getDepth());
        pattern.setParentName(saxElement.getParentName());
		String actualValue = saxElement.getAttributes().getValue(pattern.getAttributeName());
		 if(actualValue != null){
			 pattern.setSearchElement(saxElement);
			 pattern.setSearchName(actualValue);
			 pattern.setSearchNamespace(saxElement.getTargetNamespace()); // set a default
		    }
	
	}
	
	XMLSearchPattern searchPattern;
	
	public XMLSearchPatternMatcher() {
		super();
		
	}

	

	
	/**
	 * This method does dive actual match location to the requestor if there are matches
	 */
	
	public void locateMatches(SearchPattern pattern, IFile file, Element element, SearchRequestor requestor) {
		if(pattern instanceof XMLComponentSearchPattern){
			XMLSearchPattern[] childPatterns = ((XMLComponentSearchPattern)pattern).getChildren(searchParticipant);
			if (childPatterns == null){
			  return;
			}
			for (int i = 0; i < childPatterns.length; i++) {
				PatternMatcher matcher = (PatternMatcher)childPatterns[i].getAdapter(PatternMatcher.class);
				if(matcher == null){
					matcher = this;
				}
				if(matcher != null){
					matcher.locateMatches(childPatterns[i], file, element, requestor);
				}
			}
		}
		else if(pattern instanceof XMLSearchPattern){
			if(matches(pattern, element)){
				Attr attribute = element.getAttributeNode(((XMLSearchPattern)pattern).getAttributeName());
				addMatch(pattern, file, attribute, requestor);
			}
		
		}
	}
	
	/**
	 * This method only answers if the pattern matches element, it does not give actual match location
	 */
	public boolean matches(SearchPattern pattern, Object element){
		if(pattern instanceof XMLComponentSearchPattern){
			XMLSearchPattern[] childPatterns = ((XMLComponentSearchPattern)pattern).getChildren(searchParticipant);
            if (childPatterns == null){
              return false;
            }
			for (int i = 0; i < childPatterns.length; i++) {
				PatternMatcher matcher = (PatternMatcher)childPatterns[i].getAdapter(PatternMatcher.class);
				if(matcher == null){
					matcher = this;
				}
				if(matcher != null){
					if(matcher.matches(childPatterns[i], element)){
						return true;
					}
				}
				
			}
		}
		else if(pattern instanceof XMLSearchPattern){
			
			XMLSearchPattern possibleMatch = new XMLSearchPattern();
			possibleMatch.setAttributeName(((XMLSearchPattern)pattern).getAttributeName());
			if(element instanceof Element){
				initialize(possibleMatch, (Element)element);
			}
			else if(element instanceof SAXSearchElement){
				initialize(possibleMatch, (SAXSearchElement)element);
			}
			searchPattern = (XMLSearchPattern)pattern;
			return matchesPattern(possibleMatch);
		}
		return false;
	}
	
	protected boolean matchesPattern(SearchPattern pattern) {
		if(searchPattern != null && pattern instanceof XMLSearchPattern){
			XMLSearchPattern decodedPattern = (XMLSearchPattern)pattern;
            if(searchPattern.getElementName().equals(decodedPattern.getElementName()) &&                    
					searchPattern.getElementNamespace().equals(decodedPattern.getElementNamespace())){                
                if(searchPattern.getParentName() != null &&
                   decodedPattern.getParentName() != null &&
                   !searchPattern.getParentName().equals(decodedPattern.getParentName()))
                {
                  return false;  
                }  
                return searchPattern.matches(decodedPattern);
			}
		}
		
		return false;
	}
	
    
    protected SearchMatch createSearchMatch(IFile file, Attr attributeNode)
    {
      SearchMatch match = super.createSearchMatch(file, attributeNode);
      // todo... remove this ugly hack!!
      if ("name".equals(attributeNode.getName())) //$NON-NLS-1$
      {
        QualifiedName qualifiedName = new QualifiedName("todo-compute-targetNamespace", attributeNode.getValue()); //$NON-NLS-1$
        match.map.put("name", qualifiedName); //$NON-NLS-1$
      }
      //Element element = attributeNode.getOwnerDocument().getDocumentElement();
      //if (element.getAttribute("targetNamespace"))
      //{        
      //}  
      return match;
    }

}
