package com.metaweb.lessen.tokenizers;

import java.util.LinkedList;
import java.util.List;

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


public class IndentingTokenizer implements Tokenizer {
    final protected BufferedTokenizer   _tokenizer;
    final protected String              _indent;
    final protected List<Token>         _tokens = new LinkedList<Token>();
    final protected List<String>        _indents = new LinkedList<String>();
    
    public IndentingTokenizer(Tokenizer tokenizer) {
        this(tokenizer, "  ");
    }
    
    public IndentingTokenizer(Tokenizer tokenizer, String indent) {
        _tokenizer = new BufferedTokenizer(tokenizer);
        _indent = indent;
        _indents.add("");
        getMore();
    }
    
    @Override
    public Token getToken() {
        return _tokens.size() == 0 ? null : _tokens.get(0);
    }

    @Override
    public void next() {
        if (_tokens.size() > 0) {
            _tokens.remove(0);
        }
        if (_tokens.size() == 0) {
            getMore();
        }
    }
    
    protected void getMore() {
        Token t;
        while ((t = _tokenizer.getToken()) != null) {
            if (t.type == Type.Comment || t.type == Type.CDataOpen || t.type == Type.CDataClose) {
                _tokenizer.next();
                _tokens.add(t);
            } else if (t.type == Type.Whitespace) {
                _tokenizer.next();
                
                Token t2;
                while ((t2 = _tokenizer.getToken()) != null && t2.type == Type.Whitespace) {
                    _tokenizer.next();
                }
                
                _tokens.add(new Token(Type.Whitespace, t.start, t.end, "\n"));
            } else {
                processStatement(0);
                break;
            }
        }
    }
    
    protected void processStatement(int level) {
        boolean wroteNewLine = false;
        
        Token t;
        while ((t = _tokenizer.getToken()) != null) {
            if (t.type == Type.Delimiter) {
                if (t.text.equals(";")) {
                    _tokenizer.next();
                    _tokens.add(t);
                    
                    processTrailingComments();
                    break;
                } else if (t.text.equals("}")) {
                    // } belongs to the outer block, don't swallow it
                    break;
                } else if (t.text.equals("{")) {
                    _tokenizer.next();
                    _tokens.add(t);
                    
                    processBlock(level + 1);
                    
                    t = _tokenizer.getToken();
                    if (t != null && t.type == Type.Delimiter && t.text.equals("}")) {
                        _tokenizer.next();
                        
                        breakLine(t);
                        indent(t, level);
                        _tokens.add(t);
                    }
                    
                    processTrailingComments();
                    break;
                }
            }
            
            _tokenizer.next();
            
            if (!wroteNewLine) {
                if (t.type == Type.Whitespace) {
                    continue;
                }
                
                breakLine(t);
                indent(t, level);
                
                wroteNewLine = true;
            }
            
            if (t.type == Type.Whitespace) {
                String text = t.text;
                if (text.indexOf('\n') >= 0) {
                    breakLine(t);
                    indent(t, level);
                } else {
                    _tokens.add(new Token(Type.Whitespace, t.start, t.start, " "));
                }
            } else {
                _tokens.add(t);
            }
        }
    
    }
    
    protected void processBlock(int level) {
        Token t;
        while ((t = _tokenizer.getToken()) != null) {
            processStatement(level);
            
            t = _tokenizer.getToken();
            if (t != null && t.type == Type.Delimiter && t.text.equals("}")) {
                break;
            }
        }
    }
    
    protected void processTrailingComments() {
        boolean hasSpace = false;
        
        Token t;
        while ((t = _tokenizer.getToken()) != null) {
            if (t.type == Type.Whitespace) {
                if (t.text.indexOf('\n') >= 0) {
                    return;
                } else {
                    _tokenizer.next();
                    hasSpace = true;
                }
            } else if (t.type == Type.Comment) {
                if (hasSpace) {
                    _tokens.add(new Token(Type.Whitespace, t.start, t.start, " "));
                    hasSpace = false;
                }
                
                _tokenizer.next();
                _tokens.add(t);
            } else {
                break;
            }
        }
    }
    
    protected void breakLine(Token t) {
        _tokens.add(new Token(Type.Whitespace, t.start, t.start, "\n"));
    }
    protected void indent(Token t, int level) {
        _tokens.add(new Token(Type.Whitespace, t.start, t.start, makeIndent(level)));
    }
    
    protected String makeIndent(int level) {
        while (level >= _indents.size()) {
            _indents.add(_indents.get(_indents.size() - 1) + _indent);
        }
        return _indents.get(level);
    }
}
