/*
 * Copyright 1999-2017 Alibaba Group Holding Ltd.
 *
 * 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.alibaba.druid.sql.dialect.mysql.visitor.transform;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
import com.alibaba.druid.util.FnvHash;

/**
 * Created by wenshao on 26/07/2017.
 */
public class NameResolveVisitor extends OracleASTVisitorAdapter {
    @Override
    public boolean visit(SQLIdentifierExpr x) {
        SQLObject parent = x.getParent();

        if (parent instanceof SQLBinaryOpExpr
                && x.getResolvedColumn() == null) {
            SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) parent;
            boolean isJoinCondition = binaryOpExpr.getLeft() instanceof SQLName
                    && binaryOpExpr.getRight() instanceof SQLName;
            if (isJoinCondition) {
                return false;
            }
        }

        String name = x.getName();

        if ("ROWNUM".equalsIgnoreCase(name)) {
            return false;
        }

        long hash = x.nameHashCode64();
        SQLTableSource tableSource = null;

        if (hash == FnvHash.Constants.LEVEL
                || hash == FnvHash.Constants.CONNECT_BY_ISCYCLE
                || hash == FnvHash.Constants.SYSTIMESTAMP) {
            return false;
        }

        if (parent instanceof SQLPropertyExpr) {
            return false;
        }

        for (; parent != null; parent = parent.getParent()) {
            if (parent instanceof SQLTableSource) {
                return false;
            }

            if (parent instanceof SQLSelectQueryBlock) {
                SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) parent;

                if (queryBlock.getInto() != null) {
                    return false;
                }

                if (queryBlock.getParent() instanceof SQLSelect) {
                    SQLObject pp = queryBlock.getParent().getParent();
                    if (pp instanceof SQLInSubQueryExpr || pp instanceof SQLExistsExpr) {
                        return false;
                    }
                }

                SQLTableSource from = queryBlock.getFrom();
                if (from instanceof SQLExprTableSource || from instanceof SQLSubqueryTableSource) {
                    String alias = from.getAlias();
                    if (alias != null) {
                        // rownum 不加 alias
                        boolean isRowNumColumn = isRowNumColumn(x, queryBlock);
                        // 别名 不加 alias
                        boolean isAliasColumn = isAliasColumn(x, queryBlock);
                        if (!isRowNumColumn && !isAliasColumn) {
                            SQLUtils.replaceInParent(x, new SQLPropertyExpr(alias, name));
                        }
                    }
                }
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean visit(SQLPropertyExpr x) {
        String ownerName = x.getOwnernName();
        if (ownerName == null) {
            return super.visit(x);
        }

        for (SQLObject parent = x.getParent(); parent != null; parent = parent.getParent()) {
            if (parent instanceof SQLSelectQueryBlock) {
                SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) parent;
                SQLTableSource tableSource = queryBlock.findTableSource(ownerName);
                if (tableSource == null) {
                    continue;
                }

                String alias = tableSource.computeAlias();
                if (ownerName.equalsIgnoreCase(alias)
                        && !ownerName.equals(alias)) {
                    x.setOwner(alias);
                }

                break;
            }
        }

        return super.visit(x);
    }

    /**
     * 是否是 rownum 或者 rownum 别名
     *
     * @param x      x 是否是 rownum 或者 rownum 别名
     * @param source 从 source 数据中查找 and 判断
     * @return true：是、false：不是
     */
    public boolean isRowNumColumn(SQLExpr x, SQLSelectQueryBlock source) {
        if (x instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr) x;
            long nameHashCode64 = identifierExpr.nameHashCode64();
            if (nameHashCode64 == FnvHash.Constants.ROWNUM) {
                return true;
            }

            SQLSelectQueryBlock queryBlock = source;
            if (queryBlock.getFrom() instanceof SQLSubqueryTableSource
                    && ((SQLSubqueryTableSource) queryBlock.getFrom()).getSelect().getQuery() instanceof SQLSelectQueryBlock) {
                SQLSelectQueryBlock subQueryBlock = ((SQLSubqueryTableSource) queryBlock.getFrom()).getSelect().getQueryBlock();
                SQLSelectItem selectItem = subQueryBlock.findSelectItem(nameHashCode64);

                if (selectItem != null && isRowNumColumn(selectItem.getExpr(), subQueryBlock)) {
                    return true;
                }

            }

        }
        return false;
    }

    /**
     * 是否是 select item 字段的别名
     *
     * @param x      x 是否是 select item 字段的别名
     * @param source 从 source 数据中查找 and 判断
     * @return true：是、false：不是
     */
    public boolean isAliasColumn(SQLExpr x, SQLSelectQueryBlock source) {
        if (x instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr) x;
            long nameHashCode64 = identifierExpr.nameHashCode64();
            SQLSelectQueryBlock queryBlock = source;

            SQLSelectItem selectItem = queryBlock.findSelectItem(nameHashCode64);
            if (selectItem != null) {
                return true;
            }

            if (queryBlock.getFrom() instanceof SQLSubqueryTableSource
                    && ((SQLSubqueryTableSource) queryBlock.getFrom()).getSelect().getQuery() instanceof SQLSelectQueryBlock) {
                SQLSelectQueryBlock subQueryBlock = ((SQLSubqueryTableSource) queryBlock.getFrom()).getSelect().getQueryBlock();

                if (isAliasColumn(x, subQueryBlock)) {
                    return true;
                }

            }

        }
        return false;
    }

}
