/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.e4.examples.st;

import org.eclipse.swt.*;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import java.util.*;

class JavaLineStyler implements LineStyleListener {
	JavaScanner scanner = new JavaScanner();
	int[] tokenColors;
	Color[] colors;
	Vector blockComments = new Vector();

	public static final int EOF= -1;
	public static final int EOL= 10;

	public static final int WORD=		0;
	public static final int WHITE=		1;
	public static final int KEY=			2;
	public static final int COMMENT=		3;	
	public static final int STRING=		5;
	public static final int OTHER=		6;
	public static final int NUMBER=		7;

	public static final int MAXIMUM_TOKEN= 8;

public JavaLineStyler() {
	initializeColors();
	scanner = new JavaScanner();
}

Color getColor(int type) {
	if (type < 0 || type >= tokenColors.length) {
		return null;
	}
	return colors[tokenColors[type]];
}

boolean inBlockComment(int start, int end) {
	for (int i=0; i<blockComments.size(); i++) {
		int[] offsets = (int[])blockComments.elementAt(i);
		// start of comment in the line
		if ((offsets[0] >= start) && (offsets[0] <= end)) return true;
		// end of comment in the line
		if ((offsets[1] >= start) && (offsets[1] <= end)) return true;
		if ((offsets[0] <= start) && (offsets[1] >= end)) return true;
	}
	return false;
}

void initializeColors() {
	Display display = Display.getDefault();
	colors= new Color[] {
		new Color(display, new RGB(0, 0, 0)),// black
		new Color(display, new RGB(127, 0, 85)),
		new Color(display, new RGB(63, 127, 95)),
		new Color(display, new RGB(42, 0, 255))
	};
	tokenColors= new int[MAXIMUM_TOKEN];
	tokenColors[WORD]=		0;
	tokenColors[WHITE]=		0;
	tokenColors[KEY]=		1; 
	tokenColors[COMMENT]=	2; 
	tokenColors[STRING]= 	3; 
	tokenColors[OTHER]=		0;
	tokenColors[NUMBER]=	0;
}

void disposeColors() {
	for (int i=0;i<colors.length;i++) {
		colors[i].dispose();
	}
}

/**
 * Event.detail			line start offset (input)	
 * Event.text 			line text (input)
 * LineStyleEvent.styles 	Enumeration of StyleRanges, need to be in order. (output)
 * LineStyleEvent.background 	line background color (output)
 */
public void lineGetStyle(LineStyleEvent event) {
	Vector styles = new Vector();
	int token;
		
	StyleRange lastStyle;
	// If the line is part of a block comment, create one style for the entire line.
	if (inBlockComment(event.lineOffset, event.lineOffset + event.lineText.length())) {
		styles.addElement(new StyleRange(event.lineOffset, event.lineText.length(), getColor(COMMENT), null));
		event.styles = new StyleRange[styles.size()];
		styles.copyInto(event.styles);
		return;
	}
	Color defaultFgColor = ((Control)event.widget).getForeground();
	scanner.setRange(event.lineText);
	token = scanner.nextToken();
	while (token != EOF) {
		if (token == OTHER) {
			// do nothing for non-colored tokens
		} else if (token != WHITE) {
			Color color = getColor(token);
			// Only create a style if the token color is different than the 
			// widget's default foreground color and the token's style is not 
			// bold.  Keywords are bolded.
			if ((!color.equals(defaultFgColor)) || (token == KEY)) {
				StyleRange style = new StyleRange(scanner.getStartOffset() + event.lineOffset, scanner.getLength(), color, null);
				if (token == KEY) {
					style.fontStyle = SWT.BOLD;
				}
				if (styles.isEmpty()) {
					styles.addElement(style);
				} else {
					// Merge similar styles.  Doing so will improve performance.
					lastStyle = (StyleRange)styles.lastElement();
					if (lastStyle.similarTo(style) && (lastStyle.start + lastStyle.length == style.start)) {
						lastStyle.length += style.length;
					} else {
						styles.addElement(style); 
					}
				} 
			}
		} else if ((!styles.isEmpty()) && ((lastStyle=(StyleRange)styles.lastElement()).fontStyle == SWT.BOLD)) {
			int start = scanner.getStartOffset() + event.lineOffset;
			lastStyle = (StyleRange)styles.lastElement();
			// A font style of SWT.BOLD implies that the last style
			// represents a java keyword.
			if (lastStyle.start + lastStyle.length == start) {
				// Have the white space take on the style before it to 
				// minimize the number of style ranges created and the
				// number of font style changes during rendering.
				lastStyle.length += scanner.getLength();
			}
		} 
		token= scanner.nextToken();
	}
	event.styles = new StyleRange[styles.size()];
	styles.copyInto(event.styles);
}
public void parseBlockComments(String text) {
	blockComments = new Vector();
	//StringReader buffer = new StringReader(text);
	int index = 0;
	
	
	int ch;
	boolean blkComment = false;
	int cnt = 0;
	int[] offsets = new int[2];
	boolean done = false;
	
	while (!done) {
		ch = index < text.length() ? text.charAt(index++) : -1;
		switch (ch) {
			case -1 : {
				if (blkComment) {
					offsets[1] = cnt;
					blockComments.addElement(offsets);
				}
				done = true;
				break;
			}
			case '/' : {
				ch = index < text.length() ? text.charAt(index++) : -1;
				if ((ch == '*') && (!blkComment)) {
					offsets = new int[2];
					offsets[0] = cnt;
					blkComment = true;
					cnt++;	
				} else {
					cnt++;
				}						
				cnt++;
				break;
			}
			case '*' : {
				if (blkComment) {
					ch = index < text.length() ? text.charAt(index++) : -1;
					cnt++;
					if (ch == '/') {
						blkComment = false;	
						offsets[1] = cnt;
						blockComments.addElement(offsets);
					}
				}
				cnt++;	
				break;
			}
			default : {
				cnt++;				
				break;
			}
		}
	}
}

}
