package tk.eclipse.plugin.csseditor.editors;

import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IPredicateRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;

/**
 * An implementation of IPredicateRule for CSS properties and values.
 * <p>
 * For exmaple:
 * </p>
 * <pre>
 * <span style="color:red;">background:</span> <span style="color:blue;">black;</span>
 * </pre>
 * This rule returns following tokens.
 * <ul>
 *   <li><span style="color:red;">A part ends with ':'</span> - returns a property token</li>
 *   <li><span style="color:blue;">A part ends with ';'</span> - returns a value token</li>
 * </ul>
 * 
 * @author Naoki Takezoe
 */
public class CSSRule implements IPredicateRule {
	
	private IToken propToken;
	private IToken valueToken;
	
	/**
	 * Constructor.
	 * 
	 * @param propToken a property token
	 * @param valueToken a value token
	 */
	public CSSRule(IToken propToken, IToken valueToken){
		this.propToken = propToken;
		this.valueToken = valueToken;
	}
	
	private boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed) {
		for (int i= 1; i < sequence.length; i++) {
			int c= scanner.read();
			if (c == ICharacterScanner.EOF && eofAllowed) {
				return true;
			} else if (c != sequence[i]) {
				// Non-matching character detected, rewind the scanner back to the start.
				// Do not unread the first character.
				scanner.unread();
				for (int j= i-1; j > 0; j--)
					scanner.unread();
				return false;
			}
		}
		
		return true;
	}
	
	private IToken getToken(ICharacterScanner scanner) {
		int c;
		char[][] delimiters= scanner.getLegalLineDelimiters();
		while ((c= scanner.read()) != ICharacterScanner.EOF) {
			if (c == ':') {
				return propToken;
			} else if (c == ';') {
				return valueToken;
			} else {
				// Check for end of line since it can be used to terminate the pattern.
				for (int i= 0; i < delimiters.length; i++) {
					if (c == delimiters[i][0] && sequenceDetected(scanner, delimiters[i], true)) {
						return null;
					}
				}
			}
		}
		scanner.unread();
		return null;
	}
	
	public IToken evaluate(ICharacterScanner scanner, boolean resume) {
		return doEvaluate(scanner, resume);
	}
	
	private IToken doEvaluate(ICharacterScanner scanner, boolean resume) {
		if (resume) {
			IToken token = getToken(scanner);
			if (token!=null) return token;
		
		} else {
			
			int c = scanner.read();
			if (c != ' ' && c != '\t' && c!='\r' && c!='\n'){
				IToken token = getToken(scanner);
				if (token!=null) return token;
			}
		}
		
		scanner.unread();
		return Token.UNDEFINED;
	}
	
	
	public IToken getSuccessToken() {
		return null;
	}
	
	public IToken evaluate(ICharacterScanner scanner) {
		return evaluate(scanner, false);
	}
}
