/*******************************************************************************
 * Copyright 2017 Bstek
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 ******************************************************************************/
package com.bstek.ureport.expression;

import com.bstek.ureport.build.assertor.*;
import com.bstek.ureport.dsl.ReportParserLexer;
import com.bstek.ureport.dsl.ReportParserParser;
import com.bstek.ureport.exception.ReportParseException;
import com.bstek.ureport.expression.function.Function;
import com.bstek.ureport.expression.model.Expression;
import com.bstek.ureport.expression.model.Op;
import com.bstek.ureport.expression.parse.ExpressionErrorListener;
import com.bstek.ureport.expression.parse.ExpressionVisitor;
import com.bstek.ureport.expression.parse.builder.*;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.*;

/**
 * @author Jacky.gao
 * @since 2016年12月24日
 */
public class ExpressionUtils implements ApplicationContextAware {
    public static final String EXPR_PREFIX = "${";
    public static final String EXPR_SUFFIX = "}";
    private static ExpressionVisitor exprVisitor;
    private static final Map<String, Function> functions = new HashMap<>();
    private static final Map<Op, Assertor> assertorsMap = new HashMap<>();
    private static final List<ExpressionBuilder> expressionBuilders = new ArrayList<>();
    private static final List<String> cellNameList = new ArrayList<>();
    private static final String[] LETTERS = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

    static {
        expressionBuilders.add(new StringExpressionBuilder());
        expressionBuilders.add(new VariableExpressionBuilder());
        expressionBuilders.add(new BooleanExpressionBuilder());
        expressionBuilders.add(new IntegerExpressionBuilder());
        expressionBuilders.add(new DatasetExpressionBuilder());
        expressionBuilders.add(new FunctionExpressionBuilder());
        expressionBuilders.add(new NumberExpressionBuilder());
        expressionBuilders.add(new CellPositionExpressionBuilder());
        expressionBuilders.add(new RelativeCellExpressionBuilder());
        expressionBuilders.add(new SetExpressionBuilder());
        expressionBuilders.add(new CellObjectExpressionBuilder());
        expressionBuilders.add(new NullExpressionBuilder());
        expressionBuilders.add(new CurrentCellValueExpressionBuilder());
        expressionBuilders.add(new CurrentCellDataExpressionBuilder());

        assertorsMap.put(Op.Equals, new EqualsAssertor());
        assertorsMap.put(Op.EqualsGreatThen, new EqualsGreatThenAssertor());
        assertorsMap.put(Op.EqualsLessThen, new EqualsLessThenAssertor());
        assertorsMap.put(Op.GreatThen, new GreatThenAssertor());
        assertorsMap.put(Op.LessThen, new LessThenAssertor());
        assertorsMap.put(Op.NotEquals, new NotEqualsAssertor());
        assertorsMap.put(Op.In, new InAssertor());
        assertorsMap.put(Op.NotIn, new NotInAssertor());
        assertorsMap.put(Op.Like, new LikeAssertor());

        Collections.addAll(cellNameList, LETTERS);

        for (String name : LETTERS) {
            for (String letter : LETTERS) {
                cellNameList.add(name + letter);
            }
        }
    }

    public static List<String> getCellNameList() {
        return cellNameList;
    }

    public static Map<String, Function> getFunctions() {
        return functions;
    }

    public static Map<Op, Assertor> getAssertorsMap() {
        return assertorsMap;
    }

    public static boolean conditionEval(Op op, Object left, Object right) {
        Assertor assertor = assertorsMap.get(op);
        return assertor.eval(left, right);
    }

    public static Expression parseExpression(String text) {
        ANTLRInputStream antlrInputStream = new ANTLRInputStream(text);
        ReportParserLexer lexer = new ReportParserLexer(antlrInputStream);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        ReportParserParser parser = new ReportParserParser(tokenStream);
        ExpressionErrorListener errorListener = new ExpressionErrorListener();
        parser.addErrorListener(errorListener);
        exprVisitor = new ExpressionVisitor(expressionBuilders);
        Expression expression = exprVisitor.visitEntry(parser.entry());
        String error = errorListener.getErrorMessage();
        if (error != null) {
            throw new ReportParseException("Expression parse error:" + error);
        }
        return expression;
    }

    public static ExpressionVisitor getExprVisitor() {
        return exprVisitor;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Collection<Function> coll = applicationContext.getBeansOfType(Function.class).values();
        for (Function fun : coll) {
            functions.put(fun.name(), fun);
        }
    }
}
