package com.metaweb.lessen.tokenizers;

import java.util.Map;

import com.metaweb.lessen.tokens.StringValueToken;
import com.metaweb.lessen.tokens.Token;
import com.metaweb.lessen.tokens.UriToken;
import com.metaweb.lessen.tokens.Token.Type;


public class VariableResolvingTokenizer implements Tokenizer {
    final protected BufferedTokenizer   _tokenizer;
    final protected Map<String, String> _variables;
    final protected boolean             _resolveURIs;
    protected Token                     _token;
    
    public VariableResolvingTokenizer(Tokenizer tokenizer, Map<String, String> variables) {
        this(tokenizer, variables, true);
    }
    
    public VariableResolvingTokenizer(Tokenizer tokenizer, Map<String, String> variables, boolean resolveURIs) {
        _tokenizer = new BufferedTokenizer(tokenizer);
        _variables = variables;
        _resolveURIs = resolveURIs;
        
        _token = _tokenizer.getToken();
        resolve();
    }
    
    @Override
    public Token getToken() {
        return _token;
    }

    @Override
    public void next() {
        _tokenizer.next();
        _token = _tokenizer.getToken();
        
        resolve();
    }
    
    protected void resolve() {
        if (_token != null) {
            Type type = _token.type;
            
            if (type == Type.Uri) {
                UriToken uriToken = (UriToken) _token;
                if (uriToken.prefix.equals("url")) {
                    String url = uriToken.unquotedText;
                    StringBuffer sb = new StringBuffer();
                    
                    int start = 0;
                    while (start < url.length()) {
                        int dollar = url.indexOf('$', start);
                        if (dollar < 0 || dollar == url.length() - 1) {
                            break;
                        }
                        
                        sb.append(url.substring(start, dollar));
                        
                        String name;
                        if (url.charAt(dollar + 1) == '{') {
                            int n = dollar + 2;
                            while (n < url.length()) {
                                if (url.charAt(n) == '}') {
                                    break;
                                }
                                n++;
                            }
                            name = url.substring(dollar + 2, n);
                            start = n + 1;
                        } else {
                            int n = dollar + 1;
                            while (n < url.length()) {
                                char c = url.charAt(n);
                                if (Character.isLetter(c) || Character.isDigit(c) || c == '_') {
                                    n++;
                                } else {
                                    break;
                                }
                            }
                            
                            name = url.substring(dollar + 1, n);
                            start = n;
                        }
                        
                        sb.append(resolveVariable(name));
                    }
                    
                    if (start < url.length()) {
                        sb.append(url.substring(start));
                    }
                    
                    String url2 = sb.toString();
                    if (!url2.equals(url)) {
                        String text = uriToken.prefix + "(\"" + url2 + "\")";
                        
                        _token = new UriToken(
                            uriToken.start,
                            uriToken.end,
                            text,
                            uriToken.prefix,
                            url2
                        );
                    }
                }
                return;
            }
            
            StringBuffer sb = new StringBuffer();
            
            if (type == Type.Operator && _token.text.equals("#")) {
                Token nextToken = _tokenizer.getToken(1);
                if (nextToken == null || nextToken.type != Type.Variable) {
                    return;
                }
                
                sb.append("#");
                type = Type.HashName;
            } else if (type == Type.Variable) {
                sb.append(resolveVariable(_token));
            } else if (
                type == Type.HashName ||
                type == Type.Identifier ||
                type == Type.AtIdentifier
            ) {
                sb.append(_token.text);
            } else {
                return;
            }
            
            int tokenEnd = _token.end;
            while (true) {
                Token nextToken = _tokenizer.getToken(1);
                if (nextToken != null) {
                    if (nextToken.type == Type.Variable) {
                        sb.append(resolveVariable(nextToken));
                        tokenEnd = nextToken.end;
                        
                        _tokenizer.next();
                    } else if (nextToken.type == Type.Identifier) {
                        sb.append(nextToken.text);
                        tokenEnd = nextToken.end;
                        
                        _tokenizer.next();
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
            
            Token newToken = new Token(
                type,
                _token.start,
                tokenEnd,
                sb.toString()
            );
            _token = newToken;
        }
    }
    
    protected String resolveVariable(Token t) {
        return resolveVariable(((StringValueToken) t).unquotedText);
    }
    
    protected String resolveVariable(String name) {
        String resolved = _variables.get(name);
        if (resolved == null) {
            resolved = "UNDEFINED_VARIABLE";
        }
        return resolved;
    }
}
