/*******************************************************************************
 * Copyright (c) 2007-2012 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.text.ext.hyperlink;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.text.IDocument;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.sse.core.internal.provisional.StructuredModelManager;
import org.eclipse.wst.sse.ui.internal.util.Sorter;
import org.jboss.tools.common.text.ext.util.StructuredModelWrapper;
import org.jboss.tools.common.text.ext.util.Utils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
 * @author Igels
 */
public abstract class AbstractHyperlinkPartitioner implements IHyperlinkPartitioner {

    public IHyperlinkRegion getChildPartitionRegion(IDocument document, int offset, IHyperlinkRegion superRegion) {
        IHyperlinkRegion childRegion = parse(document, offset, superRegion);
        if (childRegion == null) return null;
        HyperlinkPartitionerDefinition[] hyperlinkPartitionerDefinitions = HyperlinkPartitionerBuilder.getInstance().getHyperlinkPartitionerDefinitions(childRegion.getContentType(), childRegion.getType(), childRegion.getAxis());

        List<IHyperlinkPartitioner> partitioners = new ArrayList<IHyperlinkPartitioner>();
        for(int i=0; i<hyperlinkPartitionerDefinitions.length; i++) {
            HyperlinkPartitionerDefinition def = hyperlinkPartitionerDefinitions[i];
            IHyperlinkPartitioner partitioner = def.createHyperlinkPartitioner();
            if (partitioner != null)
            	partitioners.add(partitioner);
        }
        IHyperlinkPartitioner[] sortedPartitioners = orderHyperlinkPartitioners(partitioners.toArray(new IHyperlinkPartitioner[0])); 
        
        for(int i=0; sortedPartitioners != null && i<sortedPartitioners.length; i++) {
            IHyperlinkPartitioner partitioner = sortedPartitioners[i];
            if((!(partitioner instanceof IHyperlinkPartitionRecognizer)) || 
            		((IHyperlinkPartitionRecognizer)partitioner).recognize(document, offset, childRegion)) {
            	if (partitioner instanceof IExclusiblePartitionerRecognition) {
            		IExclusiblePartitionerRecognition epr = (IExclusiblePartitionerRecognition)partitioner;
            		IHyperlinkPartitioner replacement = findExclusionPartitioner(epr.getExclusionPartitionType(), hyperlinkPartitionerDefinitions, document, offset, childRegion);
            		if (replacement != null)
            			return replacement.getChildPartitionRegion(document, offset, childRegion);
            	}
                return partitioner.getChildPartitionRegion(document, offset, childRegion);
            }
        }

        return childRegion;
    }

	protected Sorter createSorter() {
		return new Sorter() {
			public boolean compare(Object partitioner1, Object partitioner2) {
				IHyperlinkPartitioner p1 = (IHyperlinkPartitioner)partitioner1;
				IHyperlinkPartitioner p2 = (IHyperlinkPartitioner)partitioner2;

				String s1 = ((IHyperlinkPartitioner) partitioner1).getClass().getName();
				String s2 = ((IHyperlinkPartitioner) partitioner2).getClass().getName();

				int pp1 = Integer.MAX_VALUE;
				int pp2 = Integer.MAX_VALUE;
				
				if (p1 instanceof IHyperLinkPartitionPriority)
					pp1 = ((IHyperLinkPartitionPriority)p1).getPriority();

				if (p2 instanceof IHyperLinkPartitionPriority)
					pp2 = ((IHyperLinkPartitionPriority)p2).getPriority();
				
				if (pp1 == pp2) {
					return s2.compareTo(s1) > 0;
				}

				return (pp1 < pp2);
			}
		};
	}

	protected IHyperlinkPartitioner[] orderHyperlinkPartitioners(IHyperlinkPartitioner[] partitioners) {
		Object[] sorted = createSorter().sort(partitioners);
		IHyperlinkPartitioner[] sortedPartitioners = new IHyperlinkPartitioner[sorted.length];
		System.arraycopy(sorted, 0, sortedPartitioners, 0, sorted.length);
		return sortedPartitioners;
	}

    
    IHyperlinkPartitioner findExclusionPartitioner (String partitionType, HyperlinkPartitionerDefinition[] hyperlinkPartitionerDefinitions, IDocument document, int offset, IHyperlinkRegion region) {
        for(int i=0; i<hyperlinkPartitionerDefinitions.length; i++) {
            HyperlinkPartitionerDefinition def = hyperlinkPartitionerDefinitions[i];
            IHyperlinkPartitioner partitioner = def.createHyperlinkPartitioner();
            if((partitioner instanceof IExclusiblePartitionerRecognition) && 
            		((IExclusiblePartitionerRecognition)partitioner).excludes(partitionType, document, offset, region)) {
                return partitioner;
            }
        }
        return null;
    }
    
	protected static IModelManager getModelManager() {
		return StructuredModelManager.getModelManager();
	}

    protected abstract IHyperlinkRegion parse(IDocument document, int offset, IHyperlinkRegion superRegion);

	protected String getAxis(IDocument document, int offset) {
		StructuredModelWrapper smw = new StructuredModelWrapper();
		try {
			smw.init(document);
			Document xmlDocument = smw.getDocument();
			if (xmlDocument == null) return null;
			Node node = Utils.findNodeForOffset(xmlDocument, offset);
			if (node instanceof Attr) {
				Attr attr = (Attr)node;
				return Utils.getParentAxisForNode(xmlDocument, attr) + attr.getName() + "/"; //$NON-NLS-1$
			}
			return Utils.getParentAxisForNode(xmlDocument, node);
		} finally {
			smw.dispose();
		}
	}
}