package com.metaweb.lessen;

import java.util.ArrayList; 
import java.util.List;

import com.metaweb.lessen.tokenizers.BufferedTokenizer;
import com.metaweb.lessen.tokenizers.Tokenizer;
import com.metaweb.lessen.tokens.Token;
import com.metaweb.lessen.tokens.Token.Type;

public class BlockParser extends ParserBase implements Tokenizer {
    protected List<Token>   _pathTokens = new ArrayList<Token>();
    protected Token         _openToken;
    protected List<Token>   _bodyTokens = new ArrayList<Token>();
    protected Token         _closeToken;
    
    protected boolean       _nestChildren;
    protected List<Token>   _childrenTokens = new ArrayList<Token>();
    
    public BlockParser(BufferedTokenizer tokenizer, ResourceFinder resourceFinder, Scope scope, List<Token> parentPathTokens, boolean nestChildren) {
        super(tokenizer, resourceFinder, scope, false);
        _nestChildren = nestChildren;
        
        if (parentPathTokens != null) {
            _pathTokens.addAll(parentPathTokens);
        }
        parse();
    }

    @Override
    public Token getToken() {
        if (_pathTokens.size() > 0) {
            return _pathTokens.get(0);
        }
        if (_openToken != null) {
            return _openToken;
        }
        if (_bodyTokens.size() > 0) {
            return _bodyTokens.get(0);
        }
        if (_closeToken != null) {
            return _closeToken;
        }
        if (_childrenTokens.size() > 0) {
            return _childrenTokens.get(0);
        }
        return null;
    }

    @Override
    public void next() {
        if (_pathTokens.size() > 0) {
            _pathTokens.remove(0);
            return;
        }
        if (_openToken != null) {
            _openToken = null;
            return;
        }
        if (_bodyTokens.size() > 0) {
            _bodyTokens.remove(0);
            return;
        }
        if (_closeToken != null) {
            _closeToken = null;
            return;
        }
        if (_childrenTokens.size() > 0) {
            _childrenTokens.remove(0);
            return;
        }
    }
    
    protected void parse() {
        Token tFirst = _tokenizer.getToken();
        if (tFirst == null) {
            return;
        }
        
        if (tFirst.type == Type.Delimiter && tFirst.text.equals(":")) {
            if (_pathTokens.size() > 0) {
                Token tLastPath = _pathTokens.get(_pathTokens.size() - 1);
                if (tLastPath.type == Type.Whitespace) {
                    _pathTokens.remove(_pathTokens.size() - 1);
                }
            }
        } else {
            if (_pathTokens.size() > 0) {
                Token tLastPath = _pathTokens.get(_pathTokens.size() - 1);
                if (tLastPath.type != Type.Whitespace) {
                    _pathTokens.add(new Token(
                        Type.Whitespace,
                        tFirst.start,
                        tFirst.start,
                        " "
                    ));
                }
            }
        }
        
        Token t = tFirst;
        while (t != null && (t.type != Type.Delimiter || !t.text.equals("{"))) {
            _pathTokens.add(t);
            _tokenizer.next();
            t = _tokenizer.getToken();
        }
        
        if (t != null && t.type == Type.Delimiter && t.text.equals("{")) {
            _openToken = t;
            _tokenizer.next();
        }
        
        parseBody();
        
        t = _tokenizer.getToken();
        if (t != null && t.type == Type.Delimiter && t.text.equals("}")) {
            _closeToken = t;
            _tokenizer.next();
        }
    }
    
    @Override
    protected Tokenizer createCssBlockParser(boolean nestChildren) {
        if (!_nestChildren) {
            passInnerBlockTokenThrough(new Token(Type.Whitespace, -1, -1, "\n"));
        }
        return new BlockParser(_tokenizer, _resourceFinder, new Scope(_scope), _nestChildren ? null : _pathTokens, nestChildren);
    }
    
    @Override
    protected void outputToken(Token t) {
        _bodyTokens.add(t);
    }
    
    @Override
    protected void passInnerBlockTokenThrough(Token t) {
        if (_nestChildren) {
            _bodyTokens.add(t);
        } else {
            _childrenTokens.add(t);
        }
    }
}
