/**
 * ParseService的抽象类,通用的方法在此处做了实现,各数据源在此抽象类上面做实现。、
 */
import {ParseService} from "../../parseService";
import {SymbolTable} from "../../../symbols/symbolTable";
import {CodeCompletionRequest, EditorContext} from "../../..";
import {CommonTokenStream, Token} from "antlr4ts";
import {ElementType, SymbolElement} from "../../../symbols/symbolElement";
import {HashMap} from "@nebtex/hashmaps";

export abstract class AbstractParseService implements ParseService{
    editorContext: EditorContext;
    symbolTable: SymbolTable;

    findSymbolElementByTokenInSymbolTable(tokenInAST: Token): SymbolElement | undefined {
        let tableAllLevel:IterableIterator<string> = this.symbolTable.getAllLevel();
        for (let level of  tableAllLevel){
            let table:HashMap<any,SymbolElement> = this.symbolTable.getTable(level);

            let keys:IterableIterator<number> = table.keys();

            for (let startIndex of  keys){
                let element:SymbolElement = table.get(startIndex);
                if (element.startIndex == tokenInAST.startIndex &&
                    element.stopIndex == tokenInAST.stopIndex){
                    return element;
                }


            }
        }
    }

    findTokenByRangeOffSetInAST(tokenStream:CommonTokenStream): Token | undefined {
        let tokens:Token[] = tokenStream.getTokens();
        for (let token of tokens){
            if (this.editorContext.rangeOffset >= token.startIndex &&
                this.editorContext.rangeOffset <= token.stopIndex){
                return token;
            }
        }
    }

    generateCodeCompletionRequest(element: SymbolElement): CodeCompletionRequest {
        let completionRequest:CodeCompletionRequest = new CodeCompletionRequest();
        if (element.type == ElementType.TABLE){
            completionRequest.cpId = this.editorContext.cpId;
            completionRequest.currentCommandType = this.editorContext.editorType;
            completionRequest.expectType = ElementType.TABLE;
            completionRequest.dataBaseName = this.editorContext.dataBaseName;

        } else if (element.type == ElementType.COLUMN){
            completionRequest.cpId = this.editorContext.cpId;
            completionRequest.currentCommandType = this.editorContext.editorType;
            completionRequest.expectType = ElementType.COLUMN;
            completionRequest.dataBaseName = this.editorContext.dataBaseName;
            let tableName = this.findTableName(element);
            if (tableName == undefined){
                tableName = "ALL_TABLE";
            }
            completionRequest.tableName = tableName;
        }

        return completionRequest;
    }


    abstract getKeyWorld(): string[];


    abstract parserSQL(): CommonTokenStream;


    private findTableName(currentElement:SymbolElement):string|undefined{
        let table:HashMap<any,SymbolElement> = this.symbolTable.getTable(currentElement.level);

        for (let element of table.values()){
            if (element.type == ElementType.TABLE){
                if (currentElement.alias == undefined){
                    return this.findText(element);
                }else {
                    if (currentElement.alias == element.alias ||
                        currentElement.alias == element.text){
                        return this.findText(element);
                    }
                }
            }
        }
    }

    private findText(element:SymbolElement):string|undefined{
        //如果只有别名没有表名，则需要去下一级最后一个作用域中查找
        if (element.text == undefined && element.alias != undefined){
            let currentLevel = element.level;
            let childLevel:string = currentLevel + "::" + 1;
            let childTable:HashMap<any, SymbolElement> = this.symbolTable.getTable(childLevel);
            if (childTable == undefined){
                return;
            }

            for (let childElement of childTable.values()){
                if (childElement.type == ElementType.TABLE){
                    return this.findText(childElement);
                }
            }

        }

        return element.text;

    }

}