package org.beetl.sql.core.query;

import org.beetl.sql.clazz.TableDesc;
import org.beetl.sql.clazz.kit.BeetlSQLException;
import org.beetl.sql.clazz.kit.KeyWordHandler;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.db.AbstractDBStyle;
import org.beetl.sql.core.query.interfacer.QueryConditionI;
import org.beetl.sql.core.query.interfacer.StrongValue;

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

public class QueryCondition<T> implements QueryConditionI<T> {
    public final String AND = "AND";
    public final String OR = "OR";
    public final String WHERE = "WHERE";
    public final String IN = "IN";
    public final String NOT_IN = "NOT IN";
    public final String BETWEEN = "BETWEEN";
    public final String NOT_BETWEEN = "NOT BETWEEN";
    public SQLManager sqlManager;
    protected StringBuilder sql = null;
    protected List<Object> params = new ArrayList<Object>();
    protected Object startRow = null;
    long pageSize = -1;
    protected OrderBy orderBy = null;
    protected GroupBy groupBy = null;
    protected  boolean distinct = false;

    protected QueryCondition() {
    }

    protected void clear() {
        sql = null;
        params = new ArrayList<Object>();
        startRow = null;
        pageSize = -1;
        orderBy = null;
        groupBy = null;
    }

    /**
     * 获取字段 加上前后空格
     *
     * @param colName
     * @return
     */
    protected String getCol(String colName) {
        return " " + getColTrunk(colName) + " ";
    }

    /***
     * 获取字段信息
     * @param colName
     * @return
     */
    protected String getColTrunk(String colName) {
		KeyWordHandler keyWordHandler = sqlManager.getDbStyle().getKeyWordHandler();
		return keyWordHandler!=null?keyWordHandler.getCol(colName):colName;
    }

    /****
     * 根据实体class获取表名
     * @param c
     * @return
     */
    public String getTableName(Class<?> c) {
        String tname = sqlManager.getNc().getTableName(c);
        TableDesc desc = sqlManager.getMetaDataManager().getTable(tname);
        String tabeName2 = desc.getName();
        AbstractDBStyle style = (AbstractDBStyle) sqlManager.getDbStyle();
        tabeName2 = style.getSQLTemplateEngine().wrapString(tabeName2);
        if (desc.getSchema() != null) {
            return style.getKeyWordHandler().getTable(desc.getSchema()) + "." + style.getKeyWordHandler()
                    .getTable(tabeName2);
        } else {
            return style.getKeyWordHandler().getTable(tabeName2);
        }
    }


    /**
     * 拼接SQL
     *
     * @param sqlPart
     */
    public Query<T> appendSql(String sqlPart) {
        if (this.sql == null) {
            this.sql = new StringBuilder();
        }
        sql.append(sqlPart);
        return (Query) this;
    }


    /**
     * 增加参数
     *
     * @param objects
     * @return
     */
    public Query<T> addParam(Collection<?> objects) {
        params.addAll(objects);
        return (Query) this;
    }


    /**
     * 在头部增加参数
     *
     * @param objects
     * @return
     */
    public void addPreParam(List<Object> objects) {
        objects.addAll(params);
    }

    /**
     * 增加参数
     *
     * @param object
     * @return
     */
    public Query<T> addParam(Object object) {
        params.add(object);
        return (Query) this;
    }

    protected void appendAndSql(String column, Object value, String opt) {
        appendSqlBase(column, value, opt, AND);
    }

    protected void appendOrSql(String column, Object value, String opt) {
        appendSqlBase(column, value, opt, OR);
    }

    protected void appendSqlBase(String column, Object value, String opt, String link) {
        //判断是否有效的变量
        if (value instanceof StrongValue) {
            if (!((StrongValue) value).isEffective()) {
                return;
            }
            value = ((StrongValue) value).getValue();
        }
        if (getSql().indexOf(WHERE) < 0) {
            link = WHERE;
        }
        this.appendSql(link).appendSql(getCol(column)).appendSql(opt);
        if (value != null) {
            this.appendSql(" ? ");
            this.addParam(value);
        }
    }

    protected void appendInSql(String column, Object value, String opt, String link) {
        //判断是否有效的变量
        if (value instanceof StrongValue) {
            if (!((StrongValue) value).isEffective()) {
                return;
            }
            value = ((StrongValue) value).getValue();
        }
        if (getSql().indexOf(WHERE) < 0) {
            link = WHERE;
        }
        this.appendSql(link).appendSql(getCol(column)).appendSql(opt).appendSql(" ( ");
        for (Object o : (Collection<?>) value) {
            this.appendSql(" ? ,");
            this.addParam(o);
        }
        this.getSql().deleteCharAt(this.getSql().length() - 1);
        this.appendSql(" ) ");
    }

    protected void appendBetweenSql(String column, String opt, String link, Object... value) {
        if (getSql().indexOf(WHERE) < 0) {
            link = WHERE;
        }

        this.appendSql(link).appendSql(getCol(column)).appendSql(opt).appendSql(" ? AND ? ");
        this.addParam(value[0]);
        this.addParam(value[1]);
    }

    //Override
    @Override
    public Query<T> andEq(String column, Object value) {
        appendAndSql(column, value, "=");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andNotEq(String column, Object value) {
        appendAndSql(column, value, "<>");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andGreat(String column, Object value) {
        appendAndSql(column, value, ">");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andGreatEq(String column, Object value) {
        appendAndSql(column, value, ">=");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andLess(String column, Object value) {
        appendAndSql(column, value, "<");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andLessEq(String column, Object value) {
        appendAndSql(column, value, "<=");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andLike(String column, Object value) {
        appendAndSql(column, value, "LIKE ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andNotLike(String column, Object value) {
        appendAndSql(column, value, "NOT LIKE ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andIsNull(String column) {
        appendAndSql(column, null, "IS NULL ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andIsNotNull(String column) {
        appendAndSql(column, null, "IS NOT NULL ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andIn(String column, Collection<?> value) {
        appendInSql(column, value, IN, AND);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andIn(String column, StrongValue value) {
        appendInSql(column, value, IN, AND);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andNotIn(String column, Collection<?> value) {
        appendInSql(column, value, NOT_IN, AND);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andNotIn(String column, StrongValue value) {
        appendInSql(column, value, NOT_IN, AND);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andBetween(String column, Object value1, Object value2) {
        appendBetweenSql(column, BETWEEN, AND, value1, value2);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> andNotBetween(String column, Object value1, Object value2) {
        appendBetweenSql(column, NOT_BETWEEN, AND, value1, value2);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orEq(String column, Object value) {
        appendOrSql(column, value, "=");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orNotEq(String column, Object value) {
        appendOrSql(column, value, "<>");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orGreat(String column, Object value) {
        appendOrSql(column, value, ">");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orGreatEq(String column, Object value) {
        appendOrSql(column, value, ">=");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orLess(String column, Object value) {
        appendOrSql(column, value, "<");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orLessEq(String column, Object value) {
        appendOrSql(column, value, "<=");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orLike(String column, Object value) {
        appendOrSql(column, value, "LIKE ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orNotLike(String column, Object value) {
        appendOrSql(column, value, "NOT LIKE ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orIsNull(String column) {
        appendOrSql(column, null, "IS NULL ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orIsNotNull(String column) {
        appendOrSql(column, null, "IS NOT NULL ");
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orIn(String column, Collection<?> value) {
        appendInSql(column, value, IN, OR);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orIn(String column, StrongValue value) {
        appendInSql(column, value, IN, OR);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orNotIn(String column, Collection<?> value) {
        appendInSql(column, value, NOT_IN, OR);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orNotIn(String column, StrongValue value) {
        appendInSql(column, value, NOT_IN, OR);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orBetween(String column, Object value1, Object value2) {
        appendBetweenSql(column, BETWEEN, OR, value1, value2);
        return (Query) this;
    }

    //Override
    @Override
    public Query<T> orNotBetween(String column, Object value1, Object value2) {
        appendBetweenSql(column, NOT_BETWEEN, OR, value1, value2);
        return (Query) this;
    }


    //Override
    @Override
    public Query<T> and(QueryCondition condition) {
        return manyCondition(condition, AND);
    }

    //Override
    @Override
    public Query<T> or(QueryCondition condition) {
        return manyCondition(condition, OR);
    }

    private Query<T> manyCondition(QueryCondition condition, String link) {
        if (!(condition instanceof QueryCondition)) {
            throw new BeetlSQLException(BeetlSQLException.QUERY_CONDITION_ERROR, "连接条件必须是一个 QueryCondition 类型");
        }
        if (condition.getSql() == null || "".equals(condition.getSql().toString())) {
            return (Query) this;
        }
        //去除叠加条件中的WHERE
        int i = condition.getSql().indexOf(WHERE);
        if (i > -1) {
            condition.getSql().delete(i, i + 5);
        }

        if (getSql().indexOf(WHERE) < 0) {
            link = WHERE;
        }
        appendSql(link).appendSql(" (").appendSql(condition.getSql().toString()).appendSql(")");
        addParam(condition.getParams());
        return (Query) this;
    }

    //Override
    @Override
    public StringBuilder getSql() {
        if (this.sql == null) {
            return new StringBuilder();
        }
        return this.sql;
    }

    //Override
    @Override
    public void setSql(StringBuilder sql) {
        this.sql = sql;
    }

    //Override
    @Override
    public List<Object> getParams() {
        return params;
    }
    @Override
    public Query<T> distinct() {
        this.distinct = true;
        return (Query) this;
    }
}
