/*
 * 03/07/2012
 *
 * FoldingAwareIconRowHeader - Icon row header that paints itself correctly
 * even when code folding is enabled.
 * 
 * This library is distributed under a modified BSD license.  See the included
 * RSyntaxTextArea.License.txt file for details.
 */
package com.fr.design.gui.syntax.ui.rsyntaxtextarea;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.Icon;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;

import com.fr.design.gui.syntax.ui.rsyntaxtextarea.folding.FoldManager;
import com.fr.design.gui.syntax.ui.rtextarea.GutterIconInfo;
import com.fr.design.gui.syntax.ui.rtextarea.IconRowHeader;


/**
 * A row header component that takes code folding into account when painting
 * itself.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class FoldingAwareIconRowHeader extends IconRowHeader {


	/**
	 * Constructor.
	 *
	 * @param textArea The parent text area.
	 */
	public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
		super(textArea);
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void paintComponent(Graphics g) {

		// When line wrap is not enabled, take the faster code path.
		if (textArea==null) {
			return;
		}
		RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
		FoldManager fm = rsta.getFoldManager();
		if (!fm.isCodeFoldingSupportedAndEnabled()) {
			super.paintComponent(g);
			return;
		}

		visibleRect = g.getClipBounds(visibleRect);
		if (visibleRect==null) { // ???
			visibleRect = getVisibleRect();
		}
		//System.out.println("IconRowHeader repainting: " + visibleRect);
		if (visibleRect==null) {
			return;
		}

		g.setColor(getBackground());
		g.fillRect(0,visibleRect.y, width,visibleRect.height);

		if (textArea.getLineWrap()) {
			paintComponentWrapped(g);
			return;
		}

		Document doc = textArea.getDocument();
		Element root = doc.getDefaultRootElement();
		textAreaInsets = textArea.getInsets(textAreaInsets);

		// Get the first line to paint.
		int cellHeight = textArea.getLineHeight();
		int topLine = (visibleRect.y-textAreaInsets.top)/cellHeight;

		// Get where to start painting (top of the row).
		// We need to be "scrolled up" up just enough for the missing part of
		// the first line.
		int y = topLine*cellHeight + textAreaInsets.top;

		// AFTER calculating visual offset to paint at, account for folding.
		topLine += fm.getHiddenLineCountAbove(topLine, true);

		// Paint the active line range.
		if (activeLineRangeStart>-1 && activeLineRangeEnd>-1) {
			Color activeLineRangeColor = getActiveLineRangeColor();
			g.setColor(activeLineRangeColor);
			try {

				int realY1 = rsta.yForLine(activeLineRangeStart);
				if (realY1>-1) { // Not in a collapsed fold...

					int  y1 = realY1;//Math.max(y, realY1);

					int y2 = rsta.yForLine(activeLineRangeEnd);
					if (y2==-1) { // In a collapsed fold
						y2 = y1;
					}
					y2 += cellHeight - 1;

					if (y2<visibleRect.y || y1>visibleRect.y+visibleRect.height) {
						//System.out.println("... nothing to paint, bailing...");
						return;
					}
					y1 = Math.max(y, realY1);
					y2 = Math.min(y2, visibleRect.y+visibleRect.height);
					//System.out.println(y1 + "... " + y2 + "; " + realY1 + ", " + visibleRect);

					int j = y1;
					while (j<=y2) {
						int yEnd = Math.min(y2, j+getWidth());
						int xEnd = yEnd-j;
						g.drawLine(0,j, xEnd,yEnd);
						j += 2;
					}

					int i = 2;
					while (i<getWidth()) {
						int yEnd = y1 + getWidth() - i;
						g.drawLine(i,y1, getWidth(),yEnd);
						i += 2;
					}

					if (realY1>=y && realY1<visibleRect.y+visibleRect.height) {
						g.drawLine(0,realY1, getWidth(),realY1);
					}
					if (y2>=y && y2<visibleRect.y+visibleRect.height) {
						g.drawLine(0,y2, getWidth(),y2);
					}

				}

			} catch (BadLocationException ble) {
				ble.printStackTrace(); // Never happens
			}
		}

		// Paint icons
		if (trackingIcons!=null) {
			int lastLine = textArea.getLineCount() - 1;
			for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
				GutterIconInfo ti = getTrackingIcon(i);
				int offs = ti.getMarkedOffset();
				if (offs>=0 && offs<=doc.getLength()) {
					int line = root.getElementIndex(offs);
					if (line<=lastLine && line>=topLine) {
						try {
							Icon icon = ti.getIcon();
							if (icon!=null) {
								int lineY = rsta.yForLine(line);
								if (lineY>=y && lineY<=visibleRect.y+visibleRect.height) {
									int y2 = lineY + (cellHeight-icon.getIconHeight())/2;
									icon.paintIcon(this, g, 0, y2);
									lastLine = line-1; // Paint only 1 icon per line
								}
							}
						} catch (BadLocationException ble) {
							ble.printStackTrace(); // Never happens
						}
					}
					else if (line<topLine) {
						break; // All other lines are above us, so quit now
					}
				}
			}
		}

	}


	/**
	 * Paints icons when line wrapping is enabled.  Note that this does not
	 * override the parent class's implementation to avoid this version being
	 * called when line wrapping is disabled.
	 */
	private void paintComponentWrapped(Graphics g) {

		// The variables we use are as follows:
		// - visibleRect is the "visible" area of the text area; e.g.
		// [0,100, 300,100+(lineCount*cellHeight)-1].
		// actualTop.y is the topmost-pixel in the first logical line we
		// paint.  Note that we may well not paint this part of the logical
		// line, as it may be broken into many physical lines, with the first
		// few physical lines scrolled past.  Note also that this is NOT the
		// visible rect of this line number list; this line number list has
		// visible rect == [0,0, insets.left-1,visibleRect.height-1].

		// We avoid using modelToView/viewToModel where possible, as these
		// methods trigger a parsing of the line into syntax tokens, which is
		// costly.  It's cheaper to just grab the child views' bounds.

		RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
//		boolean currentLineHighlighted = textArea.getHighlightCurrentLine();
		Document doc = textArea.getDocument();
		Element root = doc.getDefaultRootElement();
		int topPosition = textArea.viewToModel(
								new Point(visibleRect.x,visibleRect.y));
		int topLine = root.getElementIndex(topPosition);

		int topY = visibleRect.y;
		int bottomY = visibleRect.y + visibleRect.height;
		int cellHeight = textArea.getLineHeight();

		// Paint icons
		if (trackingIcons!=null) {
			int lastLine = textArea.getLineCount() - 1;
			for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
				GutterIconInfo ti = getTrackingIcon(i);
				int offs = ti.getMarkedOffset();
				if (offs>=0 && offs<=doc.getLength()) {
					int line = root.getElementIndex(offs);
					if (line<=lastLine && line>=topLine) {
						try {
							int lineY = rsta.yForLine(line);
							if (lineY>=topY && lineY<bottomY) {
								Icon icon = ti.getIcon();
								if (icon!=null) {
									int y2 = lineY + (cellHeight-icon.getIconHeight())/2;
									ti.getIcon().paintIcon(this, g, 0, y2);
									lastLine = line-1; // Paint only 1 icon per line
								}
							}
						} catch (BadLocationException ble) {
							ble.printStackTrace(); // Never happens
						}
					}
					else if (line<topLine) {
						break; // All other lines are above us, so quit now
					}
				}
			}
		}

	}


}