package com.metaweb.lessen;

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

import com.metaweb.lessen.expr.Evaluable;
import com.metaweb.lessen.expr.LessMixinFunction;
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 LessFunctionDefinitionParser extends ParserBase implements Tokenizer {
    protected Token _identifierToken;
    final protected List<String> _argumentName = new ArrayList<String>();
    final protected List<Token>  _argumentDefaults = new ArrayList<Token>();
    final protected List<Object> _stuff = new ArrayList<Object>();
    
    public LessFunctionDefinitionParser(BufferedTokenizer tokenizer, ResourceFinder resourceFinder, Scope scope) {
        super(tokenizer, resourceFinder, scope, false);
        parse();
    }

    @Override
    public Token getToken() {
        return null;
    }

    @Override
    public void next() {
        // nothing to do
    }
    
    public Token getIdentifierToken() {
        return _identifierToken;
    }
    
    public String getIdentifier() {
        return _identifierToken.type == Type.Identifier ? 
                _identifierToken.text : 
                _identifierToken.text.substring(0, _identifierToken.text.length() - 1); // remove trailing (
    }
    
    public LessMixinFunction getFunction() {
        return new LessMixinFunction() {
            
            @Override
            public List<Token> invoke(List<Token> arguments, Scope scope) {
                Scope scope2 = new Scope(scope);
                
                for (int i = 0; i < _argumentName.size(); i++) {
                    String name = _argumentName.get(i);
                    Token value = arguments != null && i < arguments.size() ?
                            arguments.get(i) : _argumentDefaults.get(i);
                            
                    scope2.put(name, value);
                }
                
                List<Token> results = new LinkedList<Token>();
                
                for (Object o : _stuff) {
                    if (o instanceof Token) {
                        results.add((Token) o);
                    } else {
                        Token t = ((Evaluable) o).eval(scope2);
                        if (t != null) {
                            results.add(t);
                        }
                    }
                }
                
                return results;
            }
        };
    }
    
    @Override
    protected LessFunctionDefinitionParser createLessFunctionDefinitionParser() {
        return null;
    }

    @Override
    protected void outputToken(Token t) {
        _stuff.add(t);
    }
    
    @Override
    protected void outputEvaluable(Evaluable e) {
        _stuff.add(e);
    }

    @Override
    protected void passInnerBlockTokenThrough(Token t) {
        _stuff.add(t);
    }
    
    protected void parse() {
        Token t = _tokenizer.getToken();
        if (t != null && t.type == Type.Operator && t.text.equals(".")) {
            _tokenizer.next();
            t = _tokenizer.getToken();
        }
        
        if (t != null) {
            if (t.type == Type.Identifier) {
                _identifierToken = t;
                _tokenizer.next();
                t = _tokenizer.getToken();
                
                _tokenizer.next(lookOverWhitespaceAndComment(0));
                t = _tokenizer.getToken();
                
                if (t != null && t.type == Type.Delimiter && t.text.equals("(")) {
                    _tokenizer.next();
                    t = _tokenizer.getToken();
                }
            } else if (t.type == Type.Function) {
                _identifierToken = t;
                _tokenizer.next();
                t = _tokenizer.getToken();
            }
        }
        
        while (true) {
            _tokenizer.next(lookOverWhitespaceAndComment(0));
            t = _tokenizer.getToken();
            
            if (t == null) {
                break;
            } else if (t.type == Type.Delimiter) {
                if (t.text.equals(")")) {
                    _tokenizer.next();
                    _tokenizer.next(lookOverWhitespaceAndComment(0));
                    t = _tokenizer.getToken();
                    break;
                } else if (t.text.equals("{") || t.text.equals("}")) {
                    break;
                }
            } else if (t.type == Type.AtIdentifier) {
                String name = t.text;
                
                _tokenizer.next();
                _tokenizer.next(lookOverWhitespaceAndComment(0));
                t = _tokenizer.getToken();
                
                if (t != null && t.type == Type.Delimiter && t.text.equals(":")) {
                    _tokenizer.next();
                    _tokenizer.next(lookOverWhitespaceAndComment(0));
                    t = _tokenizer.getToken();
                }
                
                if (t == null) {
                    break;
                } else if (t.type == Type.Delimiter) {
                    _argumentName.add(name);
                    _argumentDefaults.add(null);
                } else {
                    _argumentName.add(name);
                    _argumentDefaults.add(t);
                    
                    _tokenizer.next();
                    _tokenizer.next(lookOverWhitespaceAndComment(0));
                    t = _tokenizer.getToken();
                }
                
                if (t != null && t.type == Type.Delimiter && t.text.equals(",")) {
                    _tokenizer.next();
                }
            } else {
                _tokenizer.next(); // ignore
            }
        }
        
        if (t != null && t.type == Type.Delimiter && t.text.equals("{")) {
            _tokenizer.next();
        }
        
        while ((t = _tokenizer.getToken()) != null) {
            if (t.type == Type.Delimiter && t.text.equals("}")) {
                _tokenizer.next();
                break;
            } else if (t.type == Type.Whitespace || t.type == Type.Comment) {
                _tokenizer.next();
                _stuff.add(t);
            } else {
                Object o = parseExpression(true);
                if (o != null) {
                    _stuff.add(o);
                }
            }
        }
    }
}
