package org.fife.ui.rsyntaxtextarea;

import javax.swing.text.TabExpander;


/**
 * Utility methods for dealing with tokens.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class TokenUtils {


	private TokenUtils() {}


	/**
	 * Modifies the passed-in token list to start at the specified offset.
	 * For example, if the token list covered positions 20-60 in the document
	 * (inclusive) like so:
	 * <pre>
	 *   [token1] -> [token2] -> [token3] -> [token4]
	 *   20     30   31     40   41     50   51     60
	 * </pre>
	 * and you used this method to make the token list start at position 44,
	 * then the token list would be modified to be the following:
	 * <pre>
	 *   [part-of-old-token3] -> [token4]
	 *   44                 50   51     60
	 * </pre>
	 * Tokens that come before the specified position are forever lost, and
	 * the token containing that position is made to begin at that position if
	 * necessary.  All token types remain the same as they were originally.<p>
	 *
	 * This method can be useful if you are only interested in part of a token
	 * list (i.e., the line it represents), but you don't want to modify the
	 * token list yourself.
	 *
	 * @param tokenList The list to make start at the specified position.
	 *        This parameter is modified.
	 * @param pos The position at which the new token list is to start.  If
	 *        this position is not in the passed-in token list,
	 *        returned token list will either be <code>null</code> or the
	 *        unpaintable token(s) at the end of the passed-in token list.
	 * @param e How to expand tabs.
	 * @param textArea The text area from which the token list came.
	 * @param x0 The initial x-pixel position of the old token list.
	 * @return Information about the "sub" token list.  This will be
	 *         <code>null</code> if <code>pos</code> was not a valid offset
	 *         into the token list.
	 * @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float, TokenImpl)
	 */
	public static TokenSubList getSubTokenList(Token tokenList, int pos,
									TabExpander e,
									final RSyntaxTextArea textArea,
									float x0) {
		return getSubTokenList(tokenList, pos, e, textArea, x0, null);
	}


	/**
	 * Modifies the passed-in token list to start at the specified offset.
	 * For example, if the token list covered positions 20-60 in the document
	 * (inclusive) like so:
	 * <pre>
	 *   [token1] -> [token2] -> [token3] -> [token4]
	 *   20     30   31     40   41     50   51     60
	 * </pre>
	 * and you used this method to make the token list start at position 44,
	 * then the token list would be modified to be the following:
	 * <pre>
	 *   [part-of-old-token3] -> [token4]
	 *   44                 50   51     60
	 * </pre>
	 * Tokens that come before the specified position are forever lost, and
	 * the token containing that position is made to begin at that position if
	 * necessary.  All token types remain the same as they were originally.<p>
	 *
	 * This method can be useful if you are only interested in part of a token
	 * list (i.e., the line it represents), but you don't want to modify the
	 * token list yourself.
	 *
	 * @param tokenList The list to make start at the specified position.
	 *        This parameter is modified.
	 * @param pos The position at which the new token list is to start.  If
	 *        this position is not in the passed-in token list,
	 *        returned token list will either be <code>null</code> or the
	 *        unpaintable token(s) at the end of the passed-in token list.
	 * @param e How to expand tabs.
	 * @param textArea The text area from which the token list came.
	 * @param x0 The initial x-pixel position of the old token list.
	 * @param tempToken A temporary token to use  when creating the token list
	 *        result.  This may be <code>null</code> but callers can pass in
	 *        a "buffer" token for performance if desired.
	 * @return Information about the "sub" token list.  This will be
	 *         <code>null</code> if <code>pos</code> was not a valid offset
	 *         into the token list.
	 * @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float)
	 */
	public static TokenSubList getSubTokenList(Token tokenList, int pos,
									TabExpander e,
									final RSyntaxTextArea textArea,
									float x0,
									TokenImpl tempToken) {

		if (tempToken==null) {
			tempToken = new TokenImpl();
		}
		Token t = tokenList;

		// Loop through the token list until you find the one that contains
		// pos.  Remember the cumulative width of all of these tokens.
		while (t!=null && t.isPaintable() && !t.containsPosition(pos)) {
			x0 += t.getWidth(textArea, e, x0);
			t = t.getNextToken();
		}

		// Make the token that contains pos start at pos.
		if (t!=null && t.isPaintable()) {

			if (t.getOffset()!=pos) {
				// Number of chars between p0 and token start.
				int difference = pos - t.getOffset();
				x0 += t.getWidthUpTo(t.length()-difference+1, textArea, e, x0);
				tempToken.copyFrom(t);
				tempToken.makeStartAt(pos);

				return new TokenSubList(tempToken, x0);

			}
			else { // t.getOffset()==pos
				return new TokenSubList(t, x0);
			}

		}

		// This could be a null token, so we need to just return it.
		return new TokenSubList(tokenList, x0);
		//return null;

	}


	public static class TokenSubList {

		/**
		 * The "sub" token list.
		 */
		public Token tokenList;
		
		/**
		 * The width, in pixels, of the part of the token list "removed from
		 * the front."  This way, you know the x-offset of the "new" token list.
		 */
		public float x;

		public TokenSubList(Token tokenList, float x) {
			this.tokenList = tokenList;
			this.x = x;
		}

	}


}