import {MySqlListenerBase} from "./mySqlListenerBase";
import {SymbolTable} from "../../symbols/symbolTable";
import {
    AtomTableItemContext, DeleteStatementContext,
    DottedIdContext,
    FullColumnNameContext, FullIdContext, InsertStatementContext, MySqlParser,
    QuerySpecificationContext,
    QuerySpecificationNointoContext,
    SqlStatementContext, SubqueryTableItemContext, TableNameContext, UidContext, UidListContext, UpdateStatementContext
} from "../../grammar/mysql/MySqlParser";
import {HashMap} from "@nebtex/hashmaps";
import {SymbolElement, ElementType} from "../../symbols/symbolElement";
import {TerminalNode} from "antlr4ts/tree";

export class MysqlListener extends MySqlListenerBase{

    /**
     * 符号表
     */
    private symbolTable:SymbolTable;

    /**
     * 当前作用域
     */
    private currentLevel:string = "0";



    constructor(symbolTable: SymbolTable) {
        super();
        this.symbolTable = symbolTable;
    }


    /**
     * 进入到sqlStatement语句，创建一个顶级的作用域
     * @param ctx
     */
    enterSqlStatement: (ctx: SqlStatementContext)=> void =  (ctx: SqlStatementContext):void =>{
        let table:HashMap<any, SymbolElement> = new HashMap<any, SymbolElement>();
        let level:string = this.symbolTable.insertTable(this.currentLevel, table);
        this.currentLevel = level;
    };


    /**
     * 退出sqlStatement语句的作用域
     * @param ctx
     */
    exitSqlStatement: (ctx: SqlStatementContext)=> void = (ctx: SqlStatementContext):void => {
        this.currentLevel = this.symbolTable.exitTable(this.currentLevel);
    };


    /***************************************开始select语句***************************************************/

    /**
     * 新建一个select语句的作用域
     */
    enterQuerySpecification: (ctx: QuerySpecificationContext)=> void = (ctx: QuerySpecificationContext):void => {
        let table:HashMap<any, SymbolElement> = new HashMap<any, SymbolElement>();
        let level:string = this.symbolTable.insertTable(this.currentLevel, table);
        this.currentLevel = level;

    };


    /**
     * 退出一个select语句的作用域
     * @param ctx
     */
    exitQuerySpecification: (ctx: QuerySpecificationContext)=> void = (ctx: QuerySpecificationContext):void=>{
        this.currentLevel = this.symbolTable.exitTable(this.currentLevel);
    };


    /**
     * 新建一个select语句的作用域
     * @param ctx
     */
    enterQuerySpecificationNointo: (ctx: QuerySpecificationNointoContext)=> void = (ctx: QuerySpecificationNointoContext):void => {
        let table:HashMap<any, SymbolElement> = new HashMap<any, SymbolElement>();
        let level:string = this.symbolTable.insertTable(this.currentLevel, table);
        this.currentLevel = level;
    };


    /**
     * 退出一个select语句的作用域
     */
    exitQuerySpecificationNointo: (ctx: QuerySpecificationNointoContext)=> void = ():void => {
        this.currentLevel = this.symbolTable.exitTable(this.currentLevel);
    };


    /**
     * 进入字段处理
     * @param ctx
     */
    enterFullColumnName: (ctx: FullColumnNameContext)=> void = (ctx: FullColumnNameContext):void => {
        if (ctx.childCount == 1){

            let startIndex = ctx.start.startIndex;
            let element = new SymbolElement();
            element.type = ElementType.COLUMN;
            element.text = ctx.text;
            element.startIndex = startIndex;
            element.stopIndex = ctx.start.stopIndex;
            element.level = this.currentLevel;
            this.symbolTable.getTable(this.currentLevel).set(startIndex,element);

        } else if (ctx.childCount == 2){

            let uidContext = <UidContext>ctx.getChild(0);
            let dottedIdContext = <DottedIdContext>ctx.getChild(1);
            let startIndex1 = dottedIdContext.start.startIndex;
            let stopIndex = dottedIdContext.start.stopIndex;


            let element = new SymbolElement();
            element.type = ElementType.COLUMN;
            element.level = this.currentLevel;
            element.text = dottedIdContext.text;
            element.startIndex = startIndex1;
            element.stopIndex = stopIndex;
            element.alias = uidContext.text;
            this.symbolTable.getTable(this.currentLevel).set(startIndex1, element);

        }

    };


    /**
     * 进入到表名处理
     * @param ctx
     */
    enterAtomTableItem: (ctx: AtomTableItemContext)=> void = (ctx: AtomTableItemContext):void => {
        if (ctx.childCount == 1){

            let element = new SymbolElement();
            element.level = this.currentLevel;
            element.type = ElementType.TABLE;
            element.startIndex = ctx.start.startIndex;
            element.stopIndex = ctx.start.stopIndex;
            element.text = ctx.text;

            this.symbolTable.getTable(this.currentLevel).set(ctx.start.startIndex, element);


        } else {
            let tableNameContext = <TableNameContext>ctx.getChild(0);
            let uidContext = <UidContext>ctx.getChild(ctx.childCount-1);

            let element1 = new SymbolElement();
            element1.level = this.currentLevel;
            element1.type = ElementType.TABLE;
            element1.startIndex = tableNameContext.start.startIndex;
            element1.stopIndex = tableNameContext.start.stopIndex;
            element1.text = tableNameContext.text;
            element1.alias = uidContext.text;

            this.symbolTable.getTable(this.currentLevel).set(element1.startIndex, element1);

        }

    };


    /**
     * 进入到有子select时，table的别名处理
     * @param ctx
     */
    enterSubqueryTableItem: (ctx: SubqueryTableItemContext)=> void = (ctx: SubqueryTableItemContext):void => {

        let uidContext = <UidContext>ctx.getChild(ctx.childCount - 1);

        let element = new SymbolElement();
        element.level = this.currentLevel;
        element.type = ElementType.TABLE;
        element.startIndex = ctx.start.startIndex;
        element.stopIndex = ctx.start.stopIndex;
        element.alias = uidContext.text;

        this.symbolTable.getTable(this.currentLevel).set(ctx.start.startIndex, element);


    }

    /***************************************结束select语句***************************************************/

    /***************************************开始delete语句***************************************************/

    /**
     * 进入到了delete处理语句的作用域
     * @param ctx
     */
    enterDeleteStatement: (ctx: DeleteStatementContext)=> void = (ctx: DeleteStatementContext):void => {
        let table:HashMap<any, SymbolElement> = new HashMap<any, SymbolElement>();
        let level:string = this.symbolTable.insertTable(this.currentLevel, table);
        this.currentLevel = level;
    };


    /**
     * 退出delete处理语句的作用域
     * @param ctx
     */
    exitDeleteStatement: (ctx: DeleteStatementContext)=> void = (ctx: DeleteStatementContext):void => {
        this.currentLevel = this.symbolTable.exitTable(this.currentLevel);
    };


    /**
     * 进入到表名处理
     */
    enterTableName: (ctx: TableNameContext)=> void = (ctx: TableNameContext):void => {
        let fullIdContext = <FullIdContext>ctx.getChild(0);
        let element = new SymbolElement();
        element.type = ElementType.TABLE;
        element.level = this.currentLevel;
        
        if (fullIdContext.childCount == 1){
            element.startIndex = fullIdContext.start.startIndex;
            element.stopIndex = fullIdContext.start.stopIndex;
            element.text = fullIdContext.text;
            
            if (!this.symbolTable.getTable(this.currentLevel).has(element.startIndex)){
                this.symbolTable.getTable(this.currentLevel).set(element.startIndex, element);
            }
            
        } else if (fullIdContext.childCount == 2){
            let terminalNode = <TerminalNode>fullIdContext.getChild(1);
            element.startIndex = fullIdContext.stop.startIndex;
            element.stopIndex = fullIdContext.stop.stopIndex;
            element.text = terminalNode.text;
            if (!this.symbolTable.getTable(this.currentLevel).has(fullIdContext.start.startIndex)){
                this.symbolTable.getTable(this.currentLevel).set(element.startIndex, element);
            }
            
        } 
        

    };



    /***************************************结束select语句***************************************************/

    /***************************************开始update语句***************************************************/

    /**
     * 进入update的域
     * @param ctx
     */
    enterUpdateStatement: (ctx: UpdateStatementContext)=> void = (ctx: UpdateStatementContext):void => {
        let table:HashMap<any, SymbolElement> = new HashMap<any, SymbolElement>();
        let level:string = this.symbolTable.insertTable(this.currentLevel, table);
        this.currentLevel = level;
    };

    /**
     * 退出update的域
     * @param ctx
     */
    exitUpdateStatement: (ctx: UpdateStatementContext)=> void = (ctx: UpdateStatementContext):void => {
        this.currentLevel = this.symbolTable.exitTable(this.currentLevel);
    };



    /***************************************结束update语句***************************************************/

    /***************************************开始insert语句***************************************************/


    /**
     * 进入到insert域
     * @param ctx
     */
    enterInsertStatement: (ctx: InsertStatementContext)=> void = ():void => {
        let table:HashMap<any, SymbolElement> = new HashMap<any, SymbolElement>();
        let level:string = this.symbolTable.insertTable(this.currentLevel, table);
        this.currentLevel = level;

    };

    /**
     * 退出insert域
     * @param ctx
     */
    exitInsertStatement: (ctx: InsertStatementContext)=> void = ():void => {
        this.currentLevel = this.symbolTable.exitTable(this.currentLevel);
    };



    enterUidList: (ctx: UidListContext)=> void = (ctx: UidListContext):void => {

        for (let parseTree of ctx.children){
            if (parseTree instanceof UidContext){
                let uidContext = <UidContext>parseTree;
                let element = new SymbolElement();
                element.text = uidContext.text;
                element.startIndex = uidContext.stop.startIndex;
                element.stopIndex = uidContext.stop.stopIndex;
                element.level = this.currentLevel;
                element.type = ElementType.COLUMN;

                this.symbolTable.getTable(this.currentLevel).set(element.startIndex, element);

            }

        }

    };



    /***************************************结束insert语句***************************************************/


}












