package net.sourceforge.phpeclipse.phpeditor;

/*
 * (c) Copyright IBM Corp. 2000, 2001.
 * All Rights Reserved.
 */

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.custom.LineBackgroundEvent;
import org.eclipse.swt.custom.LineBackgroundListener;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;

public class LinePainter implements IPainter, LineBackgroundListener {

	private final ISourceViewer fViewer;
	private Color fHighlightColor;
	private IPositionManager fPositionManager;

	// positions to keep track of beginning and end of line to be painted or cleared
	private Position fCurrentLine= new Position(0, 0);
	private Position fLastLine= new Position(0, 0);
	// used to keep track of the last line painted
	private int fLastLineNumber= -1;
	private boolean fIsActive;

	public LinePainter(ISourceViewer sourceViewer) {
		fViewer= sourceViewer;
	}

	public void setHighlightColor(Color highlightColor) {
		fHighlightColor= highlightColor;
	}

	/*
	 * @see LineBackgroundListener#lineGetBackground(LineBackgroundEvent)
	 */
	public void lineGetBackground(LineBackgroundEvent event) {
		// don't use cached line information because of asynch painting

		StyledText textWidget= fViewer.getTextWidget();
		if (textWidget != null) {
			
			int caret= textWidget.getCaretOffset();
			int length= event.lineText.length();

			if (event.lineOffset <= caret && caret <= event.lineOffset + length)
				event.lineBackground= fHighlightColor;
			else
				event.lineBackground= textWidget.getBackground();
		}
	}

	private boolean updateHighlightLine() {
		try {

			IDocument document= fViewer.getDocument();

			int offset= fViewer.getTextWidget().getCaretOffset() + fViewer.getVisibleRegion().getOffset();
			int lineNumber= document.getLineOfOffset(offset);
						
			// redraw if the current line number is different from the last line number we painted
			// initially fLastLineNumber is -1
			if (lineNumber != fLastLineNumber) {
				
				fLastLine.offset= fCurrentLine.offset;
				fLastLine.length= fCurrentLine.length;
				fLastLine.isDeleted= fCurrentLine.isDeleted;

				fCurrentLine.isDeleted= false;
				fCurrentLine.offset= document.getLineOffset(lineNumber);
				if (lineNumber == document.getNumberOfLines() - 1)
					fCurrentLine.length= document.getLength() - fCurrentLine.offset;
				else
					fCurrentLine.length=	document.getLineOffset(lineNumber + 1) - fCurrentLine.offset;
				
				fLastLineNumber= lineNumber;
				return true;
				
			}
			
		} catch (BadLocationException e) {
		}

		return false;
	}

	private void drawHighlightLine(Position position, int visibleOffset) {
		StyledText textWidget= fViewer.getTextWidget();
		
		// if the position that is about to be drawn was deleted then we can't
		if (position.isDeleted())
			return;		
		
		int delta= position.offset - visibleOffset;
		if (0 <= delta && delta <= fViewer.getVisibleRegion().getLength()) {
			Point upperLeft= textWidget.getLocationAtOffset(delta);
			int width= textWidget.getClientArea().width + textWidget.getHorizontalPixel();
			int height= textWidget.getLineHeight();
			textWidget.redraw(upperLeft.x, upperLeft.y, width, height, false);
		}
	}

	/*
	 * @see IPainter#deactivate(boolean)
	 */
	public void deactivate(boolean redraw) {
		if (fIsActive) {
			fIsActive= false;
			
			/* on turning off the feature one has to paint the currently 
			 * highlighted line with the standard background color 
			 */
			if (redraw)
				drawHighlightLine(fCurrentLine, fViewer.getVisibleRegion().getOffset());
				
			fViewer.getTextWidget().removeLineBackgroundListener(this);
			
			if (fPositionManager != null)
				fPositionManager.removeManagedPosition(fCurrentLine);
				
			fLastLineNumber= -1;
		}
	}

	/*
	 * @see IPainter#dispose()
	 */
	public void dispose() {
	}

	/*
	 * @see IPainter#paint(int)
	 */
	public void paint(int reason) {
		
		// check selection
		Point selection= fViewer.getTextWidget().getSelectionRange();
		if (selection.y > 0) {
			deactivate(true);
			return;
		}
		
		// initialization
		if (!fIsActive) {
			fViewer.getTextWidget().addLineBackgroundListener(this);
			fPositionManager.addManagedPosition(fCurrentLine);
			fIsActive= true;
		}
		
		//redraw line highlight only if it hasn't been drawn yet on the respective line
		if (updateHighlightLine()) {
			// used to handle segmented view of source files
			int visibleRegionOffset= fViewer.getVisibleRegion().getOffset();
			// clear last line
			drawHighlightLine(fLastLine, visibleRegionOffset);
			// draw new line
			drawHighlightLine(fCurrentLine, visibleRegionOffset);
		}
	}

	/*
	 * @see IPainter#setPositionManager(IPositionManager)
	 */
	public void setPositionManager(IPositionManager manager) {
		fPositionManager = manager;
	}
}