/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.jdt.astview.views;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.LabelProvider;

import org.eclipse.ui.PlatformUI;

import org.eclipse.jdt.core.Signature;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;

public class ASTViewLabelProvider extends LabelProvider implements IColorProvider, IFontProvider {
	public static final int RANGE_NONE= 0;
	public static final int RANGE_OFFSET= 1;
	public static final int RANGE_LINE= 2;
	public static final int RANGE_BOTH= 3;
	
	private int fSelectionStart;
	private int fSelectionLength;
	private boolean fShowRanges= true;
	private boolean fShowFlags= false;
	private boolean fHighlightSelection= true;
	private boolean fCompactLabels= false;
	private boolean fQualifiedNodeNames= false;
	private boolean fShowPropertyCount= false;
	private boolean fShowFlagHex= false;
	private int fRangeMode= RANGE_OFFSET;
	private CompilationUnit fRoot;
	
	private Color fBlue, fRed, fDarkGray, fYellow, fDarkGreen;
	private Font fBold;
	
	public ASTViewLabelProvider() {
		fSelectionStart= -1;
		fSelectionLength= -1;
		
		Display display= Display.getCurrent();
		
		fRed= display.getSystemColor(SWT.COLOR_RED);
		fDarkGray= display.getSystemColor(SWT.COLOR_DARK_GRAY);
		fBlue= display.getSystemColor(SWT.COLOR_DARK_BLUE);
		fYellow= display.getSystemColor(SWT.COLOR_YELLOW);
		fDarkGreen= display.getSystemColor(SWT.COLOR_DARK_GREEN);
		
		fBold= PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry().getBold(JFaceResources.DEFAULT_FONT);

	}
	
	public void setSelectedRange(int start, int length) {
		fSelectionStart= start;
		fSelectionLength= length;
	}

	public void setRoot(CompilationUnit root) {
		fRoot= root;
	}

	public void setShowRanges(boolean showRanges) {
		fShowRanges= showRanges;
		if (!showRanges) {
			fRangeMode= RANGE_NONE;
		} else if (fRangeMode == RANGE_NONE) {
			fRangeMode= RANGE_OFFSET;
		}
	}

	public void setShowFlags(boolean showFlags) {
		fShowFlags= showFlags;
	}

	public void setHighlightSelection(boolean highlightSelection) {
		fHighlightSelection= highlightSelection;
	}

	public void setCompactLabels(boolean compactLabels) {
		fCompactLabels= compactLabels;
	}

	public void setQualifiedNodeNames(boolean qualifiedNodeNames) {
		fQualifiedNodeNames= qualifiedNodeNames;
	}

	public void setShowPropertyCount(boolean showPropertyCount) {
		fShowPropertyCount= showPropertyCount;
	}

	public void setShowFlagHex(boolean showFlagHex) {
		fShowFlagHex= showFlagHex;
	}

	public void setRangeMode(int rangeMode) {
		fRangeMode= rangeMode;
		fShowRanges= rangeMode != RANGE_NONE;
	}
	

	public String getText(Object obj) {
		StringBuffer buf= new StringBuffer();
		if (obj instanceof ASTNode) {
			getNodeType((ASTNode) obj, buf);
		} else if (obj instanceof ASTAttribute) {
			buf.append(((ASTAttribute) obj).getLabel());
		}
		return getDisplayText(buf.toString()); 
	}
	
	private void getNodeType(ASTNode node, StringBuffer buf) {
		String className= node.getClass().getName();
		buf.append(fQualifiedNodeNames ? className : Signature.getSimpleName(className));
		appendRange(node, buf);
		if (fShowPropertyCount) {
			buf.append(" props="); //$NON-NLS-1$
			buf.append(node.structuralPropertiesForType().size());
		}
		if ((node.getFlags() & ASTNode.MALFORMED) != 0) {
			buf.append(" (malformed)"); //$NON-NLS-1$
		}
		if (fShowFlags) {
			buf.append(fShowFlagHex ? " flags=0x" : " flags="); //$NON-NLS-1$ //$NON-NLS-2$
			buf.append(fShowFlagHex ? Integer.toHexString(node.getFlags()) : Integer.toString(node.getFlags()));
		}
	}

	private void appendRange(ASTNode node, StringBuffer buf) {
		if (!fShowRanges || fRangeMode == RANGE_NONE) {
			return;
		}
		if (fRangeMode == RANGE_OFFSET || fRangeMode == RANGE_BOTH) {
			buf.append(" ["); //$NON-NLS-1$
			buf.append(node.getStartPosition());
			buf.append(", "); //$NON-NLS-1$
			buf.append(node.getLength());
			buf.append(']');
		}
		if ((fRangeMode == RANGE_LINE || fRangeMode == RANGE_BOTH) && fRoot != null && node.getStartPosition() >= 0) {
			int startLine= fRoot.getLineNumber(node.getStartPosition());
			int endLine= fRoot.getLineNumber(node.getStartPosition() + Math.max(node.getLength() - 1, 0));
			buf.append(" line="); //$NON-NLS-1$
			buf.append(startLine);
			if (endLine != startLine) {
				buf.append('-');
				buf.append(endLine);
			}
		}
	}

	private String getDisplayText(String text) {
		if (!fCompactLabels || text == null || text.length() < 120) {
			return text;
		}
		StringBuffer compact= new StringBuffer();
		boolean inWord= false;
		for (int i= 0; i < text.length(); i++) {
			char ch= text.charAt(i);
			if (Character.isWhitespace(ch)) {
				if (inWord) {
					compact.append(' ');
				}
				inWord= false;
			} else {
				compact.append(ch);
				inWord= true;
			}
			if (compact.length() >= 120) {
				compact.append("..."); //$NON-NLS-1$
				break;
			}
		}
		return compact.toString();
	}
	
	
	public Image getImage(Object obj) {
		if (obj instanceof ASTNode) {
			return null;
		} else if (obj instanceof ASTAttribute) {
			return ((ASTAttribute) obj).getImage();
		}
		
		return null;
//		String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
//		if (obj instanceof ASTNode) {
//			imageKey = ISharedImages.IMG_OBJ_FOLDER;
//		}
//		return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
	 */
	public Color getForeground(Object element) {
		if ((element instanceof ExceptionAttribute) && ((ExceptionAttribute) element).getException() != null)
			return fRed;
		
		if (element instanceof ASTNode) {
			ASTNode node= (ASTNode) element;
			if ((node.getFlags() & ASTNode.MALFORMED) != 0) {
				return fRed;
			}
			return fDarkGray;
		} else if (element instanceof Binding) {
			Binding binding= (Binding) element;
			if (binding.isRequired() && binding.getBinding() == null) {
				return fRed;
			}
			return fBlue;
		} else if (element instanceof NodeProperty) {
			return null; // normal color
		} else if (element instanceof BindingProperty) {
			return fBlue;
		} else if (element instanceof JavaElement) {
			JavaElement javaElement= (JavaElement) element;
			if (javaElement.getJavaElement() == null) {
				return fRed;
			}
			return fDarkGreen;
		}
		return fDarkGray; // all extra properties
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
	 */
	public Color getBackground(Object element) {
		if (fHighlightSelection && fSelectionStart != -1 && isInside(element)) {
			return fYellow;
		}
		return null;
	}
	
	private boolean isInsideNode(ASTNode node) {
		int start= node.getStartPosition();
		int end= start + node.getLength();
		if (start <= fSelectionStart && (fSelectionStart + fSelectionLength) < end) {
			return true;
		}
		return false;
	}
	
	private boolean isInside(Object element) {
		if (element instanceof ASTNode) {
			return isInsideNode((ASTNode) element);
		} else if (element instanceof NodeProperty) {
			NodeProperty property= (NodeProperty) element;
			Object object= property.getNode();
			if (object instanceof ASTNode) {
				return isInsideNode((ASTNode) object);
			}
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
	 */
	public Font getFont(Object element) {
		if (element instanceof ASTNode) {
			ASTNode node= (ASTNode) element;
			int start= node.getStartPosition();
			int end= start + node.getLength();
			
			ASTNode parent= node.getParent();
			if (parent != null) {
				int parentstart= parent.getStartPosition();
				int parentend= start + parent.getLength();
				
				if (start < parentstart || end > parentend) {
					return fBold;
				}
			}
		}
		return null;
	}
	
}
