package io.shardingsphere.core.parsing.antler.visitor.mysql;

import java.util.List;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;

import io.shardingsphere.core.parsing.antler.sql.ddl.AlterTableStatement;
import io.shardingsphere.core.parsing.antler.sql.ddl.ColumnDefinition;
import io.shardingsphere.core.parsing.antler.utils.TreeUtils;
import io.shardingsphere.core.parsing.antler.utils.VisitorUtils;
import io.shardingsphere.core.parsing.antler.visitor.AlterTableVisitor;
import io.shardingsphere.core.parsing.parser.token.IndexToken;

public class MySQLAlterTableVisitor extends AlterTableVisitor {
    @Override
    protected void visitPrivateTree(AlterTableStatement statement, ParseTree rootNode) {
        visitAddIndex(statement, rootNode);
        visitDropIndex(statement, rootNode);
        visitAddPrimaryKey(statement, rootNode);
        
        VisitorUtils.parseModifyColumn(statement, rootNode);
        VisitorUtils.parseRenameIndex(statement, rootNode);
        VisitorUtils.parseAddPrimaryKey(statement, rootNode);
        VisitorUtils.parseDropPrimaryKey(statement, rootNode);
    }
    
    
    /**
     * Visit add index node.
     * 
     * @param statement
     *            statement parse result
     * @param ancestorNode
     *            ancestor of index node
     * @return indexName node
     */
    protected void visitAddIndex(final AlterTableStatement statement, final ParseTree rootNode) {
        ParserRuleContext indexDefOptionNode = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(rootNode,
                "indexDefOption");
        if (null != indexDefOptionNode) {
            ParserRuleContext indexNameNode = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(indexDefOptionNode,
                    "indexName");
            if (null != indexNameNode) {
                statement.getSqlTokens().add(new IndexToken(indexNameNode.getStart().getStartIndex(),
                        indexNameNode.getText(), statement.getTables().getSingleTableName()));
            }
        }
    }
    
    
    /**
     * Visit drop index node.
     * 
     * @param statement
     *            statement parse result
     * @param ancestorNode
     *            ancestor of index node
     * @return indexName node
     */
    protected void visitDropIndex(final AlterTableStatement statement, final ParseTree ancestorNode) {
        ParserRuleContext dropIndexDefNode = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(ancestorNode,
                "dropIndexDef");
        if (null == dropIndexDefNode) {
            return;
        }

        ParserRuleContext indexNameNode = (ParserRuleContext) dropIndexDefNode
                .getChild(dropIndexDefNode.getChildCount() - 1);
        if (null != indexNameNode) {
            statement.getSqlTokens().add(new IndexToken(indexNameNode.getStart().getStartIndex(),
                    indexNameNode.getText(), statement.getTables().getSingleTableName()));
        }
    }

    /**
     * Visit add primary key.
     * 
     * @param statement
     *            statement parse result
     * @param ancestorNode
     *            ancestor of index node
     * @return indexName node
     */
    protected void visitAddPrimaryKey(final AlterTableStatement statement, final ParseTree ancestorNode) {
        ParserRuleContext constraintDefinitionNode = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(ancestorNode,
                "constraintDefinition");
        if (null == constraintDefinitionNode) {
            return;
        }

        ParserRuleContext primaryKeyOptionNode = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(ancestorNode,
                "primaryKeyOption");
        if (null == primaryKeyOptionNode) {
            return;
        }

        List<ParseTree> keyPartNodes = TreeUtils.getAllDescendantByRuleName(ancestorNode, "keyPart");
        if (null == keyPartNodes) {
            return;
        }

        for (final ParseTree each : keyPartNodes) {
            String columnName = each.getChild(0).getText();
            ColumnDefinition updateColumn = statement.getUpdateColumns().get(columnName);
            if (null == updateColumn) {
                updateColumn = new ColumnDefinition(columnName, null, null, true);
                statement.getUpdateColumns().put(columnName, updateColumn);
            } else {
                updateColumn.setPrimaryKey(true);
            }
        }
    }

}

@@ -889,24 +889,72 @@
 );%0A        %0A
+        visitChangeColumn(statement, rootNode);%0A
         Visi
@@ -1190,32 +1190,1310 @@
 de);%0A    %7D%0A    %0A
+    /**%0A     * Parse alter table change column node.%0A     * %0A     * @param statement%0A     *            statement parse result%0A     * @param rootNode%0A     *            root node of syntax tree%0A     */%0A    public static void visitChangeColumn(final AlterTableStatement statement, final ParseTree rootNode) %7B%0A        ParserRuleContext changeColumnCtx = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(rootNode,%0A                %22changeColumn%22);%0A        if(null == changeColumnCtx) %7B%0A            return;%0A        %7D%0A        %0A        ParserRuleContext oldColumnCtx = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(changeColumnCtx,%0A                %22columnName%22);%0A        %0A        if(null == oldColumnCtx) %7B%0A            return;%0A        %7D%0A        %0A        ParserRuleContext columnDefinitionCtx = (ParserRuleContext) TreeUtils.getFirstChildByRuleName(changeColumnCtx,%0A                %22columnDefinition%22);%0A        %0A        if(null == columnDefinitionCtx) %7B%0A            return;%0A        %7D%0A        %0A        ColumnDefinition column = VisitorUtils.parseColumnDefinition(columnDefinitionCtx);%0A        if (null != column) %7B%0A            statement.getUpdateColumns().remove(oldColumnCtx.getText());%0A            statement.getUpdateColumns().put(column.getName(), column);%0A        %7D%0A    %7D%0A
     %0A    /**%0A   

Add visit changeColumn